【进阶】向VUE3.0进击!快速掌握VUE3新版本姿势重点!

hero

One Piece介绍

2020年9月19日凌晨,尤雨溪大大正式发布了 Vue.js 3.0 版本,代号:One Piece。此框架新的主要版本提供了更好的性能、更小的捆绑包体积、更好的 TypeScript 集成、用于处理大规模用例的新 API,并为框架未来的长期迭代奠定了坚实的基础。

Vue3.0六大亮点:

1,性能比Vue2.X版本快1.2 ~ 2倍

2,按需加载,按需编译,体积比Vue2.X版本更小

3,新增了组合API(类似于React Hooks)【最大亮点】

4,更好的TS语法支持(新增Fragment、Teleport、Suspense)

5,暴露了自定义的渲染API

6,提供了更先进的组件定义

回顾下Vue2.0的特点:

  • 轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十kb;
  • 简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;
  • 双向数据绑定:保留了angular的特点,在数据操作方面更为简单;
  • 组件化:保留了react的优点,实现了html的封装和重用,在构建单页面应用方面有着独特的优势;
  • 视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;
  • 虚拟DOM:dom操作是非常耗费性能的, 不再使用原生的dom操作节点,极大解放dom操作,但具体操作的还是dom不过是换了另一种方式;
  • 运行速度更快: 相比较与react而言,同样是操作虚拟dom,就性能而言,vue存在很大的优势。

新版本是如何变快的?

diff方法的优化:

  • Vue2.0的虚拟DOM是进行全量的对比
  • Vue3.0新增加了静态标记 (在与上次虚拟节点进行对比时候,只对比带有patch flag的节点,并且可以通过flag的信息得知当前节点要对比的具体内容)=> 减少了比较次数,性能提升。
 <template>
  <div class="about">
    <h1>This is an about page</h1>
    <p>{{msg}}</p>
  </div>
</template>
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("template", null, [
    _createVNode("div", { class: "about" }, [
      _createVNode("h1", null, "This is an about page"),
      _createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
    ])
  ]))
}

hoistStatic 静态提升:

  • Vue2.0中无论元素是否参与更新,每次都会重新创建
  • Vue3.0中不参与更新的元素,只会被创建一次,之后每次渲染时候被不停的复用
 <template>
  <div class="about-page">
    <h1 id='hello'>This is an about 111</h1>
    <p>{{msg}}</p>
    <button @click='myFn'></button> 
  </div>
</template>
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

const _hoisted_1 = { class: "about-page" }
const _hoisted_2 = /*#__PURE__*/_createVNode("h1", { id: "hello" }, "This is an about 111", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("template", null, [
    _createVNode("div", _hoisted_1, [
      _hoisted_2,
      _createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
      _createVNode("button", { onClick: _ctx.myFn }, null, 8 /* PROPS */, ["onClick"])
    ])
  ]))
}

cacheHandlers 事件侦听器缓存

  • 默认情况下,onClick会被视为动态绑定,所以每次都会去追踪它的变化
  • 但因为是同一个函数,所以Vue3.0没有追踪变化,缓存起来直接使用即可
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

const _hoisted_1 = { class: "about-page" }
const _hoisted_2 = /*#__PURE__*/_createVNode("h1", { id: "hello" }, "This is an about 111", -1 /* HOISTED */)

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createBlock("template", null, [
    _createVNode("div", _hoisted_1, [
      _hoisted_2,
      _createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
      _createVNode("button", {
        onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.myFn && _ctx.myFn(...args)))
      })
    ])
  ]))
}

SSR渲染

  • 当有大量静态内容时,这些内容会被当做纯字符串推进一个buffer里面

    ,即使存在动态(属性、函数和值)的绑定,会通过模板插值嵌入进去。这样会比通过虚拟DOM的渲染快上很多很多

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

 <template>
  <div class="about-page">
    <h1 class='hello'>This is an about 111</h1>
    <h1 class='hello'>This is an about 111</h1>
    <h1 class='hello'>This is an about 111</h1>
    <h1 class='hello'>This is an about 111</h1>
    <h1 class='hello'>This is an about 111</h1>
    <h1 class='hello'>This is an about 111</h1>
    <p>{{msg}}</p>
    <button @click='myFn'></button> 
  </div>
</template>
import { mergeProps as _mergeProps } from "vue"
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "@vue/server-renderer"

export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {
  const _cssVars = { style: { color: _ctx.color }}
  _push(`<template${
    _ssrRenderAttrs(_mergeProps(_attrs, _cssVars))
  }><div class="about-page"><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><p>${
    _ssrInterpolate(_ctx.msg)
  }</p><button></button></div></template>`)
}

compiler原理

  • 静态Node不再做更新处理
  • 静态绑定的class/id不再做更新处理
  • 结合打包标记PatchFlag, 进行更新分析(动态绑定)
  • 事件监听器Cache缓存处理(cacheHandlers)
  • hoistStatic自动针对多静态节点进行优化,输出字符串

