Vue3.0

511 阅读4分钟

1.Vue3.0设计目标

  • 更小
  • 更快
  • 加强TS支持
  • 加强AP设计一致性
  • 提高自身可维护性
  • 开放更多底层功能

2.如何更快?

  • Object.defineProperty->Proxy
  • Virtual Dom重构
  • 更多编译优化
    • slot默认编译函数
    • Momomorphic vnode factory
    • Compiler-geenerated flags for vnode.children types

Virtual DOM重构

传统Virtual DOM的性能瓶颈
  • 数据变更之后,新的Virtual DOM和旧的Virtual DOM进行patch算法比较,并算出二者之间的差异,将差异进行修.但是传统Virtual DOM,进行算法比对时颗粒度是组件,每个组件作为一个颗粒.虽然Vue能够保证触发更新的组件最小化,但是单个组件内部依然需要遍历该组件的整个Virtual DOM树.

  • 传统Virtual DOM的性能跟模版大小正相关,跟动态节点的数量无关.模版或者组件有多大,那么在进行数据更新时损耗的性能就有多大,但实际上,这种方式利用率很低.比如发生改变的地方只有message插值的部分,整体结构不变,但是数据更新的时候,比对整个template结构,这样就存在性能损耗.

  • 所以在一些组件整个模版内只有少量动态节点的情况下,传统方法遍历存在性能的浪费.

  • JSX和手写的render function是完全动态的,过度的灵活性导致运行时可以用于优化的信息不足.

Vue3.0Virtual DOM

动静结合:每次触发更新不再以组件为单位进行遍历,找到动态变化的部分,更新时只比对可以变化的部分,减少性能损耗.将vdom更新性能由与模版整体大小相关提升为与动态内容的数量相关.

三种类型

最简单的情况

  • 节点结构完全不会改变
  • 只有一个动态节点
<template>
    <div id="content">
        <p class="text">A</p>
        <p class="text">B</p>
        <p class="text">{{ message }}</p>  Diff <p> textCintent
        <p class="text">C</p> 
    </div>
</template>

这个时候没有必要比较结构顺序

节点结构变化v-if

  • v-if外部: 只有v-if是动态节点
  • v-if内部: 只有{{ message }}是动态节点
<template>
<div>  - Check <p v-if="ok">
        - Diff <span> textContent
    <p class="text">A</p>
    <p v-if="ok">
        <span>B</span>
        <span>{{ message }} </span>
    </p>
</div>
</template

把模版切分成两部分,各部分相对静态.

节点结构变化v-for

  • v-for外部: 只有v-for是动态节点(fragment)
  • 每个v-for循环内部:只有{{ item.message}}是动态节点.
<template>
<div> - Diff <p v-for> children
      - Diff <span> textContent
      - Repeat n times
    <p class="text">A</p>
    <p v-for="item of list">
        <span>B</span>
        <span>{{ item.message }}</span>
    </p>
</div>
</template>

区块树Block tree

  • 将模版基于动态节点指令切割为嵌套的区块
  • 每个区块内部的节点结构是固定的
  • 每个区块只需要以一个Array追踪自身包含的动态节点

3.Composition API

  • 1.逻辑组合和复用
  • 2.类型推导:使用TS重构.基于函数的API对类型推导很友好.
  • 3.打包尺寸:每个函数都可作为named ES export被单独引入,对tree-shaking很友好;其次所有函数名和setup函数内部的变量都能被压缩,所以能有更好的压缩效率.

vue2.x对逻辑复用方案

  • Mixins和HOC都存在模版数据来源不清晰的问题.
  • 并且mixin的属性,方法的命名以及HOC的props注入也可能会产生命名空间冲突的问题.
  • 最后,由于HOC和Renderless Components都需要额外的组件实例来做逻辑封装,会导致,无谓的性能开销.

setup()函数

i.初始化时机

setup()是在组件实例被创建时,初始化了props之后调用,处于created前.这个时候我们能接收初始props作为参数.

import { Comonent, Vue, Prop } from 'vue-property-decorator';

@Component({
    setup(props) {
        console.log('setup', props.test)
        return {}
    }
})

export default class Hooks extends Vue {
    @Prop({  default: 'hello' })
    test: string
    
    beforeCreate () {
        console.log('beforeCreate')
    }
    
    created () {
        cosnole.log('created')
    }
}
// 打印顺序 beforeCreate->setup hello->created

setup()返回一个对象,而这个对象上的属性则会直接暴露给模版渲染上下文.

ii.reactivity api

走的是value增强的路线,它要做的是如何从一个响应式的值中,衍生出普通的值以及view

在setup()内部,Vue则为我们提供了一系列响应式的API,比如ref,它返回一个Ref包装对象,并在view层引用的时候自动展开.

而我们通过计算产生的值,即使不进行类型声明,也能直接拿到其类型进行推导,因为它是依耐Ref进行计算的.

props类型推导

关于Props类型推导,在TS中,必须在createComponent函数来定义组件.

import { createComponent } from '@vue/composition-api';

const MyComponent = createComponent({
    props: {
        msg: String
    },
    setup(props) {
        props.msg
        return {}
    }
})

props选项并不是必须的,假如你不需要运行时的props类型检查,你可以直接在TS类型层面进行声明.

import { createComponet } from '@vue/composition-api';

interface Props {
    msg: string
}

export default createComponent({
    props: ['msg'],
    setup(props: Props, { root }) {
        const { $createElement: h } = root;
        return () => h('div', props.msg)
    }
})