前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >​大概几集下饭剧时间就能懂的VUE3原理

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

作者头像
公众号@魔术师卡颂
发布2021-09-18 10:40:01
3020
发布2021-09-18 10:40:01
举报
文章被收录于专栏:魔术师卡颂魔术师卡颂

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

大家好,我是卡颂。

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

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

模块划分

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

代码语言:javascript
复制
<div>hello</div>

最终VUE会帮我们在浏览器中渲染对应的DOM节点

这之间对这段节点的描述会经历4次变化,横跨「编译时」「运行时」

「模版语法」在编译时会被「编译器」转化为「render函数」,类似:

代码语言:javascript
复制
render(h) {
  return h('div', 'hello');
}

在运行时,「render函数」执行后返回的「h函数的执行结果」就是VNode(也就是虚拟DOM),类似:

代码语言:javascript
复制
{
  tag: "div",
  children: [
    {
      text: "Hello"
    }
  ]
}

最终,VUE根据VNode的信息,在浏览器渲染对应DOM

那么,是谁在驱动这一流程?

mount和patch

组件有两种不同的渲染逻辑:「首次渲染」「更新」

「首次渲染」意味着从无到有,比如上文的VNode

代码语言:javascript
复制
{
  tag: "div",
  children: [
    {
      text: "Hello"
    }
  ]
}

可能对应如下DOM操作:

代码语言:javascript
复制
const node = document.createElement(VNode.tag);
node.textConent = 'Hello';
contanerDOM.appendChild(node);

「更新」则需要对比更新前后VNode,对变化部分执行DOM操作。

比如,以上VNode如果变为:

代码语言:javascript
复制
{
  tag: "div",
  children: [
    {
      // text改变
      text: "world"
    }
  ]
}

则最终执行:

代码语言:javascript
复制
node.textContent = 'world';

VUE「首次渲染」对应mount模块,「更新」对应patch模块。

所以,render函数执行后返回VNode,根据情况不同,会走mountpatch的渲染逻辑:

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

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

响应式更新

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

代码语言:javascript
复制
<div @click="count++">{{count}}</div>

点击div后:

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

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

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

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

代码语言:javascript
复制
// 定义状态
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官方推出了VUE3响应式原理[2]课程讲解Reactivity的实现,这是B站链接。如果经济允许,请支持正版[3]

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

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

代码语言:javascript
复制
<div @click="count++">{{count}}</div>

render函数内的count是响应式的(即:count实际是reactive({count: 0}))。

那么就能用watchEffect监听count的变化。

所以,在应用初始化时,会有类似逻辑:

代码语言:javascript
复制
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函数的执行)达到上文「监听状态变化」的效果:

代码语言:javascript
复制
// 监听状态变化
watchEffect(() => {
  console.log(state.count);
})

所以,该组件内任何状态变化都会触发watchEffect的执行,watchEffect回调内会触发后续流程。

总结

VUE3按原理大体可以划分为:

  • mount
  • patch
  • 编译器
  • Reactivity

VUE官方推出了实现简易VUE3教程[4],感兴趣的朋友可以去看看。如果有能力,记得去支持正版[5]哦。

参考资料

[1]

snabbdom: https://github.com/snabbdom/snabbdom

[2]

VUE3响应式原理: https://www.bilibili.com/video/BV1SZ4y1x7a9/?spm_id_from=333.788.b_7265636f5f6c697374.6

[3]

正版: https://www.vuemastery.com/free-weekend/?gclid=Cj0KCQjwpreJBhDvARIsAF1_BU16x7gElbhGqGzZZ1geo5RzOqz_PuaJzBM41jHcAAC6CPwPSPvo8G8aAkdhEALw_wcB

[4]

实现简易VUE3教程: https://www.bilibili.com/video/BV1rC4y187Vw?p=10

[5]

正版: https://www.vuemastery.com/free-weekend/?gclid=Cj0KCQjwpreJBhDvARIsAF1_BU16x7gElbhGqGzZZ1geo5RzOqz_PuaJzBM41jHcAAC6CPwPSPvo8G8aAkdhEALw_wcB

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 魔术师卡颂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 模块划分
  • mount和patch
  • 响应式更新
  • 总结
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档