谈谈vue3.0要更新的内容相关笔记

从去年就一直说的沸沸扬扬的vue3.0发布,虽然到现在还没有正式发布的事件,但做为前端开发,我们还是要去了解的,那么vue3.0会更新什么内容呢,早在2018年11月尤大就在远程演讲中说到了,vue3.0会带来

更快
更小
更易于维护
更好的多端渲染支持
新功能(新的API)

下面就这些内容来说一下vue3.0的更新,这些内容基本上都是我在看了2018年和2019年的中国Vue conf视频后整理出来的,如果哪里写错了,请路过大佬帮忙指出,十分感谢
后续如果有其他的更新我也会回来更新这篇博客,那么开始今天的博客分享之旅
一、 更快

  1. 基于模板编译和Vitrual DOM性能方面的优化
    使用typescript重写Virtual DOM
    我们知道,vue是使用Vitrual DOM内部渲染机制,在vue3.0中,Vitrual DOM进行了一次完全的重构,结合了模板编译等小技巧来提高性能,使得初始渲染的提速最高可以达到翻倍。
    在之前的Vitrual DOM渲染机制中,即使是静态数据的内容节点,渲染机制仍然会重新生成这些节点,然而这实际上是不必要的,在vue2.x中,已经对这部分内容做了一定的优化,通过对模板的分析,对一些静态数据的内容不再重复生成,但还不够彻底,而在vue3.0中,对这些做了更多的优化
    在vue2.0中,对一个标签,无论是组件还是原生的标签,都会先以字符串的形式,传入到h这个函数中,h是用来生成虚拟node的函数,而判断一个标签是否为组件,实际上是在运行时判断的,这样就会导致在编译时会做重复的工作,而实际上,这个工作可以在编译的时候完成,如果我们知道它是一个原生的元素,那就生成相应的原生元素对应的Vitrual DOM的代码,同样的,如果判断为一个组件时,就生成组件的代码
    (1)一些底层的优化
    此外,还有一些比较底层的优化,像比如

在调用函数处理时,函数的参数尽可能一样多,这样有益于js引擎去做一些性能优化
在模板中直接静态地分析一个元素所包含的子元素的类型,给运行时留下一些提示,这样可能会减少一些判断,举个例子,像下面的结构

复制代码div中只有一个span标签,在生成虚拟node的时候,标明其只有一个子元素,那么算法里面可以直接跳入只有一个元素的分支,这样就可以跳过很多的判断 (2)优化slots生成
{{ hello }}
复制代码在之前的vue2.0中,我们像上面这样给一个组件传入一个slot值,在这个值更新的时候,父子组件都会更新,即使在父组件其他位置没有用到这个值,而实际上我妈只是要更新这一个值而已 在vue3.0的新编译机制中,把所有的slot统一编译一个函数,当我们要将一个slot传给子组件的时候,将这个函数传给子组件,而调用这个函数,是子组件的行为,所以这个“hello”的依赖,就成为了子组件的而不是父组件的,所以当hello变动时,我们只需要重新渲染子组件,而不必再去渲染父组件了 (3)静态内容提取 在vue2.0里面已经有做过了一些处理,如果检测到一部分模板不变,可以先提取出来,在之后的更新中,这一部分模板不仅可以直接使用原来的Vitrual DOM,甚至连整个比对过程都能直接跳过这整个树 vue2.0没做到的是,只要一个元素里面任意深度包含着一个动态数据,那就无法将其静态化,但还是可以做一些优化的,像下面这个元素,虽然其内含有动态数据text,但是这个div的属性是静态的
{{ text }}
复制代码所以我们在比对时,就不需要再去比对这个元素了,可以跳过它,直接去比对它的children即可 (4)内联事件函数提取

Diff

Diff props of


Diff children of

Diff

Diff props of


Diff children of

Repeat n times…

而实际上,这里只有个message会发生改变,其他内容都是动态的,但是这里会因为这一个message发生改变,导致整个模板进行一次diff,这样就做了很多不必要的消耗了
所以在vue3.0中,将vdom更行性能由与模板整体大小相关提升为与动态内容的数量相关,做到以下几点

将模板基于动态节点指令切割为嵌套的区块
每个区块内部的节点结构是固定的
每个区块只需要以一个Array追踪自身包含的动态节点
这样子,当一个数据发生改变时,我们只需要去diff它所在的那个最小区块即可,所以上面的代码结构在vue3.0之后,diff会是
Diff

textContent

这个区块的大小,甚至可以小到class的名字,因为实际上为了改变样式,更改className是非常常用的,所以如果className是动态内容,那么会将其划分为一个单独的区块,在进行diff算法时直接设置一个className即可,这样又对性能做到了很高的优化
这种优化甚至快了6倍(在2019Vue conf上做的一个测试做到了36ms->6ms)
2. 基于proxy的新数据监听系统
vue2使用的是Object.defineProperty的set/get

对象属性增添/删除

数组index/length更改

Map,Set,WeakMap,WeakSet

Classes

