9

Vue3.0 性能优化及新特性深度解析

 3 years ago
source link: https://juejin.cn/post/6844904199726055437
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

最近,Vue作者尤雨溪在B站的演讲中分享了Vue3.0的六大亮点:

  • Tree-shaking 支持
  • Composition API
  • Fragment、Teleport、Suspense
  • 更好的 TS 支持
  • 自定义渲染API

编译时对VDom的性能优化

PatchFlag

首先看下面这个案例,模版中有三个P标签,其中只有最后一个P标签的TEXT部分是动态的

1

在之前的VDOM中,如果msg值发生改变,整个模版中的所有元素都需要重新渲染。但在Vue3.0中,在这个模版编译时,编译器会在动态标签末尾加上 /* Text*/ PatchFlag。只能带patchFlag 的 Node 才被认为是动态的元素,会被追踪属性的修改。并且 PatchFlag 会标识动态的属性类型有哪些,比如这里 的TEXT 表示只有节点中的文字是动态的。

每一个Block中的节点,就算很深,也是直接跟Block一层绑定的,可以直接跳转到动态节点而不需要逐个逐层遍历。

既有VDOM的灵活性,又有性能保证。

hoistStatic 静态节点提升

当使用hoistStatic时,所有 静态的节点都被提升到render方法之外。这意味着,他们只会在应用启动的时候被创建一次,而后随着每次的渲染被不停的复用。

1

在大型应用中对于内存有很大的优化。

cacheHandler 事件监听缓存

正常情况下,当绑定一个事件:

<div>
  <p @click="handleClick">静态代码</p>
</div>
复制代码

模版会被编译为

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("p", { onClick: _ctx.handleClick }, "静态代码", 8 /* PROPS */, ["onClick"])
  ]))
}
复制代码

其中事件会每次从全局上下文中获取。而当开启了cacheHandler之后

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("p", {
      onClick: _cache[1] || (_cache[1] = ($event, ...args) => (_ctx.handleClick($event, ...args)))
    }, "静态代码")
  ]))
}
复制代码

编辑器会为你动态创建一个内联函数,内联函数里面再去饮用当前组件上最新的handler。之后编辑器会将内联函数缓存。每次重新渲染时如果事件处理器没有变,就会使用缓存中的事件处理而不会重新获取事件处理器。这个节点就可以被看作是一个静态的节点。这种优化更大的作用在于当其作用域组件时,之前每次重新渲染都会导致组件的重新渲染,在通过handler缓存之后,不会导致组件的重新渲染了。

SSR 服务端渲染

当开启SSR了之后,如果我们模版中有一些静态标签,这些静态标签会被直接转化成文本。

1

其中的动态绑定依然是一个单独的字符串内嵌进去。这个性能肯定比React 转成VDOM在专为HTML快很多。

StaticNode 静态节点

刚才提到在SSR中静态的节点会被转化为纯字符串。如果在客户端,当静态节点嵌套足够多的时候,VUE编译器也会将VDOM转化为纯字符串的HTML。即 StaticNode。

1

通过这些操作,我们可以看下,跟vue2比可以快一倍以上,内存占用可以小一倍以上。

1

Tree Shaking

因为ES6模块是静态引用的,所以我们可以在编译时正确的判断到底加载了哪些代码。对代码全局做一个分析,找到那些没用被用到的模块、函数、变量,并把这些去掉。
复制代码

当使用一个 bundle (webpack etc.)的时候,默认会加上 TreeShaking。Vue 3.0 中没有被用到的模块可以不被打包到编译后的文件中,被 TreeShake 掉。当只有一个HelloWorld的时候 Vue3打包后 13.5kb。所有的组件全部加载进来时是 22.5kb

Composition API

随着Vue组件的增大,组件内代码变得越来越难以理解和维护。其中的一些可以复用的代码很难被抽离出来。同时 Vue2.0还缺少 TS支持。在Vue2中,逻辑概念(功能)被管理在组件中,但是功能和组件并不是一对一关系。一个功能可以被多个组件使用同时一个组件可以有多个功能。在Vue中,一个功能可能需要依赖多个Options(components、props、data、computed、methods及生命周期方法)。

在 Composition API中提供可 setup 方法。以一个有搜索功能和 排序功能组件为例:

<script>
export default {
    setup() {
    
    }
}

function useSearch() {
    return { 
    ...useSearch(), 
    ...useSorting()
    }
}

function useSorting() {
    
}

</script>
复制代码

Vue2 中的代码复用

Mixin

在Vue2中有几种方式可以复用代码,其中之一就是 Mixins。

  • Mixins可以实现组织功能
  • 容易发生冲突
  • 很难说明依赖关系
  • 代码不容易复用

Mixin 工厂

  • 可以方便复用
  • 明确的依赖关系
  • 弱命名空间
  • 隐性的属性添加

Scoped Slots

  • 解决了 Mixin 的问题
  • 增加了层级关系导致更难以理解
  • 很多配置信息
  • 灵活性更少

核心 API

  • reactive
  • computed
  • readonly
  • watchEffect
  • watch
  • Lifecycle Hooks

Fragments

Vue3中不在要求模版的跟节点必须是只能有一个节点。跟节点和和render函数返回的可以是纯文字、数组、单个节点,如果是数组,会自动转化为 Fragments。

Teleport

对标 React Portal。可以做一些关于响应式的设计,如果屏幕宽度比较宽的时候,加入某些元素,屏幕变窄后移除。

Suspense

等待嵌套的异步依赖。再把一个嵌套的组件树渲染到页面上之前,先在内存中进行渲染,并记录所有的存在异步依赖的组件。只有所有的异步依赖全部被resolve之后,才会把整个书渲染到dom中。当你的组件中有一个 async的 setup函数,这个组件可以被看作是一个Async Component,只有当这个组件被Resolve之后,再把整个树渲染出来

  • async setup()
  • Async Component

Typescript

Vue3源码使用 TS重写,但不意味着vue3的项目也要使用TS。但Vue3会对 TS有更好的支持

  • 支持 TSX
  • 支持 Class component
  • 代码会变大一些

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK