Vue3.0 新特性探索

Vue3官网文档

1、Vue3.0 新变化

  • Proxy:不只是解决了 defineProperty 的局限性
  • Performance:性能比Vue 2.x快1.2~2倍
  • Tree shaking support:可以将无用模块“剪辑”,仅打包需要的,按需编译,体积比Vue2.x更小
  • Composition API: 组合API(类似React Hooks)
  • Better TypeScript support:更优秀的 Ts 支持
  • Custom Renderer API:暴露了自定义渲染API
  • Fragment, Teleport(Protal), Suspense:更先进的组件,“碎片”,Teleport 即 Protal 传送门,“悬念”。

2、Vue3.0是如何变快

2.1 双向绑定

2.0现有限制:

  • 无法检测到新的属性添加/删除
  • 无法监听数组的变化
  • 需要深度遍历,浪费内存

3.0优化:

  • 使用 ES6的Proxy 作为其观察者机制,取代之前使用的Object.defineProperty。Proxy默认可以支持数组
  • 允许框架拦截对象上的操作
  • 多层对象嵌套,使用懒代理

Object.defineProperty -> Proxy

Object.defineProperty是一个相对比较昂贵的操作,因为它直接操作对象的属性,颗粒度比较小。将它替换为es6的Proxy,在目标对象之上架了一层拦截,代理的是对象而不是对象的属性。这样可以将原本对对象属性的操作变为对整个对象的操作,颗粒度变大。

javascript引擎在解析的时候希望对象的结构越稳定越好,如果对象一直在变,可优化性降低,proxy不需要对原始对象做太多操作。

2.2 虚拟DOM

https://vue-next-template-explorer.netlify.app/
2.0 虚拟 DOM性能瓶颈:

  • 虽然vue能够保证触发更新的组件最小化,但单个组件部分变化需要遍历该组件的整个vdom树
  • 传统vdom性能跟模版大小正相关,跟动态节点的数量无关

3.0优化工作

  • 在 vue 3.0 中重新推翻后重写了虚拟 DOM 的实现,官方宣称渲染速度最快可以翻倍。更多编译时的优化以减少运行时的开销

diff算法优化

  • Vue2中的虚拟dom是进行全量的对比,即数据更新后在虚拟DOM中每个标签内容都会对比有没有发生变化

  • Vue3新增了静态标记(PatchFlag)

    • 在创建虚拟DOM的时候会根据DOM中的内容会不会发生变化添加静态标记

    • 数据更新后,只对比带有patch flag的节点

      我是一个标题

      {{msg}}

      // 虚拟DOM
      export function render(_ctx, _cache, $props, $setup, $data, $options) {
        return (_openBlock(), _createBlock("div", null, [
          _createVNode("p", null, "我是一个标题"), // 不标记
          _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */) // 标记
        ]))
      }
      
      
    • 并且可以通过flag的信息得知当前节点要对比的具体内容

      export const enum PatchFlags {
        TEXT = 1,// 动态文本节点
        CLASS = 1 << 1, // 2  // 动态 class
        STYLE = 1 << 2, // 4 // 动态 style
        PROPS = 1 << 3, // 8 // 动态属性,但不包含类名和样式
        FULL_PROPS = 1 << 4, // 16 // 具有动态 key 属性,当 key 改变时,需要进行完整的 diff 比较。
        HYDRATE_EVENTS = 1 << 5, // 32 // 带有监听事件的节点
        STABLE_FRAGMENT = 1 << 6, // 64 // 一个不会改变子节点顺序的 fragment
        KEYED_FRAGMENT = 1 << 7, // 128 // 带有 key 属性的 fragment 或部分子字节有 key
        UNKEYED_FRAGMENT = 1 << 8, // 256 // 子节点没有 key 的 fragment
        NEED_PATCH = 1 << 9, // 512 // 一个节点只会进行非 props 比较
        DYNAMIC_SLOTS = 1 << 10, // 1024 // 动态 slot
        HOISTED = -1, // 静态节点
        // 指示在 diff 过程应该要退出优化模式
        BAIL = -2
      }
      
      

2.2 hoistStatic 静态提升

  • Vue2中无论元素是否参与更新, 每次都会重新创建, 然后再渲染
  • Vue3中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可

我是一个标题

{{msg}}

// 静态提升之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("p", null, "我是一个标题"), // 每次都会创建一个虚拟节点
    _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */) 
  ]))
}

// 静态提升之后
const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "我是一个标题", -1 /* HOISTED */) // 定义了一个全局变量,只会创建一次

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

2.3 事件监听缓存

默认情况下onClick会被视为动态绑定, 所以每次都会去追踪它的变化,但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可

// 开启事件监听缓存之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", { onClick: _ctx.onClick }, "按钮", 8 /* PROPS */, ["onClick"])
  ])) // 有静态标记 
}

// 开启事件监听缓存之后
export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("div", null, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))
    }, "按钮")
  ])) // 没有静态标记
}

值得注意的新API

1.创建vue3.0项目的方法

(1)使用vite
npm init vite-app 项目名
cd 项目名
npm run dev

使用这种方式的优缺点:
① 创建速度快,不通过webpack编译
② 还需要自己安装vue-router、vuex (vue-router以及vuex都要相应的升级为4.0版本)

vite是什么?

vite是基于vue3单文件组件的非打包开发服务器
vite 是一个基于 Vue3 单文件组件的非打包开发服务器,vite具有以下的优点:
可以快速的冷启动,不需要等待打包;
即时的热模块更新;
不用等待整个项目编译完成。

