看懂Vue3.0的优化,讲解Vue2与Vue3的区别

Vue3.0通过采用monorepo管理源码,使用TypeScript提升可维护性,拆分模块以增强代码清晰度。性能上,利用tree-shaking减少包体积,Proxy替换Object.defineProperty优化数据劫持,编译阶段的Blocktree优化提升DOM更新性能。此外,CompositionAPI改进了逻辑组织和复用,降低命名冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vue3.0的优化

1、源码优化

1.1 更好的代码管理方式monorepo

  • 首先是源码优化,也就是对于Vue.js框架本身开发的优化,它的目的是让代码更易于开发和维护

源码的优化主要体现在使用monorepoTypeScript管理和开发源码,这样做的目的是提升自身代码可维护性

vue2.0中的组织方式:

src
    ├─compiler   // 模板编译的相关代码
    ├─core   	// 与平台无关的通用运行时代码 
    ├─platforms // 平台专有代码
    ├─server       // 服务端渲染的相关代码
    ├─sfc             // .vue单文件解析相关代码
    └─shared       // 共享工具代码

vue3.0的组织方式:

packages
    ├─compiler-core  
    ├─compiler-dom   	
    ├─compiler-sfc 
    ├─compiler-ssr     
    ├─reactivity            
    ├─runtime-core
    ├─runtime-dom
    ├─runtime-test
    ├─server-renderer
    ├─shared
    ├─size-check
    ├─template-explorer
    └─vue     
  • 相对于Vue.js 2.x的源码组织方式,monorepo把这些模块拆分到不同的package中
  • 每个package都有各自的API、类型定义和测试

这样使得模板拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性。

package(比如reactivity响应式库)是可以独立于Vue.js使用的,这样用户如果指向使用Vue.js 3.0的响应式能力,可以单独依赖这个响应式库而不用去依赖整个Vue.js,减小了引用包的体积大小,而Vue.js 2.x是做不到这一点的

1.2 使用类型语言开发(有类型的JavaScript:TypeScript)

同时对于源码的优化也体现在Vue.js 3.0使用类型语言开发(Vue.js 2.x中使用的是Flow):

  • 其可以在编码期间帮你做类型检查,避免一些因类型问题导致的错误
  • 有利于它去定义接口的类型,利于IDE对变量类型的推导
PS:什么是Flow?
  • Flow是Facebook出品的JavaScript静态类型检索工具,它可以以非常小的成本对已有的JavaScript代码嵌入,非常灵活,这也是Vue.js 2.0当初选型它时一方面的考量
  • 但是Flow对于一些复杂场景类型的检索的支持并不好

2、性能优化

2.1 源码体积优化

  • 移除一些冷门的feature
  • 引入tree-shaking的技术
PS:tree-shaking依赖ES2015模块语法的静态结构(即importexport)通过编译阶段的静态分析,找到没有引入的模块并打上标记,例如,如果你在项目中没有引入TransitionKeepAlive等组件,name他们对应的代码就不会打包这样也就间接达到了减少项目引入Vue.js包体积中的目的

2.2 数据劫持优化

Vue.js区别于React.js的一大特色,是它的数据是响应式的,这个特色从Vue.js 1.0版本就一直伴随着,dom是数据的一种映射,数据发生后可以自动更新到用户,用户只需要专注于修改数据,没有其余的负担,在Vue.js内部想要实现这个功能是要付出一定代价的,那就是必须劫持数据的访问和更新。

Vue.js如何知道更新那一片DOM?

在渲染DOM的时候访问了数据,我们可以对它进行访问劫持,这样就在内部建立了依赖关系,也就知道数据对应的DOM是什么了

之前是如何更新的?

通过Object.defineProperty这个API劫持数据的getset,具体如下:

Object.defineProperty(data,'a',{
	get(){
	 	//track
	},
	set(){
		//trigger
	}
})

这个API有一个缺陷,它必须预先知道要拦截的key是什么,所以并不能检测对象属性的添加和删除。Vue中采用$set$delate