Vue3.0 快速上手步骤

  • 创建Vue3的三种方式:
    1. Vue-cli
    2. Webpack
    3. Vite
  • 什么是Vite?
    1. Vite是vue作者开发的一款意图取代webpack的工具
    2. 其实现原理是利用ES6的import会发送请求去加载文件的特性
    3. 拦截这些请求,做了一些预编译,省去了webpack冗长的打包时间(大大提升编译速度)
    4. 可以在单文件中书写ES6语法
    5. 支持热更新(请求的内容才会被打包、更新)
    6. RollUp打包模式
  • 使用Vite创建项目
    1. 安装Vite:npm install -g create-vite-app
    2. 利用Vite创建Vue3项目:create-vite-app [your_name] / npm init vite-app [your_name]
    3. 安装依赖运行项目
       cd [your_name]
       npm install
       npm run dev
    

如何兼容使用Vue3.0, 有哪些需要注意的点?

  1. 生命周期钩子函数的变化:

    • 与 2.x 版本生命周期相对应的组合式 API

      • beforeCreate -> 使用 setup()
      • created -> 使用 setup()
      • beforeMount -> onBeforeMount
      • mounted -> onMounted
      • beforeUpdate -> onBeforeUpdate
      • updated -> onUpdated
      • beforeDestroy -> onBeforeUnmount
      • destroyed -> onUnmounted
      • errorCaptured -> onErrorCaptured
    • 新增的钩子函数

      除了和 2.x 生命周期等效项之外,组合式 API 还提供了以下调试钩子函数:

      • onRenderTracked
      • onRenderTriggered
  2. 破坏性的变化\新增的变化

    • vue3中template支持多个根标签。
    <!-- 得益于 `vue3.0` 的特性,我们现在不比把组件内容全部包裹在某一个 `div` 下面了,一个 `template` 里面可以有多个根节点元素,是没有关系的。-->
    <template>
        <p>{{msg}}</p>
        <button @click='myFn'></button> 
    </template>
    
    • 异步组件的引用
    // before
    const asyncPage = () => import('./NextPage.vue')
    // after
    import { defineAsyncComponent } from 'vue'
    const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))
    
    • main.js 入口文件的变化
    //vue2
    import Vue from 'vue'
    import App from './App.vue'
    
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
    }).$mount('#app')
    
    //vue3
    import { createApp } from 'vue'
    import App from './App.vue'
    import './index.css'
    
    createApp(App).mount('#app')
    
    • setup(取代data methods) ref

    setup函数返回一个对象,这个对象中包含方法和数据,生命周期钩子函数也在setup中运行,取代的是vue2中的data,methods。
    ref类型的数据,是一种响应式的数据,待续

    // Vue2
    export default{
      props:{
        title: String
      },
      data() {
        return {
          count: 0
        },
      methods:{
        add(){
          return this.count++
        }
      }
    }
    }
    // Vue3
    export default {
      props: {
        title: String
      },
      setup(props,context) {
       const count = ref(0)
       add(){
          return count.value ++
        }
      return {count,add}
      }
    }
    
  1. 移除了的特性

    • 移除keyCode 作为v-on修饰符
    • $on,$off and $once 移除
    • fliters移除
    • Inline templates attributes 移除

新版本的不完全升级指南

  1. 90%以上的代码可与Vue2.0的复用
  2. Composition API作为新增的API不会影响旧的逻辑代码
  3. Mixins不再推荐,filters已经被遗弃
  4. TS语法的更多应用
  5. Vue3有专用的迁移版本,对Vue2进行兼容

新老版本双向数据绑定的原理

  1. 在Vue2.x中是通过defineProperty来实现响应式数据的

    原理:vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变;
    核心:关于VUE双向数据绑定,其核心是 Object.defineProperty()方法

  2. 在Vue3.x中是通过proxy来实现响应式数据的

    let obj = { name: "lorretta", age: 18 };
    let handler = {
      // 获取值
      get(obj, key) {
        console.log(obj, "obj", key, "key");
        return obj[key];
      },
      // 设置值
      set(obj, key, val) {
        console.log("set proxy: ", obj, key, val);
        obj[key] = val;
        console.log("更新了UI界面");
        return true
      },
    };
    let state = new Proxy(obj, handler);
    
    state.name = "rita";
    console.log(state);
    
    

Composition API(组合API)

专业版: 通过创建 Vue 组件,我们可以将接口的可重复部分及其功能提取到可重用的代码段中。仅此一项就可以使我们的应用程序在可维护性和灵活性方面走得更远。然而,我们的经验已经证明,光靠这一点可能是不够的,尤其是当你的应用程序变得非常大的时候——想想几百个组件。在处理如此大的应用程序时,共享和重用代码变得尤为重要。

设计动机

  1. 维护大型项目的时候,特别是在阅读他人代码时,uptionAPI会把完整的逻辑切割到不同选项,导致阅读苦难

  2. 组件公共代码,需要进行代码复用,vue2.0会出现比较困难的情况,例如mixin

  3. 为了以后使用ts实现更好的类型推断

img

再次回顾下:Vue3的优势

一、Vue 3.0 性能提升主要是通过哪几方面体现的?

1、源码体积的优化
  • 重写了虚拟 dom
