天天看點

記一次 Vue2 遷移 Vue3 的實踐總結

大家好,我是若川​。持續組織了6個月源碼共讀活動​,,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》​ 包含20餘篇源碼文章。曆史面試系列

一、Vue3

Vue3中文文檔[1]

  • Vue3是什麼,與Vue2差別(What)
  1. ​Performance​

    ​:性能更強。
  2. ​Tree shaking support​

    ​:可以将無用子產品“剪輯”,僅打包需要的。
  3. ​Composition API​

    ​​:組合式​

    ​API​

  4. ​Fragment, Teleport, Suspense​

    ​​:“碎片”,​

    ​Teleport​

    ​​即​

    ​Protal傳送門​

    ​,“懸念”
  5. ​Better TypeScript support​

    ​:更優秀的Ts支援
  6. ​Custom Renderer API​

    ​​:暴露了自定義渲染​

    ​API​

  • 為什麼要大版本疊代 (Why)
  1. 主流浏覽器對新的JavaScript語言特性的普遍支援。
  2. 目前Vue代碼庫随着時間的推移而暴露出來的設計和體系架構問題。
  • 他是如何提升的(How)
  1. 響應式系統提升:使用Proxy提升了響應式的性能和功能
  2. 編譯優化:标記和提升所有的靜态節點,diff時隻需要對比動态節點内容
  3. 事件緩存:提供了事件緩存對象cacheHandlers,無需重新建立函數直接調用緩存的事件回調
  4. 打包和體積優化:按需引入,Tree shaking支援(ES Module)

二、編碼

全局API

  1. 【新增】createApp:入口檔案(main.ts)挂載方式
import { createApp } from "vue";

import App from "./App.vue";



// Vue2

new Vue({

   render: (h) => h(App)

}).$mount("#app");



// Vue3

createApp(App)

  .use(**)

  .mount("#app");      
  1. 【修改】
2.x 全局 API 3.x 執行個體 API (app)
Vue.config app.config
Vue.config.productionTip
Vue.config.ignoredElements app.config.isCustomElement
Vue.component app.component
Vue.directive app.directive
Vue.mixin app.mixin
Vue.use app.use
Vue.prototype app.config.globalProperties

config.ignoredElements​

​替換為​

​config.isCustomElement

引入此配置選項的目的是支援原生自定義元素,是以重命名可以更好地傳達它的功能,新選項還需要一個比舊的 string/RegExp 方法提供更多靈活性的函數:

// Vue2

Vue.config.ignoredElements = [

  // 用一個 `RegExp` 忽略所有“ion-”開頭的元素

  // 僅在 2.5+ 支援

  /^ion-/

]



// Vue3

const app = Vue.createApp({})

app.config.isCustomElement = tag => tag.startsWith('ion-')      

​Vue.prototype​

​​ 替換為 ​

​config.globalProperties​

在Vue 2中,Vue.prototype通常用于添加可在所有元件中通路的屬性。

Vue 3中的等效項是config.globalProperties。在執行個體化應用程式内的元件時,将複制這些屬性

// Vue2

Vue.prototype.$http = () => {}



// Vue3

const app = Vue.createApp({})

app.config.globalProperties.$http = () => {}      

生命周期

2.0生命周期 3.0生命周期
beforeCreate(元件建立之前) setup()
created(元件建立完成) setup()
beforeMount(元件挂載之前) onBeforeMount(元件挂載之前)
mounted(元件挂載完成) onMounted(元件挂載完成)
beforeUpdate(資料更新,虛拟DOM打更新檔之前) onBeforeUpdate(資料更新,虛拟DOM打更新檔之前)
updated(資料更新,虛拟DOM渲染完成) onUpdated(資料更新,虛拟DOM渲染完成)
beforeDestroy(元件銷毀之前) onBeforeUnmount(元件銷毀之前)
destroyed(元件銷毀之後) onUnmounted(元件銷毀之後)
activated(被 keep-alive 緩存的元件激活時調用) onActivated(被激活時執行)
deactivated(被 keep-alive 緩存的元件停用時調用) onDeactivated(比如從 A 元件,切換到 B 元件,A 元件消失時執行)
errorCaptured(當捕獲一個來自子孫元件的錯誤時被調用) onErrorCaptured(當捕獲一個來自子孫元件的異常時激活鈎子函數)

新特性

  1. Options API => Composition API
記一次 Vue2 遷移 Vue3 的實踐總結
記一次 Vue2 遷移 Vue3 的實踐總結
  1. setup()
import {toRefs} from 'vue'

export default {

    name: 'demo',

    props:{

      name: String,

    },

    // setup()作為在元件内使用Composition API的入口點。

    // 執行時機是在beforeCreate和created之間,不能使用this擷取元件的其他變量,

    // 而且不能是異步。setup傳回的對象和方法,都可以在模版中使用。

    setup(props, context){

      // 這裡需要使用toRefs來進行解構

      // 這裡的props與vue2基本一緻,當然這裡的name也可以直接在template中使用

      const { name }=toRefs(props);

      console.log(name.value);

      

      // context是一個上下文對象

      //【從原來 2.x 中 this 選擇性地暴露了一些 property(attrs/emit/slots)】

      // 屬性,同vue2的 $attrs

      console.log(context.attrs);

      // 插槽

      console.log(context.slots);

      // 事件,同vue2的 $emit

      console.log(context.emit);

      // 生命周期鈎子

      onMounted(() => {})

  }

}      

注意點:

  • 注意​

    ​props​

    ​​ 對象是響應式的,​

    ​watchEffect​

    ​​ 或​

    ​watch​

    ​​ 會觀察和響應​

    ​props​

    ​ 的更新,不要解構​

    ​props​

    ​ 對象,那樣會使其失去響應性
  • ​attrs​

    ​​ 和​

    ​slots​

    ​ 都是内部元件執行個體上對應項的代理,可以確定在更新後仍然是最新值。是以可以解構,無需擔心後面通路到過期的值
  • ​this​

    ​在​

    ​setup()​

    ​中不可用。由于​

    ​setup()​

    ​​ 在解析 2.x 選項前被調用,​

    ​setup()​

    ​​ 中的​

    ​this​

    ​​ 将與 2.x 選項中的​

    ​this​

    ​​ 完全不同。同時在​

    ​setup()​

    ​​ 和 2.x 選項中使用​

    ​this​

    ​ 時将造成混亂
  1. 響應式reactive,ref
import { ref, reactive, toRefs } from 'vue';



setup() {

    // ref

    // ref 對我們的值建立了一個響應式引用

    const counter = ref(0); 

    

    // reactive

    // 接收一個普通對象然後傳回該普通對象的響應式代理

    const obj = {a:1};

    const objReactive = reactive(obj);



    const add = () => {

      counter.value++;

      objReactive.a++;

    }

    

    return {

      counter,  // return傳回會自動解套【在模闆中不需要.value】

      objReactive,

      add,

    }; // 這裡傳回的任何内容都可以用于元件的其餘部分

}      
ref reactive
入參 基本類型 引用類型
傳回值 響應式且可變的 ref 對象 響應式代理(Proxy)
通路方式 1.ref 對象擁有一個指向内部值的單一屬性 ​

​.value​

2.在dom和setup()的return中會自動解套

3.ref 作為 reactive 對象的 property 被通路或修改時,也将自動解套

直接.通路即可

問題 & 注意點: 因為reactive是組合函數【對象】,是以必須始終保持對這個所傳回對象的引用以保持響應性,不能解構該對象或者展開

例如:

​const { a } = objReactive​

​​或者​

​return { ...objReactive }​

解決方法:

​toRefs​

​ API

用來提供解決此限制的辦法——它将響應式對象的每個 property 都轉成了相應的 ref【把對象轉成了ref】。

import { reactive, toRefs } from 'vue';



setup() {

    // reactive

    // 接收一個普通對象然後傳回該普通對象的響應式代理

    const obj = {a:1};

    const objReactive = reactive(obj);

    

    // toRefs

    // 将響應式對象轉換為普通對象,其中結果對象的每個 property 都是指向原始對象相應 property 的ref

    const objRef = toRefs(objReactive);

    

    const { a } = objRef;

    const addObj = () => {

      a.value++;

      console.log(a.value, objRef, objReactive, obj);

    }



    return {

      ...objRef,

      addObj

    }; 

}      
  • Hooks方式
  1. counter.js
