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的节点

      <div>
        <p>我是一个标题</p>
        <p>{{msg}}</p> <!-- 双向绑定 -->
      </div>
      
      
      // 虚拟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中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可
<div>
  <p>我是一个标题</p>
  <p>{{msg}}</p>
</div>

// 静态提升之前
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会被视为动态绑定, 所以每次都会去追踪它的变化,但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可

<div>
  <button @click="onClick">按钮</button>
</div>

// 开启事件监听缓存之前
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: '<App/>',
   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里可以使用多个根节点

<template>
    <!-- vue3.0组件的根节点可以有多个,或者使用<Fragment> 空标签 -->
    <div class="login"></div>
    <div class="main"></div>
    <div></div>
</template>

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。

// 父组件
<template>
    <Child val="测试"/>
</template>
<script>
    import {setup} from 'vue';
    import Child from './Child';
    export default {
        name: 'Parent',
        components: {
            Child
        },
        setup(props, ctx) {
            console.log(ctx); // 在setup()中无法访问到this
            console.log(this); // undefined
        }
    }
</script>
 
// 子组件 Child
<template></tempalte>
<script>
    import {setup, watchEffect} from 'vue';
    export default {
        name: 'Child',
        // props: ['val'], // 跟vue2.0相同 也可以采用对象形式
        props: {
            val: String
        },
        setup(props, context) {
            console.log(props); // 在这里可以获取父组件传过来的属性值 
            watchEffect(() => { // 利用watchEffect监听props 
                console.log(props.val); // 首次以及props改变才会执行这里的代码
            })
            return { // setup 返回的对象/方法  可以在模板中被访问得到
            }
        }
    }
    </script>

vue2.0 里数据都是在data里定义, 而在vue3.0可以使用reactive, ref

state更名为reactive
reactive等价于 Vue 2.x 的Vue.observable(),用于获取一个对象的响应性代理对象
const obj = reactive({ count: 0 });

value更名为ref,并提供isRef和toRefs
const unwrapped = isRef(foo) ? foo.value : foo;

reactive 函数
  • 可以监听复杂类型(json/Array)的变化
  • 实现响应式数据的方法,Vue2中通过Object.defineProperty实现的,Vue3通过ES6的Proxy实现
  • 注意点:如果给 reactive 传递了其他对象,即不是 json/Array
    • 默认情况下修改对象界面不会自动更新
    • 如果要更新,可以通过重新赋值的方式
reactive 传递基本数据类型
import { reactive } from 'vue'

setup() {
  let state = reactive(123) // 传递基本数据类型
  function fn() {
    state = 666 // 界面不会更新,不是 Proxy 对象
    console.log(state) // 666
  }
}

import { reactive } from 'vue'

setup() {
  let state = reactive({age: 13}) 
  function fn() {
    state.age = 20 // 界面更新
    console.log(state) // Proxy{age: 20}
  }
}

reactive 传递其他对象
import { reactive } from 'vue'

setup() {
  let state = reactive({time: new Date()}) // 其他对象
  function fn() {
    state.time.setDate(state.time.getDate() + 1) // 界面不会更新
    console.log(state) // Proxy{...}
  }
}

import { reactive } from 'vue'

setup() {
  let state = reactive({time: new Date()}) // 其他对象
  function fn() {
    const newTime = new Date(state.time.getTime())
    newTime.setDate(state.time.getDate() + 1)
    state.time = newTime // 界面更新
    console.log(state) // Proxy{...}
  }
}

3.1.2 ref 函数
  • 本质还是 reactive ,当给 ref 函数传递一个值后,底层会自动转成 reactive,所以一般都是使用 ref 函数创建响应式数据

    • ref(10) -> reactive({value: 18})
    • 在 setup 函数内部修改 ref 的值时必须通过 .value 的方式
    import { ref } from 'vue'
    
    setup() {
      let state = ref(10)
      function fn() {
        state.value = 20
        console.log(state) // RefImp{...}
      }
    }
    
    
    • 在 template 中使用不用添加 .value,Vue内部已经进行了转换
