Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >vue3 源码解析

vue3 源码解析

作者头像
EchoROne
发布于 2022-08-15 00:35:26
发布于 2022-08-15 00:35:26
79000
代码可运行
举报
文章被收录于专栏:玩转大前端玩转大前端
运行总次数:0
代码可运行

一、核心变化

首先,Vue 3 全面拥抱了 TypeScript。一方面源码全部使用 TpeScript 进行了重写,另一方面,开发者使用 TypeScript 进行开发时也能进行更好地类型推导,获得更好的开发体验。关于这方面,我们不做过多的解析。

第二个重要的改变是,Vue 3 在基本兼容 Vue 2 的使用方式之外,还提供了一套名为 Compostion API 的新接口

1、Composition API

Composition API 是指可以在组件初始化的时候使用一个setup()方法,数据、方法等都可以由setup()方法统一初始化后返回

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
  <button @click="increment">{{count}}</button>
</template>

<script>
export default {
  setup(){
    const count = ref(0);

    function increment(){
      count.value++;
    }

    return {
      count,
      increment
    }
  }
}
</script>

上面的例子只演示了 datamethod 在 Composition API 中是如何等价表达,其它的如 computedwatch 等也有相应的表达。这套 API 带来的好处比较多:

  1. 对 TypeScript 类型推导友好,编程体验较好
  2. 提供了类似 React Hooks 的复用机制,从而可以在不引入新的组件的情况下,将组件内部的代码再次合理进行抽取,即在组件内部又提供了一种组织代码的方法

官方例子:

使用 Vue 2 的源代码位于 https://github.com/vuejs/vue-cli/blob/a09407dd5b9f18ace7501ddb603b95e31d6d93c0/packages/@vue/cli-ui/src/components/folder/FolderExplorer.vue

基于 Vue 3 Composition API 重构的代码位于 https://gist.github.com/yyx990803/8854f8f6a97631576c14b63c8acd8f2e

2、基于 Proxy 的 reactivity

Vue 2 中响应式数据是通过 ES5 的 getter/setter 来实现的,在前面的章节中我们做了比较详尽的介绍。这套响应式数据机制受限于语言机制,有一些不能完全覆盖到数据变更的地方,例如当动态向对象上添加 key,或者通过下标的方式修改数组,就不能触发数据变更的检测机制。

Vue 3 中响应式数据使用了一个不同的机制 ——Proxy,这个机制我们在前文介绍响应式的实现机制时有专门介绍过,在这里可以再复习一下。

Proxy 可以为任何一个对象设置一个代理对象,并在代理对象中设置一些自定义的逻辑。Vue 3 使用这个机制来监测数据变更:首先针对需要监听的数据对象设置一个代理对象,在代理对象中拦截对数据赋值的操作,从而获知数据变化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const dataObj = {};

const dataProxy = new Proxy(dataObj, {
    set(target, key, value){
        // 进行对应界面的更新
        Reflact.set(target, key, value);
    }
});

值得注意的是,因为只有在 ES6 的环境中才可以使用 Proxy,且这个特性无法通过编译到 ES5 的方式进行降级,所以目前兼容性相对来说比较差,这也是 Vue 3 不支持 IE 浏览器的主要原因

3、tree-shaking

Vue 2 的体积其实已经不算大了,但是使用 Vue 3 时可以让打包体积更小。这一方面利益于 Composition API 的设计,另一方面也是因为随着 ESM 模块化规范(和 TS 模块化规范)的成熟,tree-shaking 被使用得越来越广泛了。

tree-shaking 是指针对代码中模块的依赖关系进行静态分析,将完全没有使用到的模块在打包时直接移除掉,只打包使用到的模块,从而减少体积。

得益于 Vue 3 良好的模块划分,开发者在使用 Vue 3 时可以按需选择需要的模块引入,而不用一次性将所有的代码全部引入,这样在打包时 Vue 3 中没有被引用的源码将被移除。

在 Vue 的仓库中专门有一个 @vue/size-check 的包,就是用来测试 tree-shaking 的:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { h, createApp } from '@vue/runtime-dom'

// The bare minimum code required for rendering something to the screen
createApp({
  render: () => h('div', 'hello world!')
}).mount('#app')

