
# Vue3.0 Beta笔记(侧重Performance提升原因和Composition API)
# 前言
昨晚(北京时间2020年4月22日),Vue作者Evan You(尤雨溪)在前端圈的B站直播间分享了关于'Vue3.0 Beta'的一些新的特性和进展。(在此附附上掘金录播链接:https://juejin.im/e/vue-3)
本文,是个人记录的一些笔记,更关注与侧重Performance提升原因和Composition API,这两部分记录的比较详细,相当于是下方连接的一些补充,欢迎大家留言补充和探讨。
参考链接:抄笔记:尤雨溪在Vue3.0 Beta直播里聊到了这些…
# 一、RFCs(Request For Comments)
尤大在直播开头提到,所有的Vue3大的的改动都是通过 Vue3-RFCs GitHub仓库上和大家进行讨论,所有进度和改动细节都能在其中看到,不需要再获取别人的二手信息了。如果大家想要深入了解可以点击上面的连接。(PS:看的真的会很累,讨论地特别细。)
# 二、六大亮点
- Performance:性能更比
Vue 2.0
强。(update 性能提高1.3-2倍和ssr服务端渲染速度快2-3倍,基于bechmark) - **Tree shaking support:**可以将无用模块“剪枝”。
- **Composition API:**组合
API
相对于Vue2.x
的Options API
(后面细讲) - Fragment, Teleport, Suspense
- **Better TypeScript support:**更优秀的Ts支持
- **Custom Renderer API:**暴露了自定义渲染
API
# 1.Performance提升的原因(视频中通过以下Demo展示)
可以通过点击vue-next-template-explorer进入视频中模板编制的Playgruond。其永远和vue3.0 github上master最新的commit保持一致
(1)模板编译的优化
代码:
<div>
<span/>
<span>{{ msg }}</span>
</div>
将会被编译成以下模样:
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("span", null, "static"),
_createVNode("span", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
}
// Check the console for the AST
根结点div
将会被编译成Block
动态绑定msg
属性的span
,编译后_createVNode会生成PacthFlag
(相当于编译时生成一个hint),JS runtime在运行的时候,会知道div
是一个block
,只会对带有PacthFlag
的结点进行真正的追踪。在真正的更新的时候,会直接跳到该结点,比较该结点文字的变化。不需要去关注其他属性和绑定的变化。
# PatchFlags枚举定义
export const enum PatchFlags {
TEXT = 1,// 表示具有动态textContent的元素
CLASS = 1 << 1, // 表示有动态Class的元素
STYLE = 1 << 2, // 表示动态样式(静态如style="color: red",也会提升至动态)
PROPS = 1 << 3, // 表示具有非类/样式动态道具的元素。
FULL_PROPS = 1 << 4, // 表示带有动态键的道具的元素,与上面三种相斥
HYDRATE_EVENTS = 1 << 5, // 表示带有事件监听器的元素
STABLE_FRAGMENT = 1 << 6, // 表示其子顺序不变的片段(没懂)。
KEYED_FRAGMENT = 1 << 7, // 表示带有键控或部分键控子元素的片段。
UNKEYED_FRAGMENT = 1 << 8, // 表示带有无key绑定的片段
NEED_PATCH = 1 << 9, // 表示只需要非属性补丁的元素,例如ref或hooks
DYNAMIC_SLOTS = 1 << 10, // 表示具有动态插槽的元素
// 特殊 FLAGS -------------------------------------------------------------
HOISTED = -1, // 特殊标志是负整数表示永远不会用作diff,只需检查 patchFlag === FLAG.
BAIL = -2 // 一个特殊的标志,指代差异算法(没懂)
}
更具体的例子:
加上一大堆的静态内容:
在默认的Diff的算法下,会把所有的静态的span
都检查一遍,而且每个span
都要看新的Props
和旧的有没有变化,虽然说JS做这写效率很高,但是当所需要的更新的结点量很大时,不可避免地会浪费很多的时间。
在Vue 3.0的Diff的算法中,只需在Block中寻找带PacthFlag
的结点,只要把这些结点检查一遍就行了,这样就解决的传统Diff算法中最耗时最浪费性能的部分。
无论层级嵌套多深,它的动态节点都直接与Block
根节点绑定,无需再去遍历静态节点
如果存在一个动态的绑定和静态绑定:
我们在Diff时,只会关注id
是否变化,不会关注class
的变化。
PatchFlag
变成了9 /* TEXT, PROPS */, ["id"],
它会告知我们不光有TEXT
变化,还有PROPS
变化(id)
这样既跳出了virtual dom
性能的瓶颈,又保留了可以手写render
的灵活性。 等于是:既有react
的灵活性,又有基于模板的性能保证。
**react
是否能在JSX做这样的东西呢?**在简单的情况下可以做,因为JSX
是JS
的语法比模板本身的语法要灵活的多。
所说的简单的情况:
hostStatic(把静态的结点提升)
静态的结点被拿到渲染函数外,在应用启动的时候被创建一次,这些虚拟结点就会在每次渲染的时候被复用。优化大型项目的内存占用,不用每次渲染都创建这些结点了。
# (2) 添加事件监听缓存:cacheHandlers
关闭cacheHandlers
:
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("span", { onClick: _ctx.onClick }, _toDisplayString(_ctx.msg), 9 /* TEXT, PROPS */, ["onClick"])
]))
}
开启cacheHandlers
:
cache[1]
,会自动生成并缓存一个内联函数,“神奇”的变为一个静态节点。
export function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("span", {
onClick: _cache[1] || (_cache[1] = $event => (_ctx.onClick($event)))
}, _toDisplayString(_ctx.msg), 1 /* TEXT */)
]))
}
并且支持手写内联函数:
<div>
<span @click="()=>foo()">
{{msg}}
</span>
</div>
# (3)SSR(server side render服务端渲染)优化
静态内容会直接当做纯字符串推进一个buffer里去了。
即使存在动态绑定,依然尽可能地做成字符串。
_ssrRenderAttr("id",_ctx.foo)
当静态结点数量超过一定阈值时,启用这样一个优化。单独的创建一个div,将其设置成innerHTML。
比React
做成一个Virtual Dom
再去渲染出来,快上一个量级。服务端渲染的性能,完全不在一个层面上。
整体上,比Vue 2.x
内存占用少一半以上,总体速度快一倍以上。
2.Composition API(组合) 和 Options API的对比
组件小的时候,用不同的Options比如methods、compute、data、props等这样分类比较清晰。大型组件中,大量的Options聚在一起。同一个组件可能有多个逻辑关注点,当使用Options API时,每一个关注点都有自己的Options,如下图每一个颜色代表不同的逻辑关注点之间的代码。当修改一个逻辑关注点时,就要在一个文件不断地切换和寻找。
**如要用切分这些逻辑点呢?**有时候,不好切分,如果用minxin又会导致命名空间冲突。
Composition API给了一个很好的机制去解决这样的问题,所有某一个逻辑关注点(功能)相关的代码全都放在一个函数里,当需要去修改一个功能时,就不再需要在一个文件中跳来跳去。
当需要复用的时候,就只需要把这个函数提取出去。然后在另一个组件中引入,这个功能就变得可复用了,Composition API使得组件复用变得更加灵活了。
另一方面,**Composition API会有更好的类型的支持,**因为都是一些函数,在调用函数时,自然所有的类型就被推导出来了。不像OptionsAPI所有的东西使用this。**同时,Composition API的可压缩性会更好一些。**以上就是Composition API引入的理由。
具体地Composition API如何使用可以同过这个网站[Vue Composition API](https://juejin.im/editor/posts/Vue Composition API)来学习。