3.1.3 reactive 和 ref 的区别

Vue在解析数据之前,会通过当前数据的__v_ref这个私有属性判断这个数据是否是ref类型,如果值为true就是一个ref类型的数据

  • 在template里使用的是ref类型的数据,Vue会自动添加.value
  • 在template里使用的是reactive类型的数据,Vue不会自动添加.value

注意: compisition-api 的本质也是将 return 出去的值注入到 option-api 中

return {state, remStu}

// 等同
data() {
  return {
    state: ''
  }
},
methods: {
  remStu() {}
}

3.1.4 markRaw 函数

数据永远不会被追踪

setup() {
  let obj = {name: 'zs', age: 18}
  obj = markRaw(obj) // 此时无论怎么修改都不会更新数据
  let state = reactive(obj) 
}

3.1.5 toRef 函数

如果利用ref将某一个对象中的属性变成响应式的数据,修改响应式的数据是不会影响到原始数据的

setup() {
  let obj = {name: 'zs'}
  let state = ref(obj.name)
  function fn() {
    state.value = 'ls'
    console.log(obj) // {name: 'zs'}
    console.log(state.value) // ls
  }
}

利用 toRef 将一个对象的某一个属性变成响应式的数据,修改响应式数据会影响原始数据,但不会更新视图,只是引用了原始数据

setup() {
  let obj = {name: 'zs'}
  let state = toRef(obj, 'name')
  function fn() {
    state.value = 'ls'
    console.log(obj) // {name: 'ls'}
    console.log(state.value) // ls
  }
}

应用场景:

如果想让响应式数据和原始数据关联起来,并且修改数据之后不想更新UI界面,那么就可以使用

3.1.6 toRefs 函数

将对象的全部属性变成响应式数据,修改后不更新视图

setup() {
  let obj = {name: 'zs', age: 18}
  let state = toRefs(obj)
  function fn() {
    state.name.value = 'ls'
    state.age.value = 20
    console.log(obj) // {name: 'ls', age: 20}
    console.log(state.name.value) // ls
  }
}

3.1.7 toRaw 函数

ref 和 reactive 数据类型每次修改都会被追踪,都会更新UI界面,这样会非常消耗性能,有时候一些数据不需要追踪就可以通过toRaw方法拿到它的原始数据,对原始数据进行修改就不会被追踪

setup() {
  let obj = {name: 'zs', age: 18}
  let state = reactive(obj) // 包装后的对象
  let obj2 = toRaw(state)
  /* 
  * let state = ref(obj)
  * let obj2 = toRaw(state.value)
  */
  console.log(obj === obj2) // true
  /* 
  * state 和 obj 的关系:
  * state 本质是一个 Proxy 对象,只是引用了 obj
  */
  console.log(obj === state) // false

  // 修改原始数据
  function fn() {
    obj2.name = 'ls' // 这里只是在内存里发生了改变,并不会更新视图
    // obj.name = 'ls' 这样写才会更新视图
  }
}

data 函数

在 rfcs 中就有提到 data() 将不在支持作为一个对象,只能作为一个 function 返回一个对象。虽然在 Vue 2.x 中可以返回一个状态进行状态共享,但是这势必会引发一些问题,而且如果要实现这种状态共享,function 完全可以替代,通过 function 返回对象,v-bind 给子组件就可以实现状态共享,使用 object 会对逻辑造成紊乱,并且需要示例去说明,更加重了学习者的心智负担!所以在 Vue 3.0 中不再支持 object 方式,强行使用编译不会通过,并且给出警告:[Vue warn]: data() should return an object.

watch函数

  import { reactive, watch } from "vue";
    import store from "../stores";
    export default {
      setup() {
        const state = reactive({
          searchValue: '',
        });
        // 监听搜索框的值
        watch(
          () => {
            return state.searchValue;
          },
          () => {
            // 存储输入框到状态 store 中心,用于组件通信
            store.setSearchValue(state.searchValue);
          }
        );
        return {
          ...toRefs(state)
        };
      }
    };

computed

跟 Vue2.0 的使用方式很相近,同样需要按需导入该模块 , 计算属性分为两种,只读 以及可读可写