这个例子中只使用了 h 和 createApp,其它的诸如 watch、computed、reactive 等模块都没有被引用,因此打包时也将只包含使用到的源码,从而使整个应用的打包体积变得更小

4、渲染性能优化

Vue 2 引入了虚拟 DOM 来进行对比计算,从而确定 DOM 中需要更新的节点。虽然虚拟 DOM 是在 JavaScript 中进行运算,速度足够快了,但是实际上这个对比计算的过程仍然有非常多不必要的步骤。

Vue 3 在这里进行了更为精细的性能优化,主要包括:

以 v-if 和 v-for 作为边界,将模板分成很多 “块”,每个块中的结构是完全固定的,这样块中就不需要进行树的遍历,只需要对比绑定的值即可

将静态节点、子树等渲染代码移到渲染函数之外,这样可以避免每次渲染时重新创建这些不会变化的对象

将元素的更新类型进行细分,例如动态绑定的部分如果只涉及到 class,则在对比时只需要对比 class 即可,不需要对比它的内容

这些优化大大改进了 Vue 3 的渲染性能,从各种公开的数据来看,基本上能有 5 到 10 倍的提升

除了上述几点变化以外,Vue 还有不少的变化,如官方支持了Teleport组件、模板中支持了多根节点的组件等等。但相比上述变化,这些都显得不是那么重要,因此不花更多篇幅进行解读

二、Vue3整体结构和源码目录

1、单体仓库模式

我们非常熟悉的模式是一个 npm 包对应一个 git 代码仓库,这样做的好处是代码仓库中的代码边界清晰、职责单一,在开发维护的时候也一定程度上会强迫开发人员认真思考每个包的功能和 API。但是如果代码间拆分的包过多,或者包之间耦合较多,这种模式在日常开发和维护时就显得不太方便了,因为很有可能一个小小的改动就需要动到好几个 npm 包的代码。

单体仓库就是为解决这个问题而出现的一种解决方案。具体而言,就是各个包从逻辑上互相独立,通过 npm 包的形式来互相依赖,但是各个包的代码都放在同一个 git 仓库中。

Vue 3 从代码结构上进行了梳理,更加模块化,而且将很多之前属于内部实现的部分也抽象成了模块,作为单独的 npm 包发布。这样开发者还可以不引用整个框架,而只使用框架中用到的各个独立的模块。这样一来,Vue 3 最终要发布的包数量就多了很多,因此也选择了使用单体仓库模式

2、整体结构

Vue 3 的代码仓库根目录基本上就只有 packagesscripts 两个目录,后者主要用于一些工程操作(检查环境、打包等),因此真正的源码都位于 packages 目录中。这个目录下一共有 13 个包:

  • compiler-core 模板解析核心,与具体环境无关,主要生成 AST,并根据 AST 生成 render() 方法
  • compiler-dom 浏览器环境中的模板解析逻辑,如处理 HTML 转义、处理 v-model 等指令
  • compiler-sfc 负责解析 Vue 单文件组件,在前面 vue-loader 的解析中有讲解过
  • compiler-ssr 服务端渲染环境中的模板解析逻辑
  • reactivity 响应式数据相关逻辑
  • runtime-core 与平台无关的运行时核心,包括 render
  • runtime-dom 浏览器环境中的运行时核心
  • runtime-test 用于自动化测试的相关配套
  • server-renderer 用于 SSR 服务端渲染的逻辑
  • shared 一些各个包之间共享的公共工具
  • size-check 一个用于测试 tree shaking 后代码大小的示例库
  • template-explorer 用于检查模板编译后的输出,主要用于开发调试
  • vue Vue 3 的主要入口,包括运行时和编译器,包括几个不同的入口(开发版本、runtime 版本、full 版本)

上述包中,剔除服务端渲染相关代码、开发调试、测试相关代码,并将它们的依赖关系体现出来,则大致是这样的(来自 Vue 3 仓库):

可见 Vue 3 的模块关系拆分得非常清楚:

  • compiler 模块负责 Vue 模板的解析、生成 render 方法
  • runtime 模块负责调用 render 方法生成虚拟 DOM 并渲染,同时处理用户交互、更新应用状态等等
  • vue 作为入口,分别依赖 compiler 和 runtime 2 个大模块(如果是 runtime only 的版本,则只依赖 runtime)。这两个大模块内部又将核心剥离出去,然后将浏览器环境相关的部分再放到一个单独的包中

