【注意】最后更新于 June 15, 2020,文中内容可能已过时,请谨慎使用。
前言
代码例子
这次用个简单的例子, 这里为了简化就不使用 jsx 转换了,手写 jsx。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| import { h, Component, render } from "zreact";
class App extends Component {
constructor(props, context) {
super(props, context);
this.state = {
num: 0
};
}
click() {
this.setState({ num: this.state.num + 1 });
}
render(props, state) {
return h("div", { onClick: this.click.bind(this) }, state.num);
}
}
render(h(App), document.body);
|
例子代码分解
- App 类没什么好说的。
render(h(App), document.body)
,这里的h(App)
作为参数会先执行得到
1
2
3
4
5
6
| VNode:{
nodeName: App,
children: [],
attributes: undefined,
key: undefined
}
|
- 最后
render
接受到两个参数,一个上面的VNode
实例,一个要挂载的父节点。
render 执行流程
1
2
3
4
5
| const child = {};
function render(vnode, parent, merge, domChild) {
const pchild = domChild || child;
return diff(merge, vnode, {}, false, parent, false, pchild);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| function diff(
dom: Element | undefined,
vnode: VNode | void,
context: IKeyValue,
mountAll: boolean,
parent: any,
componentRoot: boolean,
child: any
) {
// 因为是第一次调用设置isSvgMode,hydrating
const ret = idiff(dom, vnode, context, mountAll, componentRoot, child);
// 生成dom后的操作
return ret;
}
|
idiff
通过分支判断出vnode.vnodeName
是一个类,调用buildComponentFromVNode
diff.ts#L158
1
2
3
4
| if (typeof vnodeName === "function") {
// 是一个组件,创建或复用组件实例,返回dom
return buildComponentFromVNode(dom, vnode, context, mountAll, child);
}
|
1
2
3
4
| // 通过缓存组件的方式创建组件实例
c = createComponent(vnode.nodeName, props, context);
// 设置props,并同步执行render
setComponentProps(c, props, SYNC_RENDER, context, mountALL);
|
1
2
| // 同步执行render
renderComponent(component, SYNC_RENDER, mountAll);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // 当前组件的render函数返回的VNode
const rendered: VNode | void = component.render(props, state, context);
// rendered {
// vnodeName: "div",
// children: [0],
// attributes: {onClick: fun},
// key: undefined
// }
// ...
// 渲染原生组件
base = diff(
// 原dom
cbase,
// VNode
rendered,
context,
// 父级或者该原生组件,原dom不存在说明必须触发生命周期
mountALL || !isUpdate,
// 把组件挂载到缓存dom的父级
initialBase && initialBase.parentNode,
// 以原生组件这里执行说明是自定义组件的第一个原生组件
true,
// dom上下文
component.child
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| if (!dom || !isNamedNode(dom, vnodeName)) {
// 没有原dom或者原dom与vnode里的不同,新建一个
out = createNode(vnodeName, isSvgMode);
// ...
}
// ...
const vchildren = vnode.children;
else if (vchildren && vchildren.length || fc != null) {
if (!child.children) {
child.children = [];
}
// vnode子元素需要渲染或者为空但dom子元素需要清空
diffChildren(
out,
vchildren,
context,
mountAll,
hydrating || props.dangerouslySetInnerHTML != null,
child,
);
}
|
1
2
| // vchild=1
child = idiff(child && child.base, vchild, context, mountAll, false, tchild);
|
1
2
| out = document.createTextNode(data);
return out;
|
1
| dom.appendChild(child);
|
1
2
3
4
| // 设置dom属性
diffAttributes(out, vnode.attributes, props, child);
return out;
|
1
| componentRef.base = base;
|
1
2
3
| // 获取dom
dom = c.base;
return dom;
|
1
2
| parent.appendChild(ret);
return ret;
|
render 流程图
setState 流程
文章作者
上次更新
2020-06-15 18:12:42 +08:00
(9c054d8)
许可协议
CC BY-NC-ND 4.0