事实上基于proy的监听,是所谓的lazy by default,只有当一个数据被使用到的时候,我们才会真正地去监听这个数据,所以对于一个大规模数据的监听,在之前使用Object.defineProperty的监听中,我们对一个数据监听,不管这个数据是否使用到,都会对其进行监听
利用proxy减少组件实例初始化开销
每个vue组件都会代理它所包含的所有data,computed以及props,而这些代理都是通过Object.defineProperty实现的
在实际的实例化中,大量的Object.defineProperty实际上是一个昂贵的操作,而在vue3.0中,暴露给用户的this,实际上是真正组件实例的一个Proxy,所以我们实际上是在一个Proxy上获取数据,而在这个Proxy上获取数据时,我们再做一些内部的判断,这样就彻底避免了Object.defineProperty的使用
二、更小

让代码结构和tree-shaking配合起来

内置组件(keep-alive,transition…)
指令的运行时helper(v-model,v-for…)
各种工具函数(asyncComponent,mixins…)

之前Vue的整个代码都是直接将整个Vue对象放进来,所以一些没用到的东西,也无法通过tree-shaking将其扔掉,所以这样就会造成即使Vue里面的一些功能我们没有用到,我们还是会将其放入到项目中,这就造成了不必要的内存消耗(即使这部分内容不大)
而在Vue3.0中,采取了ES module imports按需引入的方式。通过这种做法,如果我们没有用到一些内置组件时,在编译时就不会去加载这些内容。所以如果我们只用到所有Vue应用都会用到的一些核心功能,那我们就只会加载相应的这些核心功能相关的代码,而这一部分代码在所有无关的东西都使用tree-shaking处理后,只有大概在10kb左右
三、更易于维护
vue3.0从Flow迁移到TypeScript,使用TypeScript重写了
内部模块解耦
降低源码阅读的难度
编译器重构

插件化设计
带位置信息的parser(source maps)
为更好的IDE工具链铺路

四、更好的多端渲染支持
vue2中需要自己去fork源码
vue3.0引入一个Custom Render API
五、新功能(新API)
响应式数据监听API
之前没有显式地将Vue的响应式功能暴露出来,而在vue3.0中可以通过import引入两个函数使用
import { observable , effect } from ‘vue’

const state = observable({
count: 0
})

effect(()=>{
console.log(count is : ${state.count})
}) // count is : 0

state.count++ // count is : 1
复制代码上面引入了observable和effect,observable可以用来创建一个显式的响应式的对象,在effect中依赖的数据,就会被注册依赖,在之后当这个对象被改动到的时候,effect就会重新执行一遍
暴露这个API的主要目的,是为了轻松地实现跨组件的状态共享
轻松排查组件更新的触发原因
提供了renderTriggered API,每一次这个组件触发了更新的时候,可以在这个API中放入一个debugger,如下代码
const Comp = {
render(props){
return h(‘div’,props.count)
},
renderTriggered(event){
debugger
}
}
复制代码我们就可以在浏览器中直接地看到究竟是哪一行触发这个更新,同时event还会提供更具体的更新,包括了触发更新的对象,更新前的值和更新后的值,以及这个对象中被改动的属性,触发更新的操作是什么(比如set)
更好的typescript支持
使用了原生的classAPI,不再需要去依赖Vue Class Component这个库了(现在已经被撤销了)
采用了Function-based API
const App = {
setup(){
const count = value(0)
const plusOne = computed(() => count.value+1)
const increment = () => { count.value++ }
watch(() => console.log('mounted!))
// 暴露给模板或渲染函数
return { count }
}
}
复制代码对比于Class API

更好的TypeScript类型推导支持
TypeScript对函数的参数和返回的内容类型支持是很好的,使用这个API,js和ts写出来是一模一样的,而且会给出类型补全
更灵活的逻辑复用能力
使用这个API可以做到

没有命名空间冲突
我们将逻辑写在一个函数中,返回时可以使用解构赋值的方式获取里面的值,这样也能重写变量名,不会造成命名空间冲突
数据来源清晰
数据可以清晰地从这个函数中获得
没有额外的组件性能消耗
相对于之前版本需要使用Mixins,高阶组件,slot这些方法,使用这个API只需要去写一个函数即可

Tree-shaking友好
这里面的方法都是使用import引入的,即按需引入,如果我们不引入的话,就会被tree-shaking丢掉
代码更容易被压缩
在代码压缩的时候,一个函数内部的一些变量或者函数名会被压缩成一个字母,但是出于安全考虑,对象的属性是不会被压缩的,所以在代码压缩的时候就更容易了

对比React Hooks

同样的逻辑组合、复用能力
只调用一次

符合js直觉
没有闭包变量问题
没有内存/GC压力
不存在内联回调导致子组件永远更新的问题

支持TSX
更好的警告信息

组件堆栈包含函数式组件
可以直接在警告信息中查看组件的props
在更多的警告中提供组件堆栈信息

(Experimental)Time Slicing Support
开启这个API会将JavaScript的操作切成一部分一部分,每执行一段时间,就会将主线程还给浏览器,让浏览器得以去监听用户事件并做出相应
在2019年的中国vue conf中,尤大提到了如果做的够快,可以缓解对时间分片的需求,所以这个功能最后是否会在vue3.0里出现,现在我也不清楚

发布了68 篇原创文章 · 获赞 1 · 访问量 936

猜你喜欢

转载自blog.csdn.net/A669MM/article/details/104954055