我们很关注的响应式数据的部分 reactivity 也是作为 runtime 的一部分,用于处理运行时数据发生变化的情况

三、Reactivity的原理和实现方式

Vue 3 将响应式数据的逻辑单独提出来作为一个独立的模块了,即源码中的reactivity包,在 npm 上是@vue/reactivity

  1. 当一个数据被包装成响应式数据时,它的读取和修改方法会被 Proxy 拦截
  2. 数据被读取时,Proxy 记录 effect 与当前数据的依赖关系
  3. 数据被修改时,触发 effect 运行
  4. 如果调用一下effect(fn),则之后任何fn()中依赖的数据发生变化,fn()都会被再次调用。这便是 Vue 3 的响应式数据的原理

Vue 作者还写了一个小项目vue-lithttps://github.com/yyx990803/vue-lit),这个项目使用了reactivity,仅仅用很少量的代码就搭出了一个非常轻量的组件化框架。这个项目也可以帮忙我们更好地理解独立出来的reactivity模块的用法。

四、compiler源码解析

和 Vue 2 一样,compiler 的主要作用是解析 Vue 中的模板部分,最终将模板转换成render()方法

整个编译过程分为 3 步:

  1. 调用 baseParse() 方法,解析传入的模板,生成 AST(即 ast 变量)
  2. 调用 transform() 方法对 AST 进行转换
  3. 调用 generate() 方法,根据转换后的 AST 生成 render() 方法并返回

第 2 点需要特别注意,在 Vue 2 中,生成 AST 后会有一个 optimize 的过程,而在 Vue 3 中,optimize “升级” 了一下,变成了 transform 的过程。transform 会做一些额外的逻辑处理,也会为了后续渲染性能的优化预先做一些处理并反映到 AST 中。

1、parse

首先看模板解析并生成 AST 的过程,Vue 3 的模板解析是非常典型的代码解析方法,即从源码的第 1 个字符开始逐个字符进行扫描,每当找到可以识别的对象则将该对象放入 AST,并接着之前的源码位置往下继续解析

2、transform

transform 的作用是针对上一步生成的 AST 进行一些变换,例如针对 v-ifv-for 增加一些特殊的节点,以及为了后续渲染性能优化增加一些属性等等

  1. 首先使用 tranverseNode() 对 AST 进行遍历,并在每次遍历退出前调用 hook,以便相关逻辑对相应的节点进行修改
  2. 如果需要进行静态节点提升,则调用 hoistStatic() 方法提升静态节点
  3. 调用 createRootCodegen() 方法生成 codegenNode,以便在后续 generate 阶段使用

3、generate

和前面的 parse、transform 一样,generate 的过程也是先生成一个 context,然后再处理。

首先它会做一堆前置处理:

  • 生成模块化相关包装代码
  • 根据是否是服务端渲染,应用不同的处理参数生成函数签名
  • 处理 scopeIdisSetupInlined 相关逻辑
  • 处理组件、指令等

接下来调用 genNode(),很机械地生成代码,不做详细解析,值得注意的是传入的参数正是 ast.codegenNode,而不是 ast 本身

五、runtime 源码解析

在 Vue 3 中,不再使用new App()的方式来新建 Vue APP,而是使用createApp()方法来创建。这个入口就属于runtime

1、block

上一节我们提到了一个概念叫 block,在上面的代码中也再次出现了 block 的概论。这个概念是 Vue 3 为了提升渲染性能而提出来的。

在 Vue 2 中,虚拟 DOM 的更新需要对新、旧两棵 VNode 树进行全量遍历和对比,然后才能确定发生变动的地方。但是,Vue 的虚拟 DOM 多数情况下是由模板编译而来,而模板的结构大部分地方是确定的,也就是树的结构大部分地方不会发生变化,变化的可能只是属性、文字等,真正会使结构发生变化的基本上只有 v-ifv-for 等少数几个指令。

