4

大概几集下饭剧时间就能懂的VUE3原理

 3 years ago
source link: https://segmentfault.com/a/1190000040611431
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

大家好,我是卡颂。

最近中午没胃口,找来VUE源码相关视频来当下饭剧。几顿饭下去,人胖了,VUE也整明白了。

这篇文章为你带来一份VUE3原理速成指南。

如果我们用VUE的模版语法定义:

<div>hello</div>

最终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,根据情况不同,会走mountpatch的渲染逻辑:

如果想深入虚拟DOM相关知识,推荐阅读snabbdom源码。这是个优秀的虚拟DOM库,VUE2虚拟DOM部分就是fork这个库改造的。

那么是谁在什么时机调用了render函数呢?

响应式更新

VUE中,状态变化会实时反映到视图上,比如:

<div @click="count++">{{count}}</div>

点击div后:

  1. 触发点击事件,count变化
  2. count变化触发回调,回调中更新视图

当前我们已经知道第二步是由于触发了如下流程:

所以只需要建立count变化到执行render函数的联系即可。

具体来说,我们希望实现reactivewatchEffect

// 定义状态
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模块。

VUE官方推出了课程讲解Reactivity的实现,这是B站链接。如果经济允许,请支持正版

当实现了Reactivity模块,我们就能将组件状态与后续流程串联起来。

刚才讲过,render函数是编译器根据模版语法生成的。在面对带状态的模版语法时,比如上文的count

<div @click="count++">{{count}}</div>

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

VUE官方推出了,感兴趣的朋友可以去看看。如果有能力,记得去支持正版哦。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK