vue3
performance 性能
tree-shaking support 按需打包
composition API
Fragment,Teleport,Suspense
Better TyperScript support (更好的TyperScript支持度)
Custom Render API (自定义的Render API)
Performance
Rewritten virtual dom implementation (virtual dom 重构)
传统virtual dom的性能瓶颈:
(1)虽然vue能够保证触发更新的组件最小化,但在单个组件内部依然需要遍历该组件的整个virtual dom树。
(2)传统virtual dom的性能跟模板大小正相关,跟动态节点的数量无关。在一些组件整个模板内只有少量动态节点的情况下,这些遍历都是性能的浪费。
(3)JSX和手写的render function是完全动态的,过渡的灵活性导致运行时可以用于优化的信息不足。
为什么不抛弃virtaul dom呢?
(1)高级场景下手写render function获得更强的表达力;
(2)生成的代码更简洁
(3)兼容2.x,在2.x中有的用户会手写render function,甚至会使用JSX
vue的特点是底层为Virtual DOM,上层包含大量静态信息的模板。为了兼容手写render function,最大化利用模板静态信息,vue3.0采用了动静结合的解决方案,将virtual dom的操作颗粒度变小,每次触发更新不再以组件为单位遍历。vue 3利用编译器的特性,通过分析模板,进行一些模板编译时的分析优化,来生成更加优化的virtual dom render function,这样就可以大大减少通过编译时在生成的javascript留下的优化的提示。
vue3.0 virtual dom主要改动:
(1)将模板基于动态节点指令切割为嵌套的区块,每个区块内部节点结构是固定的,每个区块只需要以一个Array追踪自身包含的动态节点,使得每次触发更新与动态内容的数量相关。
(2)virtual dom提供了一种模板之外的逻辑表达能力,更好、更灵活逻辑表达的一种能力(对于库的作者、高级工具的作者能够脱离模板,做一些相对复杂的一种渲染逻辑的表达)。
Compiler-informed fast paths
编译优化,Runtime性能可以获得很大的提升。
(1)virtual dom的优化
(2)proxy的使用
vue 2.0的每创一个组件实例的开销比较大,每次创建一个实例的时候,要在this上面暴露很多东西,这些每一个暴露在this上面的属性都是用Object.defineProperty去定义的,然后这个操作其实还挺费时的(需要层层递归自定义属性描述符),在基于proxy的前提下,把属性定义的过程直接丢掉,然后暴露给渲染函数的这个this其实是一个proxy,去proxy拿东西的时候,动态得给你决定返回什么东西。
像vue 2.0定义了一对methods、computed、data、props,这些全都要从this上面去暴露,之前的办法就是一个一个得Object.defineProperty上去,现在就等于说一个属性的get拿到之后,我们proxy拦截你,然后根据我们之前已经知道的你这个属性是个prop还是啥,直接从prop上返回给你。
More efficient component initialization
组件初始化创建在vue 3里面也做了特定的优化。
1.3~2x better update performance
相比vue 2有1.3到2倍的性能优势。
2~3x faster SSR
服务器渲染也完全重写,从模板编译到服务器渲染生成完全不同的渲染函数,可以比vue 2快2~3倍。
模板分析diff时跳过静态节点
静态节点被提升到渲染函数的外部,在渲染的时候,我们将这些静态的内容传入,这就使得我们只需要在第一次创建的时候去生成这些节点,在后续的更新中,只需要去传入这些节点,这样就不需要重复创建节点。即:这些静态节点,只需要创建一次,不用在运行更新的时候创建新的、销毁旧的,使得内存也得到了优化。同时,更新时会直接跳到对应的dom更新且只会关注动态的一些属性,这样不用diff每个DOM,无论层次多么深,直接到了要改变状态的DOM,使得性能得到提高。这样既跳出了virtual dom更新的性能瓶颈,又依然保留了可以手写render function的灵活性。
事件侦听器的缓存 cashehandlers
对onclick进行一个缓存处理,当第一次渲染的时候,因为不存在_cache[1],所以vue会自动生成一个内联函数,给cache[1]赋值_cache[1] = (...args) => (_ctx.a(...args)
,使我们能自动去调用组件上最新的onclick。在后续的更新中,我们只需要从缓存_cache[1]中去读取同一个函数即可,而既然是同一个函数,那也没有被更新的必要了,所以@click也会被看成静态的。
在click里面手写内联函数也可以被cashe起来,JSX中这样写,就会导致这个span每一次都会生成一个新的函数,这个函数重新绑定到这个元素上。在vue里面启用了cashehandlers,即使写的内联函数也被看成静态的。
这种优化在要给组件传入一个函数时尤为明显,如果不使用事件监听缓存的话,那么在父组件更新的时候,子组件就会跟着更新。而通过事件监听缓存机制,我们传给子组件的函数,它会在调用时动态地去找到组件中最新的那个函数,不需要对子组件进行更新。
SSR渲染优化
在SSR渲染时,会在服务端尽可能地将静态部分处理为字符串返回。如果存在动态节点,也会竟可能放在字符串里面,动态数据编译成模板字符串推入。
Tree-shaking
Most Optional feature (e.g. v-model, <transition></transition>) are now tree-shakable
Bare-bane HelloWorld size: 13.5kb
11.75kb with only composition API support
All runtime features included: 22.5kb
more features but still lighter than Vue 2
(1)使更多可选的内容可以被tree-shaking,比如v-model和<transition>现在就可以被tree-shaking了,没有用到的时候,打包就不会被放入项目中;
(2)模板中只包含一个简单的Hello World时,框架为项目带来的大小只增加了13.5kb。如果采用了Composition API支持替代其他options的API的话,会变成11.75kb大小,在vue3.0中为了兼容之前版本,默认不会把options的API去掉,但是会有开关提供把这些API去掉的选项;
(3)即便是vue3.0的东西都放进去,也只有22.5kb(包括所有的新功能)。这个大小比vue2.x小,但功能却更多。
webpack是有tree-shaking这个功能的,但Tree Shaking前提是ES Module(import…)来写。vue 3在浏览器中,依然会有一个全局的vue对象,但是在用webpack的时候,它就没有defualt export,你就不能import vue,把vue当对象本身去操作,所有的api就要import进来。这样使得一些不会被用到的功能不会被引用进来。
composition API
https://composition-api.vuejs.org/
(1)能和options API一起使用
composition api可以视为新添加的api,不影响已有api的使用,甚至可以跟已有api一起使用。
(2)灵活的逻辑组合与复用
在vue2.x里面,我们可能会采用mixin来进行逻辑的抽取。尽量使用composition api来抽逻辑。如果是逻辑库的作者,提供可复用逻辑的时候,尽量也用composition api去提供。
(3)几个主要的API
vue 2:data传给Vue,Vue就把它变成了响应式。
vue3:
把一个对象给它,然后把对象变成响应式,然后,Vue在需要的地方去追踪它所用到的响应式的依赖,当依赖变化的时候重新去渲染。(reactive)
vue 3 里暴露一个新的API叫做watchEffect,effect就是副作用。比如:打印一个数字,这个数字是从响应式的对象上取出来,第一遍执行并且取到这个数字的时候,这个数字就被作为一个依赖,追踪在当前这个副作用函数里面,每当这个依赖变化的时候,这个副作用函数就会再跑一遍。
以及没有办法被对象的方式被响应化的数据,比如一些原生的数据结构,比如像数字、Boolean等,那布尔值这些就需要用一个东西包装(ref)
当使用对象的时候,就不需要去包一下,但如果一定要传一下非对象的数据,就得用ref去包一下。
核心:reactive \ ref \ watchEffect \ 其他是这三者的组合使用。
Fragment,Teleport,Suspense
Fragment
No longer limited to a single root node in tamplates
Manual render functions can simple return Arrays
“Just works”
vue 2 模板只有一个单独的根节点。vue 3:文字、多个节点、甚至也可以是个v-for,都会自动变成碎片,如果使用渲染函数,也可以直接在渲染函数里面返回一个数组,自动变成一个碎片。
Teleport
Previously known as <Portal>
More details to be shared by @Linusborg
对标react Portal,可以接收一个disable的参数,就会把本来传送出去的东西,挪回来,放在原来的渲染树里面。disabled用于做一些响应式的设计,把屏幕宽的时候,可以把teleport拉出去,窄的时候,可以放回原来树的layout
多个teleport添加内容到同一个component。
Suspense
Wait on nested async dependencise in a nested tree
Works with async setup()
Works with Async Components
页面数据到达之前,可以先呈现别的东西,来优化使用体验
相比react走轻量路线。在把嵌套的组件树渲染到屏幕上之前,先在内存中进行渲染,在渲染的过程中,会记录所有的存在异步依赖的组件,当所有嵌套的异步依赖全部被resolve之后,才会把整个树,渲染到dom里面去,异步依赖再结合composition API,就可以使用async setup() 的选项。在你的组件里面,如果你有一个async的 setup 函数,这个组件就会被看做一个异步的组件,这个suspense就会等待所有的async setup、promise resolve之后,再把整个数渲染出来,实现了一定程度上的嵌套的异步调度。
suspense 本身,可以使用一个底层的异步元语,实现更加高级的功能,比如说vue 3异步组件,内部就是通过 suspense 和 async setup 去做的。默认可以不跟suspense绑定,如果你想用suspense去管理一些嵌套的异步组件间的加载也是可以加的。
Better TypeScript Support
Codebase written in TS w/ auto-generated type definitions
API is the same in JS and TS
In fact, code will also be largely the same
TSX support
Class component is still supported
vue-class-component@next is currently in alpha
(1)vue 3 使用TS重写的,但不代表要写TS,在vs code装了插件,即使用的JS也会补全跟参数提示,当然用了TS会用更好的静态检查;
(2)在js和ts中API是相同的,事实上,代码也基本相同;
(3)TSX的支持;
(4)class组件还会继续支持,但是需要引入vue-class-component@next,该模块目前还处在 alpha 阶段,不是推荐的使用方式;
Custome Render API
NativeScript Vue integration underway by @rigor789
Users already experimenting w/ WebGL custom renderer that can be used alonside a normal Vue application (Vugel)
(1)正在进行NativeScript Vue集成;
(2)用户已经可以尝试使用WebGL自定义渲染器,它可以与普通Vue应用程序一起使用;
自定义渲染器api,vue 2也可以做,之前weex、nativescript vue都是通过vue 2的一个非暴露的api,去实现这个功能。但是vue 2引入了一些依赖上同步的问题。vue 3就是一个内置的api。
最后:router、vuex都是以兼容vue3为重点,目前不会有太大改动。
参考文章(非常感谢知识的分享):
https://zhuanlan.zhihu.com/p/92143274
https://blog.csdn.net/zemprogram/article/details/105726956
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。