Vue 3.0 新特性 预览

新特性总结

  • 全局API和内置组件/功能支持tree-shaking
  • 常驻代码大小控制在10kb gzipped上下
  • 编译器架构重构,更多的编译时优化
  • 添加TypeScript支持,使外部贡献者更有信心作改动
  • 代码采用monorepo结构,内部分层更清晰

1、使Vue更快(性能优化)

基于Proxy的响应式实现,性能总体优于Object.defineProperty

长远来看,JS引擎会继续优化Proxy,但Object.defineProperty(getter/setter)基本不会再有针对性的优化html

Virtual DOM 重构

2.x版本的VDOM更新
  • Vue虽然可以保证触发组件更新的最小化,但在单个组件内部依然须要遍历该组件的整个VDOM树
  • 但若是碰到下列只有少许动态节点时,这些遍历都是性能的浪费
  • 所以:传统的VDOM的性能与模板带下正相关,与动态节点数目无关
3.x 动静分离优化,提示更新效率

React 解决方法: 时间分片vue

  • 经过模板静态分享生成更优化的VDOM渲染函数node

  • 先将模板切分为block,每一个block内部动态节点位置都是固定的npm

  • 每一个block的根节点会记录本身所包含的动态节点(包含子block的根节点)编程

  • 更新时,只须要直接遍历动态节点api

  • block Tree 数组

  • 最简单状况 性能优化

  • v-if状况 架构

  • 优化效果app

    • 新策略将VDOM更新性能与模板大小解耦,变为与动态节点数目相关
    • 总体性能提高2~5倍

2、Function-based API

单个函数内部的变量会被压缩,对压缩更友好

替换Class API

  • class api(vue-property-derocator和vue-class-component) 对vue而已只是扩展了对TypeScript和与React相似的装饰器写法,其他并无其余的优势,
  • Derocator提案严重不稳定是的依赖他的方案具备很大风险
  • Props和其余须要注入到this的属性致使类型声明存在很大问题

逻辑复用

  • Mixin
    • 混入的属性来源不清楚
    • 命名空间冲突
  • 高阶组件(HOC)
    • Props来源不清楚
    • Props命名空间冲突
    • 多余的组件实例形成性能浪费
  • Scoped Slots
    • 来源清楚
    • 无命名空间冲突
    • 多余的组件形成性能浪费
  • Composition Functions (Function-based API提供)
    • 即函数组合
    • 无额外实例开销

优势

  • 更灵活的逻辑复用能力
  • 更好的TypeScript类型推导支持
  • 更好的性能
  • Tree-shaking优化
    • 基于函数的 API 每个函数均可以做为 named ES export 被单独引入,这使得它们对 tree-shaking 很是友好。没有被使用的 API 的相关代码能够在最终打包时被移除
  • 代码容易压缩
    • 全部的函数名和 setup 函数体内部的变量名均可以被压缩,但对象和 class 的属性/方法名却不能够

使用Function-based API

官方提供了一个vue-function-api依赖库,以便vue2.x也可使用function api编程,不过它只有Function-based API的setup、value、computed、watch、lifecycle、provide、inject等部分API

// npm install vue-function-api --save
// 在2.x中使用
import Vue from 'vue'
import { plugin } from 'vue-function-api'
Vue.use(plugin)
复制代码
setup函数

是Function-Based API的入口函数,相似data(),setup()的返回值就是注入页面模板的变量,能够像data同样直接使用

import { value } from 'vue'
function useMousePosition() { // 组件内容复用 Composition Function
const x = value(0) const y = value(0) const update = e => {
        x.value = e.pageX
       y.value = e.pageY
    }
    onMounted(() => {
       window.addEventListener('mousemove', update)
    })
    onUnmounted(() => {
       window.removeEventListener('mousemove', update)
    })
    return { x, y }
}
const MyComponent = {
 setup(props) {
   const msg = value('hello')
   const {x, y} = useMousePosition()
   const appendName = () => {
     msg.value = `hello ${props.name}`
   }
   return {
     msg,
     appendName
   }
 },
 template: `<div @click="appendName">{{ msg }}</div>`
}
复制代码
  • 函数API配合Render实现
    • 若是你的组件不使用模版,你也能够选择在 setup() 中直接返回一个渲染函数
