大家好,我是若川。持續組織了6個月源碼共讀活動,,每周大家一起學習200行左右的源碼,共同進步。同時極力推薦訂閱我寫的《學習源碼整體架構系列》 包含20餘篇源碼文章。曆史面試系列
一、Vue3
Vue3中文文檔[1]
- Vue3是什麼,與Vue2差別(What)
-
:性能更強。Performance
-
:可以将無用子產品“剪輯”,僅打包需要的。Tree shaking support
-
:組合式Composition API
API
-
:“碎片”,Fragment, Teleport, Suspense
即Teleport
,“懸念”Protal傳送門
-
:更優秀的Ts支援Better TypeScript support
-
:暴露了自定義渲染Custom Renderer API
API
- 為什麼要大版本疊代 (Why)
- 主流浏覽器對新的JavaScript語言特性的普遍支援。
- 目前Vue代碼庫随着時間的推移而暴露出來的設計和體系架構問題。
- 他是如何提升的(How)
- 響應式系統提升:使用Proxy提升了響應式的性能和功能
- 編譯優化:标記和提升所有的靜态節點,diff時隻需要對比動态節點内容
- 事件緩存:提供了事件緩存對象cacheHandlers,無需重新建立函數直接調用緩存的事件回調
- 打包和體積優化:按需引入,Tree shaking支援(ES Module)
二、編碼
全局API
- 【新增】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");
- 【修改】
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.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(當捕獲一個來自子孫元件的異常時激活鈎子函數) |
新特性
- Options API => Composition API

- 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()
在解析 2.x 選項前被調用,setup()
中的setup()
将與 2.x 選項中的this
完全不同。同時在this
和 2.x 選項中使用setup()
時将造成混亂this
- 響應式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 對象擁有一個指向内部值的單一屬性 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方式
- 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
}
}
- 父元件
<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>
響應式計算和偵聽
- 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
},
})
- watch
// 直接偵聽一個 ref
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
})
// 也可以使用數組來同時偵聽多個源
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
- 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
被調用的時機很微妙:它隻作用于異步函數,并且隻有在如下兩種情況下才會被調用:
- 當
函數被重新調用時effect
- 當監聽器被登出時(如元件被解除安裝了)
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 -
················· 若川簡介 ·················
你好,我是若川,江湖人稱菜如若川,曆時一年隻寫了一個學習源碼整體架構系列~
1. 關注若川視野,回複"pdf" 領取優質前端書籍pdf,回複"1",可加群長期交流學習