2、响应式系统的升级
  • 用 Proxy 和 Reflect 来代替 vue2 中的 Object.definepeoperty()方法来重写响应式
  • vue3 中可以监听动态新增的属性
  • vue3 中可以监听删除的属性
  • vue3 中可以监听数组的索引和 length 属性
3、代码编译优化
  • 使用了 组合 API 来代替 vue2 中的 Options API
  • 组件内不需要根节点了,使用 fragment(代码片段)代替了,fragment(代码片段)不会在页面显示
  • vue3 中标记和提升所有的静态根节点,diff 的时候只需要对比动态节点内容

二、Vue 3.0 所采用的 Composition Api 与 Vue 2.x 使用的 Options Api 有什么区别?

  • 代码更利于维护和封装
  • Vue2 中,我们会在一个 vue 文件的 data,methods,computed,watch 中定义属性和方法,共同处理页面逻辑 ,一个功能的实现,代码过于分散
  • vue3 中,代码是根据逻辑功能来组织的,一个功能的所有 api 会放在一起(高内聚,低耦合),提高可读性和可维护性,基于函数组合的 API 更好的重用逻辑代码
  • Vue3 中用 setup 函数代替了 Vue2 中的 beforeCreate 和 created
  • Vue3 中用 onUnmounted 代替了 Vue2 中的 beforeDestory
  • Vue3 中用 unmounted 代替了 Vue2 中的 destroyed

三、Proxy 相对于 Object.defineProperty 有哪些优点?

  • 代码的执行效果更快
  • Proxy 可以直接监听对象而非属性
  • Proxy 可以直接监听数组的变化
  • Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的
  • Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改
  • Proxy 不需要初始化的时候遍历所有属性,另外有多层属性嵌套的话,只有访问某个属性的时候,才会递归处理下一级的属性

四、Vue 3.0 在编译方面有哪些优化?

  • diff算法的优化:vue3.x 中标记和提升所有的静态节点,diff 的时候只需要对比动态节点内容
  • Fragments(升级 vetur 插件): template 中不需要唯一根节点,可以直接放文本或者同级标签
  • 静态提升(hoistStatic),当使用 hoistStatic 时,所有静态的节点都被提升到 render 方法之外.只会在应用启动的时候被创建一次,之后使用只需要应用提取的静态节点,随着每次的渲染被不停的复用
  • patch flag, 在动态标签末尾加上相应的标记,只有带 patchFlag 的节点才被认为是动态的元素,会被追踪属性的修改,能快速的找到动态节点,而不用逐个逐层遍历,提高了虚拟 dom diff 的性能
  • 缓存事件处理函数 cacheHandler, 避免每次触发都要重新生成全新的 function 去更新之前的函数
  • tree shaking, 通过摇树优化核心库体积,减少不必要的代码量

五、Vue.js 3.0 响应式系统的实现原理?

  • 通过 2 个响应式 API 函数的调用,一个是 reactive() , 一个是 ref(), 就可以大致明白了
  • reactive 函数是用来把普通对象创建成为响应式对象的,函数内通过执行 proxy 创建的 get、set、deleteProperty 方法来实现
  • get 方法获取响应式数据,同时调用 track 方法去收集依赖
  • set 方法设置响应式数据,同时调用 trigger 方法是触发响应式数据的更新
  • deleteProperty 方法删除响应式数据,同时用 trigger 方法是触发响应式数据的更新
  • ref 函数是用来把一般类型的数据或者普通对象创建成为响应式对象的,函数内返回的是一个对象
  • 对象内的 get 方法获取响应式数据,同时调用 track 方法去收集依赖
  • 对象内的 set 方法设置响应式数据,同时调用 trigger 方法是触发响应式数据的更新
  • tarck 函数内通过 targetMap 找到 depsMap 通过 depsMap 找到 dep,最后向 dep 内添加 effect()函数
  • trigger 函数内通过 targetMap 找到 depsMap 通过 depsMap 找到 dep,最后遍历 dep 数组,执行里面的 effect()函数来更新响应式数据

附录:一些参考资料和网站

1,PatchFlags

export const enum PatchFlags {Ⅰ
  TEXT = 1,1/动态文本节点
  CLASS = 1<<1,1/ 2// 动态 classSTYLE= 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 或部分子字节有
  keyUNKEYED_FRAGMENT = 1<<8,// 256//子节点没有key 的 fragment
  NEED_ PATCH =1<<9,//512//一个节点只会进行非 props比较
  DYNAMIC_SLOTS = 1 << 10,//1024 // 动态的插槽
  
  // --------- SPECIAL FLAGS (下面是特殊的)---------------
  // 以下是特殊的flag,不会在优化中被用到,是内置的特殊flag
  HOISTED = -1,
  BAIL = -2, // 用来表示一个节点的diff应该结束
}

2,验证网址:

https://vue-next-template-explorer.netlify.app/

[图片上传失败...(image-81db52-1629942396026)]

3,参考文档地址

官方文档:https://www.vue3js.cn/

Vite使用文档:https://vue3js.cn/vite/

TS学习文档:https://www.tslang.cn/

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

推荐阅读更多精彩内容