原理:ES module/.vue文件拆分为http请求+type?=参数/热更新(koa/websocket/watcher)
vite使用ES module,代码以模块的方式引入到文件;即在浏览器使用import/export方式导入/导出,webpack使用require方式导入。
vite使用koa构建的服务端,webpack使用webpack-dev-server构建服务端。

(2)利用vue-cli
npm install -g @vue/cli
vue create 项目名
cd 项目名
vue add vue-next //重点 执行这行,会把2.0代码改为3.0的, vue-router, vuex会变成4.0的
npm run serve

介绍完安装vue3.0,接下来,咱们就正式进入咱们今天的重点了~

vue3.0的主要变化

响应式基本原理:Object.defineProperty -> Proxy,提高性能
初始化方式:Options api -> composition api,提供代码复用,更好的tree-shaking
初始化项目:vue-cli -> vite,提高开发效率
扩展方法:Vue.property.xxx -> config.globalProperties.xxx,更好的tree-shaking
实例化:new Vue -> createApp。
原来的写法,如果一个项目有多个Vue实例,那么多个实例造成污染:

Vue.use(plugin)
new Vue({el:'app1'});
new Vue({el:'app2'});

而createApp这种方式,每次创建一个vue实例,然后use不同插件

app1 = createApp({});
app2 = createApp({});
app1.use(plugin1)
app2.use(plugin2)

setup内部不支持this,因为setup时组件实例还没有创建,通过setup的第二个参数context获取实例
支持ts -> ts的优点:类型检查/编译器提示/ts拥抱ES6规范及部分起草规范

2.相比于vue2.0,vue3.0的新变化

1、main.js,新增createApp方法

// vue2.0
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './App.scss'
 
new Vue({
   el: '#app',
   router,
   store,
   template: '',
   components: { App }
 })
 
 
 
// vue3.0
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './App.scss'
 
const app = createApp(App)
app.use(router).use(store).mount('#app');

注意: 由于vue3.0 使用的是 import {createApp} from 'vue' 而不是像vue2.0的import Vue from 'vue'。因此之前使用的ui框架以及第三方包可能会因为不支持vue3.0而报错。

2、template模板(Fragment)

vue2.0里template只支持单一根节点,在vue3.0里可以使用多个根节点


3、组合式API(Composition API)

composition api是什么?

组合api:将组件属性公开为函数api
data -> reactive()/ref()
computed -> computed():创建计算属性,返回的是ref实例
watch -> watch()
provide/inject -> provide()/inject()
lifeCicle -> on+xxx

options api的优点:保证代码的下限,在指定的地方写指定的代码
options api的缺点:同一个逻辑代码比较分散

composition api的优点:自由度高,复用性提高
composition api的缺点:对编码人员的要求比较高

composition API的设计动机

组合API实战教程

  1. 逻辑复用及代码整理
    vue2.x中代码复用是通过mixin提取公共逻辑和通过作用域插槽编写复用性组件
    mixin的缺点:名称一样时会被覆盖
    作用域插槽的缺点:只能在模板中使用
    vue3.0中代码复用通过composition API。对于业务逻辑相同的代码,可以抽取到一个js文件,然后导入到不同到组件
    composition的缺点:自由度高,需要编码人员有比较高的抽象能力
  2. 更好的typescript支持

使用传统的option配置方法写组件的时候问题,随着业务复杂度越来越高,代码量会不断的加大;由于相关业务的代码需要遵循option的配置写到特定的区域,导致后续维护非常的复杂,同时代码可复用性不高,而composition-api就是为了解决这个问题而生的。

在vue2.0里我们都把.vue文件里的js部分叫做Options API, 而在3.0里变为Composition API。

注:Composition API 里有vue3.0的新增的主要新特性:

(1)一个全新的属性 setup ,这是一个组件的入口,让我们可以运用 Vue3.0 暴露的新接口,它运行在组件被实例化时候,props 属性被定义之后,实际上等价于 Vue2.0 版本的 beforeCreate 和 Created 这两个生命周期,setup 返回的是一个对象,里面的所有被返回的属性值,都会被合并到 Vue2.0 的 render 渲染函数里面,在单文件组件中,它将配合 模板的内容,完成Model到View之间的绑定,在未来版本中应该还会支持返回 JSX 代码片段。

  • 组合API的入口函数, 在 beforeCreate 之后、created 之前执行 ,主要是实现数据和业务逻辑不分离
  • 无法使用data和methods,所以为了避免错误使用,直接将内部的this改成了undefined
  • 内部的方法只能是同步,不能是异步

(2)setup函数的第一个参数 props 是父组件传进来的值,在 Vue2.0 中我们可以使用 props 属性值完成父子通信,在这里我们需要定义 props 属性去定义接受值的类型,然后我们可以利用 setup 的第一个参数获取 props 使用。

(3) setup 函数的第二个参数context是一个上下文对象,这个上下文对象中包含了一些有用的属性,这些属性在 Vue2.0 中需要通过 this 才能访问到,在 vue3.0 中,访问他们变成以下形式:

context.parent--> this.$parent

context.root--> this

context.emit-->this.$emit

context.refs-->this.$refs

context.slots --> this.$slots
程序执行setup时,组件尚未被创建,因此能访问到的有用属性有: root、parent、refs、attrs、listeners、isServer、ssrContext、emit 于此同时 data、computed、methods等是访问不到的.

(4)setup()不单可以return 对象,还可以返回方法。

(5)利用watchEffect可以监听props。

// 父组件


 
// 子组件 Child