vue3系列

326 阅读3分钟

1.vue3设计目标,做了哪些优化

更小

  • 移除一些不常用的API
  • tree-shaking,将无用模块剪辑,仅打包所需要的,使得打包的体积整体减小

更快

主要体现在编译阶段

  • diff算法优化
  • 静态提升
  • 事件监听缓存
  • SSR优化

更友好

  • TypeScript支持

更好的类型检查 + 复杂类型推导 + 源码跳转 + 问题定位 + 调试便捷

  • composition api

增强了代码逻辑组织和复用能力

2.vue3性能提升通过哪几个方面体现

编译阶段

vue2中的响应式系统,每一个组件实例对应一个watcher,它会在组件渲染的过程中把使用到的数据property记录为依赖,当依赖发生改变时候触发setter,则会通知watcher,从而使关联的组件重新渲染。

模版、动态节点、静态节点

  • diff算法优化

Vue 3 Virtual Dom Diff源码阅读

vue3源码diff算法地址

1)预处理优化:

与vue2的双向遍历不一样,vue3首先会进行头尾的单项遍历,进行预处理优化

2)静态标记

vue2在patch阶段会进行全量diff,但是有的节点明明是不变的,没必要去diff,vue3中提出PatchFlags的概念,在可能变化的地方加一个flag标记,下次变化的时候直接找改地方进行比较

export const enum PatchFlags {
  // 动态文本
  TEXT = 1,

  // 动态class
  CLASS = 1 << 1,

  // 动态style
  STYLE = 1 << 2,

  // 动态props
  PROPS = 1 << 3,

  // 动态变化的属性,比如:[attr] = "foo"
  FULL_PROPS = 1 << 4,

  ...
}
  • 静态提升

vue3中对于不参与更新的元素,会做静态提升,只会创建一次,在渲染时直接复用

对于模版

<span>你好</span>

<div>{{ message }}</div>

静态提升之前

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock(_Fragment, null, [
    _createVNode("span", null, "你好"),
    _createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
  ], 64 /* STABLE_FRAGMENT */))
}

静态提升之后,静态内容在render函数之外,每次渲染取缓存中的进行复用即可,同时被打上了patchFlag,静态标记值是 -1

const _hoisted_1 = /*#__PURE__*/_createVNode("span", null, "你好", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock(_Fragment, null, [
    _hoisted_1,
    _createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)
  ], 64 /* STABLE_FRAGMENT */))
}

  • 事件监听缓存

默认情况下绑定事件行为会被视为动态绑定,所以每次都会去追踪它的变化

  • SSR优化

当静态内容达到一定量级的时候,会使用createstaticVnode方法在客户端生成一个static node,这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染

源码体积减小

移除一些不需要的API + tree-shaking

composition api 中任何一个函数,仅仅在使用到才会被打包,没用到的模块都会被tree shaking

响应式系统

相比于vue2的Object.defineProperty方式对对象属性进行劫持带来的性能问题,以及数据属性的添加和删除无法做到响应式(提供了Vue.set和Vue.delete),vue3使用Proxy重写了响应式系统

3.为什么要使用Proxy代替defineProperty

defineProperty方式

function defineReactive(data, key, val){
    const dep = new Dep()
    Object.defineProperty(data, key, {
        get(key){
            if(Dep.target){
                dep.depend
            }
            return val
        },
        set(key, newVal){
            if(val !== newVal){
                val = newVal
                dep.notify()
            }
        }
    }
}

proxy模式

function reactive(data){
    const observed = new Proxy(obj, {
        get(target, key, receiver) {
            const res = Reflect.get(target, key, receiver)
            return isObject(res) ? reactive(res) : res
        },
        set(target, key, value, receiver) {
            const res = Reflect.set(target, key, value, receiver)
            return res
        },
        deleteProperty(target, key) {
            const res = Reflect.deleteProperty(target, key)
            return res
        }
    })
}

弊端:

Proxy 不兼容IE,也没有 polyfill, defineProperty 能支持到IE9

我想这也是没有大面积开始使用vue3的原因吧

4.Options API 和 composition api 对比

  • 逻辑组织和逻辑复用上,composition api 优于options api
  • composition api 几乎都是函数,会有更好的类型推断
  • composition api 对tree shaking更友好
  • composition api 中没有this的使用,减少了this指向不明的问题

5.tree shaking原理

Tree shaking是基于ES6模板语法(import与exports),主要是借助ES6模块的静态编译思想,在编译时就能确定模块的依赖关系,以及输入和输出的变量

Tree shaking无非就是做了两件事:

编译阶段利用ES6 Module判断哪些模块已经加载 判断那些模块和变量未被使用或者引用,进而删除对应代码

参考

vue3源码:github.com/vuejs/vue-n…

面试指南:github.com/febobo/web-…