方法去解决这个问题,另外Object.defineProperty的方法还有一个问题,遇到嵌套层级比较深的对象时,Vue,js无法判断你在运行时会访问哪个属性,如果定义的响应式数据十分复杂就会有很大的性能负担。

现在如何更新?

Vue.js 3.0使用了Proxy API自拍数据劫持的对象,其内部:

observed = new Proxy(data,{
	get(){
		//track
	},
	set(){
		//trigger
	}
})

2.2 编译优化

通过在编译阶段优化编译的结果,实现运行时patch过程的优化

Vue.js 2.x的数据劫持更新并触发重新渲染的粒度是组件级的,虽然能保证触发更新的组件最小化,但在单个组件内部依然需要遍历该组件整个vnode树。

例如:

<template>
	<div id="content">
        	<p class="text">static text</p>
        	<p class="text">static text</p>
        	<p class="text">{{message}}</p>
        	<p class="text">static text</p>
       	        <p class="text">static text</p>
    </div>
</template>

以上组件只有第三个p中的数据是动态的

整个diff过程如下:

Diff div
	│
	│
	└─→Diff props of div
			 │
			 ↓
		Diff children of div
			│
			│
			└─→Diff p			Repeat n times
				   │        ┌  ─────────────────── ┐
				   │		↓				       ┃
				   └─→ Diff props of p			   ┃
				   			   │				   ┃
						       ↓				   ┃
					     Diff children of p  ──────┘
					               |
					               ↓
					             End
						

因为只有一个动态节点,所以在这里有很多的diff和遍历都是不需要的,这就导致vnode性能和模板大小正相关,更动态节点的数量无关,对于上面理想状态是只需要diff这个绑定message动态节点的p标签即可

Vue.js 3.0中通过编译阶段对静态模板的分析,编译生成了Block tree

Block tree

  • Block tree是一个将模板基于动态节点指令切割的嵌套区块,每个区块内部节点结构是固定的每个区块只需要以一个Array来追踪自身包含的动态节点
  • 借助Block tree,Vue.js将vnode更新性能与模板整体大小相关提升为与动态内容的数量相关

Vue.js 3.0在编译阶段还包含了对Slot的编译优化、事件侦听函数的缓存优化并且在运行时重写了diff算法

3、语法优化

3.1 优化逻辑组织

Vue.js 3.0提供了Composition API,在之前版本的Vue中编写组件本质就是在编写一个“包含了描述组件选项的对象”,我们称之为Options API

Options API

  • Options API的设计是按照methods、computed、data、props 这些不同的选项分类
  • 当组件小的时候,这种方式一目了然;但是在大型组件中,一个组件可能有多个逻辑关注点,当使用Options API的时候,每一个关注点都有自己的Options,如果需要修改一个逻辑点关注点,就需要在单个文件中不断上下切换和寻找

Composition API

  • 将某个逻辑关注点相关的代码全放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去
  • 除了在逻辑复用方面有优势,也会有更好的类型支持
  • 因为它们都是一些函数,在调用函数时,自然所有的类型就被推导出来了,不想Options API所有的东西使用this
  • 另外,Composition APItree-shaking友好,代码也更容易压缩

3.2 优化逻辑复用

在Vue.js 2.0中我们通常会用mixins复用逻辑

mixin的缺点

  • 首先每个mixin都可以定义自己的props、data,它们之间是无感的所以很容易定义相同的变量,导致命名冲突

3.2 引入RFC:使每个版本改动可控

RFC:Rquest For Comments,旨在为新功能进入框架提供一个一致且可控的路径

大规模启用RFC,可以了解每一个feature采用或被废弃掉的前因后果

3.3 过渡期

Vue.js 2.0中通常major版本的升级会有很多breaking change,这就意味着想从2.x升级到3.0的项目需要改代码,而且不仅仅项目的代码要改,所依赖的周边生态也需要升级

Vue.js 3.0使用ES2015的语法开发,有些API如Proxy是没有polyfill的,这就意味着官方需要单独出一个IE11 compat版本来支持IE11

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值