<tempalte>
    <div>{{addCount}}</div>
    <div>{{addCount2}}</div>
</tempalte>
 
<script>
    import {setup, reactive, computed} from 'vue';
    export default {
        setup(props, context) {
            const count = ref(0);
            const addCount = coomputed(() => count.value + 1); // 只读
            const addCount2 = computed(() => {
                get: () => count.value + 1,  // 取值函数
                set: (value) => count.value = value; // 赋值函数
            })
            return {
                count,
                addCount,
                addCount2
            }
        }
    }
</script>

生命周期lifecycle hooks

① vue2.0的生命周期的钩子函数有: beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed

② vue3.0的生命周期在vue2.0的基础上做了些修改, 新版本的都是以onXxx()函数注册使用,其中 beforeCreate、created 这两个函数可以由setup()代替

    import { onMounted, onUpdated, onUnmounted } from "@vue/composition-api";//引入钩子
    export default {
      setup(props, ctx) {
         // `props` 属性被定义之后,实际上等价于 `Vue2.0` 版本的 `beforeCreate` 和 `Created` 这两个生命周期
        const loadMore = () => {};
        onMounted(() => {
          loadMore();
        });
        onUpdated(() => {
          console.log('updated!')
        })
        onUnmounted(() => {
          console.log('unmounted!')
        })
        return {
          loadMore
        };
      }
    };

新旧版本生命周期对比 :
Options API Hook inside inside setup
beforeCreate Not needed*
created Not needed*

beforeCreate --> use setup()

created --> use setup()

beforeMount --> onBeforeMount

mounted --> onMounted

beforeUpdate --> onBeforeUpdate

updated --> onUpdated

beforeDestory --> onBeforeUnmount

destoryed --> onUnmounted

errorCaptured --> onErrorCaptured

同时vue3.0 还提供了2个全新的生命周期帮助我们去调试代码:

onRenderTracked

onRenderTriggered

3.2 递归监听

默认情况下,ref和reactive创建的数据都是递归监听,即无论里面套多少层都可以监听得到

const state = ref({
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
})

ref.b.c.value = 3 // 可以监听,且会更新界面

缺点:当数据量比较大时,会非常消耗性能,因为每一层都是一个Proxy对象

3.3 非递归监听

只能监听第一层,不能监听其它层,只有第一层数据改变了,其它层才会被监听,可以通过 shallowReactive、shallRef 创建非递归监听数据

3.3.1 shallowReactive
import { shallowReactive } from 'vue'
const state = shallowReactive({
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
})

// 这样可以被监听
state.a = 'a'  /* 如果第一层不变,则视图不会更新 */
state.b.c = 'c'
state.b.d.e = 'e'

3.3.2 shallowRef

本质是 shallowReactive ,当给 shallowRef 函数传递一个值后,底层会自动转成 shallowReactive,所以 shallowRef 的第一层是.value

import { shallowRef } from 'vue'
const state = shallowRef({
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
})

// shallowRef 的第一层是 state.value,只有state.value 发生变化才会更新视图
state.value = {
  a: 2,
  b: {
    c: 3,
    d: {
      e: 4
    }
  }
}

3.3.2 triggerRef

自动触发一次shallowRef的数据更新,没有triggerReactive函数

import { shallowRef, triggerRef } from 'vue'
const state = shallowRef({
  a: 1,
  b: {
    c: 2,
    d: {
      e: 3
    }
  }
})

state.b.d.e = 'e'
triggerRef(state) // 此时视图会更新

3.1.1 删除数据
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let { state, remStu } = useRemoveStudent()
    return { state, remStu }
  }
}
function useRemoveStudent() {
  let state = reactive({
    stus:[
      {id:1, name:'zs', age:10},
      {id:2, name:'ls', age:20},
      {id:3, name:'ww', age:30}
    ]
  })
  function remStu(index) {
    state.stus = state.stus.filter((stu, idx) => idx !== index)
  }
  return {state, remStu}
}