因此 Vue 3 增加了一个 block 的概念,一个 block 中就是一个确定的树状结构。我们以 v-if 为例,尽管它会导致组件的整体结构发生变更,但它自己的子节点却是稳定的,因此它可以当作是一个完整的 block。当其它的 block 中碰到 v-if 的时候,只需要知道 “这里有一个 block” 就好了。通过这样的方式,虚拟 DOM 的对比就会非常高效,因为 block 的结构是固定的,block 内部就没有必要去对比是否结构发生了变化。

具体到实现中,如果 VNode 节点上存在 dynamicChildren,则说明它是一个 block。再回到前面生成的 render() 函数

Vue 3 runtime 模块的主要功能:

  1. 提供 createApp()API,完成初始化的工作
  2. 调用 render() 方法完成渲染
  3. 如何对虚拟节点进行对比和更新

在上面我们也看到了 Vue 3 是如何通过 ShapeFlagPatchFlag、block 等机制实现高性能渲染的

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Vue 3 源码导读
https://juejin.im/post/5d977f47e51d4578453274b3 来源:掘金
前端达人
2019/10/10
1.5K0
Vue 3 源码导读
前端常见vue面试题合集
通过webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
bb_xiaxia1998
2022/11/09
7540
Vue3 深度解析
距离尤雨溪首次公开 Vue3 (vue-next)源码有一个多月了。青笔观察到,刚发布国庆期间,出现不少解读 Vue3 源码的文章。当然不少有追风蹭热之嫌,文章草草讲讲响应式原理,或者只是做了一些上层的导读,告诉读者应该先看哪再看哪。不能说这些文章就没有一点价值,它确实能够让你在短时间内,不用过多思考就能了解到一些 Vue3 重中之重的“干货”。但是过于干货的未必就是好的。因为干货通常是经过作者咀嚼过后的产物,大部分营养其实只被作者消化了。留给读者的只是一些看似很有料,实则没有营养的残渣。就像一块啃到只剩骨头的排骨。这样的文章通常适合于媒体传播,仅用于快速捕获眼球。但是对于想更细致了解 Vue3 的专业前端开发,这显然远远不够。
我是一条小青蛇
2019/11/20
5.2K0
「中文翻译」Vue3 的诞生之路
因时间有限,文中翻译不对的地方还请指出,望海涵。想感受原汁原味还请移步上方链接。致敬尤大!
童欧巴
2020/06/04
7120
Vue 3.0 源码分析-数据侦测
2020年前端大事件之一,Vue 3.0终于正式发布了。作为一个大的版本更新,Vue 3 与 Vue 2相比,实现原理,使用方式等均有着不小的改动。本文主要会介绍讲述二块内容,分别是Vue 3.0 的简要介绍,Vue 3.0 数据侦测源码分析。小伙伴们可以根据自己的需求,查看对应的内容,也欢迎各位一起探讨,一起学习。 Vue 3.0 简要介绍 Vue 3 的 “前世今生” 2016年,Vue 2.0 正式发布,时至今日,已经过去了四年的时光。诚然,在这四年中,Vue 2的社区建设一直呈现出一副蓬勃向上的态
QQ音乐前端团队
2021/01/11
7560
面试官:Vue3有了解过吗?能说说跟Vue2的区别吗?
「Vue 新版本的理念成型于 2018 年末,当时 Vue 2 的代码库已经有两岁半了。比起通用软件的生命周期来这好像也没那么久,但在这段时期,前端世界已经今昔非比了
@超人
2021/02/26
10.5K0
面试官:Vue3有了解过吗?能说说跟Vue2的区别吗?
《Vue3.0抢先学》系列之:网友们都惊呆了!
今天开始,我想给大家讲点新东西。大家不用大喊学不动,请放松心情随意观看,我也讲不出什么很深奥难学的东西,本系列文章都会是些比较浅显易懂的家常内容。
一斤代码
2019/10/29
8500
《Vue3.0抢先学》系列之:网友们都惊呆了!
写给小白(自己)的vue3源码导读!
目前社区有很多 Vue3 的源码解析文章,但是质量层次不齐,不够系统和全面,总是一个知识点一个知识点的解读,这样我在拜读中,会出现断层,无法将整个vue3的知识体系融合,于是只能自己操刀来了
用户7413032
2022/03/24
1.4K0
写给小白(自己)的vue3源码导读!
Vue2 源码解析
Vue.js 是一个渐进式 MVVM 框架,目前被广泛使用,也成为目前前端技术中颇具代表性的一个框架。
EchoROne
2022/08/15
1.2K0
Vue2 源码解析
第一篇:一文看懂 Vue.js 3.0 的优化
我们的课程是要解读 Vue.js 框架的源码,所以在进入课程之前我们先来了解一下 Vue.js 框架演进的过程,也就是 Vue.js 3.0 主要做了哪些优化。
越陌度阡
2023/08/09
4070
第一篇:一文看懂 Vue.js 3.0 的优化
Vue3 对比Vue2,你找到哪些变化?
希望本篇文章能帮你加深对 Vue 的理解,能信誓旦旦地说自己熟练Vue2/3。除此之外,也希望路过的朋友可以帮助我查漏补缺🤞。 内容混杂用法 + 原理 + 使用小心得,建议收藏,慢慢看。 区别 生命周期的变化 整体来看,变化不大,只是名字大部分需要 + on,功能上类似。使用上 Vue3 组合式 API 需要先引入;Vue2 选项 API 则可直接调用,如下所示。 // vue3 <script setup> import { onMounted } from 'vue' onMounted(
@超人
2022/04/14
1.2K0
快速了解Vue3的新特性
10月5日尤大大公布了 vue 3.0 的源码,目前还是 pre-alpha 状态,预计年后会发布正式版本,这个无疑是国庆期间前端圈最大的新闻了。虽然此前关于 vue 3.0众说纷纭,但是既然已经官宣了,而且明年有可能迁移至新的开发框架,是时候撸起袖子学习一波了。
用户9914333
2022/07/22
4790
快速了解Vue3的新特性
Vue2和Vue3的底层原理详解
Vue.js是一个流行的JavaScript框架,用于构建用户界面。Vue.js通过MVVM架构模式和响应式数据绑定来实现数据和UI的分离。Vue.js的底层原理在Vue2和Vue3中略有不同。
世间万物皆对象
2024/03/20
8440
前端工程师的vue面试题笔记
指令本质上是装饰器,是 vue 对 HTML 元素的扩展,给 HTML 元素增加自定义功能。vue 编译 DOM 时,会找到指令对象,执行指令的相关方法。
bb_xiaxia1998
2022/11/10
7250
前端高频vue面试题总结3
通过webpack的tree-shaking功能,可以将无用模块“剪辑”,仅打包需要的
bb_xiaxia1998
2023/01/05
1.2K0
Vue3源码解析,打造自己的Vue3框架
分析Vue3源码并尝试打造自己的Vue3框架是一个复杂但极具教育意义的项目。这不仅能帮助你深入理解Vue3的内部工作机制,还能提升你的JavaScript、TypeScript以及前端框架开发能力。以下是一个大致的步骤指南,帮助你开始这一挑战:
瘦瘦itazs和fun
2025/01/02
870
Vue篇(011)-vue3带来的新特性/亮点
2. Tree shaking support: 按需编译,体积比vue2.x更小;
齐丶先丶森
2022/05/12
1.2K0
Vue篇(011)-vue3带来的新特性/亮点
Vue.js 3.x 优化概览
Vue.js 从 1.x 到 2.0 版本,最大的升级就是引入了虚拟 DOM 的概念。
CherishTheYouth
2022/05/10
3.5K0
Vue.js  3.x 优化概览
Vue常识面试题
Web是World Wide Web的简称,中文译为万维网我们可以将它规划成如下的几个时代来进行理解
隔壁老陈
2023/03/09
2.2K0
Vue常识面试题
Vue3源码解析,打造自己的Vue3框架无密分享
随着前端技术的飞速发展,Vue.js 作为一款轻量级且功能强大的前端框架,受到了广大开发者的青睐。Vue 3 作为 Vue.js 的最新版本,带来了许多令人振奋的改进和优化。本文将深入探讨 Vue 3 的源码,并基于这些理解,指导读者如何打造自己的 Vue 3 框架。
爱学IT-学无止境
2024/06/25
3020
相关推荐
Vue 3 源码导读
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验