组件分割
假如他们用「VUE的模板句法」表述:
hello
最后VUE会帮他们在应用程序中图形相关联的DOM结点。
这间对那段结点的叙述会历经4次变动,横贯「校对时」与「运转时」:
「模板句法」在校对时能被「校对器」转化成为「render表达式」,类似于:
render(h) { return h(div, hello);}
在运转时,「render表达式」继续执行后回到的「h表达式的继续执行结论」就是VNode(也是交互式DOM),类似于:
{ tag: “div”, children: [ { text: “Hello” } ]}
最后,VUE依照VNode的重要信息,在应用程序图形相关联DOM。
所以,是谁在驱动力而此业务流程?
mount和patch
组件有三种相同的图形方法论:「首度图形」和「预览」。
「首度图形」意味着从无到有,比如上文的VNode:
{ tag: “div”, children: [ { text: “Hello” } ]}
可能相关联如下DOM操作:
const node = document.createElement(VNode.tag);node.textConent = Hello;contanerDOM.appendChild(node);
「预览」则需要对比预览前后VNode,对变动部分继续执行DOM操作。
比如,以上VNode假如变为:
{ tag: “div”, children: [ { // text改变 text: “world” } ]}
则最后继续执行:
node.textContent = world;
VUE的「首度图形」相关联mount组件,「预览」相关联patch组件。
所以,render表达式继续执行后回到VNode,依照情况相同,会走mount或patch的图形方法论:
所以是谁在什么时机调用了render表达式呢?
响应式预览
在VUE中,状态变动会实时反映到视图上,比如:
{{count}}
点击div后:
触发点击事件,count变动
count变动触发回调,回调中预览视图
当前他们已经知道第二步是由于触发了如下业务流程:
所以只需要建立count变动到继续执行render表达式的联系即可。
具体来说,他们希望实现reactive及watchEffect:
// 表述状态const state = reactive({count: 0});// 监听状态变动watchEffect(() => { console.log(state.count);})// 改变状态state.count++;
reactive表述状态。
watchEffect依照回调继续执行的情况决定监听哪些状态。
比如watchEffect回调继续执行了console.log(state.count);,他就会监听state的变动。
当继续执行state.count++;,由于watchEffect监听了state的变动,则其回调会触发,打印state.count。
这是Reactivity组件。
当实现了Reactivity组件,他们就能将「组件状态」与后续业务流程串联起来。
刚才讲过,render表达式是校对器依照「模板句法」生成的。在面对带状态的模板句法时,比如上文的count:
{{count}}
render表达式内的count是响应式的
(即:count实际是reactive({count: 0}))。
所以就能用watchEffect监听count的变动。
所以,在应用初始化时,会有类似于方法论:
let isMounted = false;let oldVNode;watchEffect(() => { if (!isMounted) { // mount方法论 // 调用render表达式 oldVNode = component.render(); // mount mount(oldVNode); } else { // patch方法论 // 调用render表达式 newVNode = component.render(); patch(oldVNode, newVNode); oldVNode = newVNode; }})
其中component.render()(render表达式的继续执行)达到上文「监听状态变动」的效果:
// 监听状态变动watchEffect(() => { console.log(state.count);})
所以,该组件内任何状态变动都会触发watchEffect的继续执行,watchEffect回调内会触发后续业务流程。
总结
VUE3按基本原理大体可以分割为:
mount
patch
校对器
Reactivity