import { value, createElement as h } from 'vue'
const MyComponent = {
  setup(initialProps) {
    const count = value(0)
    const increment = () => { count.value++ }

    return (props, slots, attrs, vnode) => (
      h('button', {
        onClick: increment
      }, count.value)
    )
  }
} 
复制代码
value()返回的是一个value wrapper(包装对象)
  • 一个包装对象只有一个属性.value,指向内部被包装的值,该值能够被修改

  • 为何要使用

    • 咱们知道在 JavaScript 中,原始值类型如 string 和 number 是只有值,没有引用的。若是在一个函数中返回一个字符串变量,接收到这个字符串的代码只会得到一个值,是没法追踪原始变量后续的变化的。
    • 所以,包装对象的意义就在于提供一个让咱们可以在函数之间以引用的方式传递任意类型值的容器
  • Value Unwrapping(包装对象的自动展开)

    • 当包装对象被暴露给模版渲染上下文,或是被嵌套在另外一个响应式对象中的时候,它会被自动展开 (unwrap) 为内部的值
const MyComponent = {
  setup() {
    return {
      count: value(0)
    }
  },
  template: `<button @click="count++">{{ count }}</button>`
}
复制代码
生命周期函数

全部现有的生命周期钩子都会有对应的 onXXX 函数(只能在 setup() 中使用):

import { onMounted, onUpdated, onUnmounted } from 'vue'
const MyComponent = {
  setup() {
    onMounted(() => {
      console.log('mounted!')
    })
    onUpdated(() => {
      console.log('updated!')
    })
    // destroyed 调整为 unmounted
    onUnmounted(() => {
      console.log('unmounted!')
    })
  }
}
复制代码
Computed Value (计算值)

计算值的行为跟计算属性 (computed property) 同样:只有当依赖变化的时候它才会被从新计算。 computed(getter, setter) 返回的是一个只读的包装对象,它能够和普通的包装对象同样在 setup() 中被返回 ,也同样会在渲染上下文中被自动展开。默认状况下,若是用户试图去修改一个只读包装对象,会触发警告

import { value, computed } from 'vue'
const count = value(0)
const countPlusOne = computed(() => count.value + 1)
console.log(countPlusOne.value) // 1
count.value++
console.log(countPlusOne.value) // 2
复制代码
Watchers
  • watch() API 提供了基于观察状态的变化来执行反作用的能力。
  • watch() 接收的第一个参数被称做 “数据源”,它能够是:
    • 一个返回任意值的函数
    • 一个包装对象
    • 一个包含上述两种数据源的数组
  • 第二个参数是回调函数。回调函数只有当数据源发生变更时才会被触发
  • 观察的对象能够是:Props,包装对象等
// double 是一个计算包装对象
const double = computed(() => count.value * 2)
watch(double, value => {
  console.log('double the count is: ', value)
}) // -> double the count is: 0
count.value++ // -> double the count is: 2
复制代码
  • 中止观察
const stop = watch(...)
// stop watching
stop()

// 若是 watch() 是在一个组件的 setup() 或是生命周期函数中被调用的,那么该 watcher 会在当前组件被销毁时也一同被自动中止:
export default {
  setup() {
    // 组件销毁时也会被自动中止
    watch(/* ... */)
  }
}
复制代码
纯 TypeScript 的 Props 类型声明

3.0 的 props 选项不是必须的,若是你不须要运行时的 props 类型检查,你也能够选择彻底在 TypeScript 的类型层面声明 props 的类型:

import { createComponent, createElement as h } from 'vue'
interface Props {
  msg: string
}
const MyComponent = createComponent({
  setup(props: Props) {
    return () => h('div', props.msg)
  }
})
复制代码
类型推导

createComponent 从概念上来讲和 2.x 的 Vue.extend 是同样的,但在 3.0 中它实际上是单纯为了类型推导而存在的,内部实现是个 noop(直接返回参数自己)。它的返回类型能够用于 TSX 和 Vetur 的模版自动补全。若是你使用单文件组件,则 Vetur 能够自动隐式地帮你添加这个调用

import { createComponent } from 'vue'
const MyComponent = createComponent({
  // props declarations are used to infer prop types
  props: {
    msg: String
  },
  setup(props) {
    props.msg // string | undefined

    // bindings returned from setup() can be used for type inference
    // in templates
    const count = value(0)
    return {
      count
    }
  }
})
复制代码

参考文献

相关文章
相关标签/搜索