首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >vue3 源码解析

vue3 源码解析

作者头像
EchoROne
发布于 2022-08-15 00:35:26
发布于 2022-08-15 00:35:26
82600
代码可运行
举报
文章被收录于专栏:玩转大前端玩转大前端
运行总次数: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 删除。

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
真相只有一个 !God.Game 代币被盗事件原理分析
8月22日中午,区块链游戏God.Game宣布游戏内所有代币被攻击者卷走,项目方筹备两个月,游戏却在运营不久后迅速夭折。
区块链大本营
2018/09/21
5130
真相只有一个 !God.Game 代币被盗事件原理分析
智能合约安全审计技术概览
智能合约是区块链技术的重要组成部分,它能够自动执行代码并将结果写入区块链以实现各种业务场景,然而由于智能合约本质上是代码,因此也存在着相应的安全风险,如果智能合约存在漏洞,黑客就有可能利用这些漏洞进行攻击,导致资产损失甚至系统崩溃,因此对智能合约进行安全审计是至关重要的,本文将概述智能合约安全审计技术的相关知识为读者带来更深入的了解
Al1ex
2023/08/10
1K0
智能合约安全审计技术概览
“以太坊智能合约设计缺陷问题”影响分析报告
在审计各种智能合约之后,我发现了一类很有趣的问题,这类问题出现的原因不只是由于开发者的疏忽,也同样是因为智能合约本身的一些设计缺陷,在开发者不了解这些问题的基础上,就容易出现问题。
LoRexxar
2023/02/21
3950
“以太坊智能合约设计缺陷问题”影响分析报告
【区块链安全】技术小白如何做到让一行代码值64亿元?
2018年4月24日,又一件突发性事件引爆了币圈!刚刚发行了才两个月的“美链 Beauty Chain” (简称BEC)在受到黑客的攻击的影响下直接归零了!黑客使用的是以太坊ERC-20智能合约BatchOverFlow数据溢出的漏洞,向两个地址转出了数量巨大的BEC代币!黑客先是试探性地往Okex中转100万的BEC,发现成功转入卖出后,又分两次转入了一千万的BEC。发现两次都成功,黑客变得更加大胆,便转入了一亿枚BEC。但这1亿枚 BEC转入后,OKEx已经发现问题并停止了BEC的交易。按照转入记录,预计黑客已经卖出了最少 1100万枚BEC,折合昨日售价约一千八百多万人民币。
辉哥
2018/08/10
9500
【区块链安全】技术小白如何做到让一行代码值64亿元?
以太坊 Solidity 合约 call 函数簇滥用导致的安全风险
Solidity(http://solidity.readthedocs.io/en/v0.4.24/) 是一种用与编写以太坊智能合约的高级语言,语法类似于 JavaScript。
Seebug漏洞平台
2018/07/26
8760
以太坊 Solidity 合约 call 函数簇滥用导致的安全风险
以太坊合约审计 CheckList 之“以太坊智能合约设计缺陷问题”影响分析报告
作者:LoRexxar'@知道创宇404区块链安全研究团队 发布时间:2018/08/22
Seebug漏洞平台
2018/09/30
5800
以太坊合约审计 CheckList 之“以太坊智能合约设计缺陷问题”影响分析报告
Python在区块链开发与智能合约编写中的实战应用
区块链技术正逐渐成为各行各业的焦点,而Python作为一种灵活且强大的编程语言,被广泛应用于区块链开发和智能合约编写。本文将介绍如何利用Python进行区块链开发以及智能合约的编写,并提供代码实例来帮助读者更好地理解这些概念。
一键难忘
2024/08/14
4580
浅谈以太坊智能合约的安全漏洞
智能合约的安全是区块链安全中的热议话题,但其实 89% 的智能合约都存在漏洞,本文将浅谈以太坊智能合约出现过的一些安全漏洞。
信安之路
2018/08/08
1.2K0
浅谈以太坊智能合约的安全漏洞
智能合约中常见的漏洞总结复现#技术创作101训练营#
一个小朋友,他可以数着手指运算十以内的运算,比如 1+1=2,他可以用两个手指算出来,但是如果你问他 5+6 等于多少,他数完十个手指之后发现手指不够用了,就会把手指扳回来,说:结果为 1,对于小朋友来说,这个问题就超纲“溢出”了
yichen
2020/09/23
3K0
blockwell.ai 虚假转账 事件分析
2018年9月7日早上1点左右,许多以太坊账户都收到了一种名为blockwell.ai KYC Casper Token转账消息,其中有的是收到了这种代币,而有的用户是支出了这种代币。
LoRexxar
2023/02/21
2600
blockwell.ai 虚假转账 事件分析
nest2.0智能合约架构解析四
继前文[1],我们在这里对 nest2.0 剩下的三个文件(NESTNODE,NEST_3_OrePoolLogic,NEST_3_OfferFactory)做简单的解析。NESTNODE.sol主要是守护者节点的内容,制作 1500 个 token,并提供高级权限,比如分红。里面生成了 4 个合约。其中最复杂也需要最后运行的 NEST_NodeAssignment。NEST_3_OrePoolLogic这个合约主要是设定各种报价出块的参数。这个合约不涉及执行任何 NEST_3_OfferFactory,仅仅验证运行者是不是 NEST_3_OfferFactory.NEST_3_OfferFactory主要讲的是报价工厂的成立,与各种价格的成交。orePoolLogic 提供各种报价功能。
Tiny熊
2021/02/25
5300
“以太坊智能合约编码安全问题”影响分析报告
这篇扫描报告其实算是一片比较特殊的文章,因为这是checklist唯一被我直接标记为安全问题的一类,其中很多问题虽然特征明显,但修复逻辑却千变万化,所以统计数据一直波动很大,犹豫了很久才发出来,图片的很多涉及到的合约并不一定真的存在问题,但仍然值得注意。
LoRexxar
2023/02/21
4240
“以太坊智能合约编码安全问题”影响分析报告
智能合约的重入攻击
智能合约的重入攻击是一种常见的安全漏洞,特别是在基于以太坊的区块链上,它利用了智能合约设计或实现中的缺陷。重入攻击的核心在于攻击者能够在一个交易的中间阶段,即智能合约尚未完成其预期的内部状态更新时,递归地调用合约的同一或另一个函数。
终有链响
2024/07/29
1840
以太坊蜜罐智能合约分析
在学习区块链相关知识的过程中,拜读过一篇很好的文章《The phenomenon of smart contract honeypots》,作者详细分析了他遇到的三种蜜罐智能合约,并将相关智能合约整理收集到Github项目smart-contract-honeypots。
Seebug漏洞平台
2018/07/12
1.2K0
以太坊智能合约 Owner 相关 CVE 漏洞分析
最近学习了下以太坊的智能合约,而且也看到挺多厂家pr智能合约相关的漏洞,其中《ERC20智能合约整数溢出系列漏洞披露》文章中披露了6个CVE编号的漏洞,而这些漏洞都属于整型溢出漏洞范畴,其中5个漏洞均需要合约Owner才能触发利用。本文正是针对这些漏洞从合约代码及触发逻辑上做了详细分析,并提出了一些关于owner相关漏洞的思考。
Seebug漏洞平台
2018/07/12
8250
如何编写 NFT 智能合约
在之前的教程中,我们向你展示了如何使用我们的生成艺术库[4]来创建一个头像集合[5],生成符合要求的 NFT 元数据,并将元数据 JSON 和媒体文件上传至 IPFS[6]。
Tiny熊
2022/11/07
1.3K0
如何编写 NFT 智能合约
以太坊智能合约审计 CheckList
在以太坊合约审计checkList中,我将以太坊合约审计中遇到的问题分为5大种,包括编码规范问题、设计缺陷问题、编码安全问题、编码设计问题、编码问题隐患。其中涵盖了超过29种会出现以太坊智能合约审计过程中遇到的问题。帮助智能合约的开发者和安全工作者快速入门智能合约安全。
Seebug漏洞平台
2018/12/13
1.1K0
如何做智能合约审计?
你可以自己学习,或者你可以使用这份便利的一步步的指南来准确地知道在什么时候该做什么,并对合约进行审计。
辉哥
2018/08/10
1.4K0
重入漏洞分析-基于hardhat、solidity0.8环境
重入,顾名思义是指重复进入,也就是“递归”的含义,本质是循环调用缺陷。重入漏洞(或者叫做重入攻击),是产生的根源是由于solidity智能合约的特性,这就导致许多不熟悉 solidity 语言的混迹于安全圈多年的安全人员看到“重入漏洞”这 4 个字时也都会一脸蒙圈,重入漏洞本质是一种循环调用,类似于其他语言中的死循环调用代码缺陷。
Tiny熊
2022/11/07
4540
重入漏洞分析-基于hardhat、solidity0.8环境
应对黑客的进攻——浅谈数字货币安全问题
摘要:随着智能合约飞速发展,越来越多的项目基于以太坊发行token,链上资产的类别和规模呈指数级增长,“虚拟世界”中的数字资产也点燃了黑客们的“热情”。以太坊区块链被认为是区块链的2.0时代,各种各样新的数字资产都基于以太坊发行早期代币甚至实现部分功能,虽然国外区块链社区甚至认为以太坊体量变得太大,已经不可轻易战胜,但以太坊也是数字货币历史上产生最多安全问题的币种,从2016年的The DAO事件,到最近的BEC,EDU,SMT的安全漏洞,以太坊的智能合约可以说充满安全漏洞。大多数的代币都在自己主网上线前使用以太坊代币,作为投资者,为了自身资产的安全着想,熟悉智能合约的漏洞概念变得尤为重要。
安恒信息
2018/07/24
6030
应对黑客的进攻——浅谈数字货币安全问题
推荐阅读
相关推荐
真相只有一个 !God.Game 代币被盗事件原理分析
更多 >
交个朋友
加入腾讯云官网粉丝站
蹲全网底价单品 享第一手活动信息
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验