<ul>
  <li v-for="(stu, index) in state.stus"
      :key="stu.id"
      @click="remStu(index)">
    {{stu.name}} - {{stu.age}}
  </li>
</ul>

3.1.2 新增数据
import { reactive } from 'vue'
export default {
  name: 'App',
  setup() {
    let {state, remStu} = useRemoveStudent();
    let {state2, addStu} = useAddStudent(state);
    return {state, remStu, state2, addStu}
  }
}

function useAddStudent(state) {
  let state2 = reactive({
    stu:{
      id:'',
      name:'',
      age:''
    }
  })
  function addStu() {
    const stu = Object.assign({}, state2.stu)
    state.stus.push(stu)
    state2.stu.id = ''
    state2.stu.name = ''
    state2.stu.age = ''
  }
  return {state2, addStu}
}

<form>
  <input type="text" v-model="state2.stu.id">
  <input type="text" v-model="state2.stu.name">
  <input type="text" v-model="state2.stu.age">
  <input type="submit" @click.prevent="addStu">
</form>

Tree-shaking Global API

Tree-shaking的本质是消除无用的js代码。无用代码消除在广泛存在于传统的编程语言编译器中,编译器可以判断出某些代码根本不影响输出,然后消除这些代码,这个称之为DCE(dead code elimination)。
Tree-shaking 是 DCE 的一种新的实现,Javascript同传统的编程语言不同的是,javascript绝大多数情况需要通过网络进行加载,然后执行,加载的文件大小越小,整体执行时间更短,所以去除无用代码以减少文件体积,对javascript来说更有意义。
Tree-shaking 和传统的 DCE的方法又不太一样,传统的DCE 消灭不可能执行的代码,而Tree-shaking 更关注宇消除没有用到的代码。

在 Vue2.x 的版本中,在 Vue.prototype 定义了全局 API,如下:这会导致 Vue 库的整体体积较大,部署生产时即使未用到的 API ,也会被打包。

import Vue from 'vue'

Vue.nextTick(() => {})

const obj = Vue.observable({})

Vue 3.0 在平衡功能和包体积大小做了一定的努力,力图在 Vue 包做到更新并且不限制其功能。Vue 3.0 中使用了 组合式 API,通过 ES Modules 的静态分析的设计,与现代的 捆绑器 webpack 和 rollup 相结合,Tree-shaking 中剔除了那些未在项目使用却导出 ES Module API,如此,用户只会使用到那些 import 的 API.

需要注意的是: 在使用模块打包器构建 ES Module 包时可以 Tree-shaking,在打包 UMD 构建包时还是会全局打包 API 至 Vue 全局变量中.

teleport 传送

<teleport> 组件的实现动机时,解决了组件树的一个弱点, 能够将组件中的模板迁移带 DOM 的其他位置,在没有我们的DOM树的情况下,将其从深层嵌套的位置中分解出来。

example:

<body>
  <div id="app">
    <h1>Move the #content with the portal component</h1>
    <teleport to="#endofbody">
      <div id="content">
        <p>
          this will be moved to #endofbody.<br />
          Pretend that it's a modal
        </p>
        <Child />
      </div>
    </teleport>
  </div>
  <div id="endofbody"></div>
  <script>
    new Vue({
      el: "#app",
      components: {
        Child: { template: "<div>Placeholder</div>" }
      }
    });
  </script>
</body>

result:

<div id="app">
  <!-- -->
</div>
<div id="endofbody">
  <div id="content">
    <p>
      this will be moved to #endofbody.<br />
      Pretend that it's a modal
    </p>
    <div>Placeholder</div>
  </div>
</div>

渲染函数

地址

const app = Vue.createApp({})

app.component('anchored-heading', {
  render() {
    const { h } = Vue

    return h(
      'h' + this.level, // tag name
      {}, // props/attributes
      this.$slots.default() // array of children
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})

迁移升级v3 地址

注意:vue3.0兼容vue2.0 vue版本升级之后不需要更改关于vue部分的代码,但是项目中使用的相关的插件就不一定了~

酱酱酱~目前vue3.0的新特性就这些,后期有更新的话,继续补充

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容