import { ref } from 'vue';



export function useCounter() {

    const count = ref(0);

    const decrement = () => {

        count.value--;

    }

    const increment = () => {

        count.value++;

    }

    return {

        count,

        decrement,

        increment

    }

}      
  1. 父元件
<template>

    <h2>{{ count }}</h2>

    <button @click="increment">increment</button>

    <button @click="decrement">decrement</button>

</template>

<script>

import { useCounter } from './counter.js';

export default {

    setup() {

        return {

          ...useCounter(),

        };

    }

}

</script>      

響應式計算和偵聽

  1. computed
const count = ref(1)

/*不支援修改【隻讀的】 */

const plusOne = computed(() => count.value + 1)

plusOne.value++ // 錯誤!



/*【可更改的】 */

const plusOne = computed({

  get: () => count.value + 1,

  set: (val) => {

    count.value = val - 1

  },

})      
  1. watch
// 直接偵聽一個 ref

const count = ref(0)

watch(count, (count, prevCount) => {

  /* ... */

})



// 也可以使用數組來同時偵聽多個源

watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {

  /* ... */

})      
  1. watchEffect
  • 定義:在響應式地跟蹤其依賴項時立即運作一個函數,并在更改依賴項時重新運作它
  • 第一個參數:​

    ​effect​

    ​​,顧名思義,就是包含副作用的函數。如下代碼中,副作用函數的作用是:當​

    ​count​

    ​ 被通路時,旋即(随即)在控制台打出日志。
  • 傳回值:也是一個函數,顯式調用可以清除watchEffect,元件解除安裝時會被隐式調用
const count = ref(0);

const stop = watchEffect(() => console.log(count.value)); // -> logs 0



setTimeout(() => {

  count.value++; // -> logs 1

}, 100);

// 清除watchEffect

stop();      
  • 清除副作用(onInvalidate)

​watchEffect​

​​ 的第一個參數——​

​effect​

​​函數——自己也有參數:叫​

​onInvalidate​

​​,也是一個函數,用于清除 ​

​effect​

​ 産生的副作用。

​onInvalidate​

​ 被調用的時機很微妙:它隻作用于異步函數,并且隻有在如下兩種情況下才會被調用:

  1. 當​

    ​effect​

    ​ 函數被重新調用時
  2. 當監聽器被登出時(如元件被解除安裝了)
import { asyncOperation } from "./asyncOperation";



const id = ref(0);



watchEffect((onInvalidate) => {

  const token = asyncOperation(id.value);



  // onInvalidate 會在 id 改變時或停止偵聽時,取消之前的異步操作(asyncOperation)

  onInvalidate(() => {

    token.cancel();

  });

});      
  • 第二個參數:options

主要作用是指定排程器,即何時運作副作用函數。

// fire before component updates

watchEffect(

  () => {

    /* ... */

  },

  {

    flush: "pre",

    onTrigger(e) {

      // 依賴項變更導緻副作用被觸發時,被調用

      debugger;

    },

    onTrack(e) {

      // 依賴項變更會導緻重新追蹤依賴時,被調用【調用次數為被追蹤的數量】

      debugger

    },

  }

);      

三、源碼

未完待續...

四、其他相關

Vite2.0,2月17号正式釋出

中文文檔:https://cn.vitejs.dev/

五、感悟

優點:很優秀

缺點:他的對手(React),更優秀

雖然好多地方神似React,但是我們也可以從中看出,他們的都源于比較成熟的程式設計範式——FP(Functional Programming)。

架構隻是工具,解決問題才是終極目标;我們還是要把重點放在領悟架構的設計思想上;悟到了,才是真正掌握了解決問題的手段。(抄的)

參考文檔:

https://www.jianshu.com/p/ad38a1f27d0f

https://www.jianshu.com/p/a8fdf52d0bcf

參考資料

[1]

Vue3中文文檔: https://v3.cn.vuejs.org/

- END -

記一次 Vue2 遷移 Vue3 的實踐總結

················· 若川簡介 ·················

你好,我是若川,江湖人稱菜如若川,曆時一年隻寫了一個學習源碼整體架構系列~

1. 關注若川視野,回複"pdf" 領取優質前端書籍pdf,回複"1",可加群長期交流學習

繼續閱讀