從 Compat 2021 到 Interop 2022
Compat 2021 致力於消除瀏覽器相容性問題的五個主要焦點領域,使開發者可以在這些領域上自信地建立可靠的基礎。而 Interop 2022 的重點是在15個開發者認為在不同瀏覽器之間缺失或存在相容性問題時尤其困擾的領域上制定基準。2216 Words (12min read)

嘗試 30 天寫文充實版面
(跳過特休假跟假日 ლ(́◕◞౪◟◕‵ლ) )不間斷更新文章的 Day 11
Vue 是一個簡潔而高效的 JavaScript 前端框架,Vue3.x 相對於 Vue2.x 帶來了多個重要改進和新特性。
Vue3 的核心在於更高效的渲染和更快的速度,而這是通過重新設計核心架構和創新功能來實現的。 這些改進使得 Vue3.x 更適用於大型 Web 應用程式,同時引入了多個新功能,例如: Composition API 和更好的 TypeScript 支援。這些功能讓開發者能夠更輕鬆地建構可重用且易於維護和擴展的應用程式,並能更快地構建更完整、更複雜的 Web 應用程式。
在本文中,將深入探討 Vue 3.x 相對於 Vue 2.x 的新特性和改進,並探討這些功能如何改進了 Vue 框架的開發體驗。
Two key considerations led us to the new major version (and rewrite) of Vue: First, the general availability of new JavaScript language features in mainstream browsers. Second, design and architectural issues in the current codebase that had been exposed over time.
Vue3 在重新設計核心架構上下了很大的功夫,使用了更先進的渲染技術,可以實現更快的渲染速度。
Vue3 強化了對 TypeScript 的支援,包括更好的類型推斷和更嚴格的類型檢查。
Vue3 引入了全新的 Composition API,提供了更好的代碼組織方式,可以更輕鬆地重用邏輯代碼和狀態邏輯,使得代碼更加清晰易懂。
Vue3 在設計上更為精簡,使得 Tree Shaking 技術更加容易實現,可以減少打包出來的代碼體積。
Vue3 提供了更好的支援適應性設計的 API,可以更輕鬆地開發適配不同設備的 Web 應用程式。
Vue2.x | Vue3.x |
---|---|
beforeCreate | setup |
created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
activated | onActivated |
deactivated | onDeactivated |
vm.$destroy()
已從 Vue3.0 移除
renderTracked、renderTriggered:This hook is development-mode-only and not called during server-side rendering.
activated、deactivated:This hook is not called during server-side rendering.
Vue2 可直接從元件選項訪問生命週期鉤子。
# Vue2
<script>
export default {
props: {
title: String,
},
// ...
mounted() {
console.log("component mounted");
},
// ...
};
</script>
Vue3 Composition API 幾乎所有內容都在 setup()
方法中,包括已安裝的生命週期掛鉤。
但在默認情況下不包含生命週期掛鉤,因此必須 onMounted
在 Vue3 中調用它時導入該方法。與下方導入響應式相同。
# Vue3
<script>
import { reactive, onMounted } from "vue";
export default {
props: {
title: String,
},
setup() {
// ..
onMounted(() => {
console.log("component mounted");
});
// ...
},
};
</script>
setup()
也處理方法。它的工作方式有點類似於聲明數據—— 必須先聲明方法然後返回它,以便元件的其他部分可以訪問它。Vue2 將資料放入 data 中,定義資料變數是 data(){}
,創建方法要在 methods:{}
中。
Vue3 使用 setup()
方法在元件初始化構造的時候觸發。
為了讓開發人員更好地控制什麼是響應性的,可以直接訪問 Vue 的 reactivity API (響應性 API)。
創建響應式數據涉及三個步驟:
vue
導入 reactive
reactive()
來聲明資料為響應性資料setup()
來返回響應性資料,從 template 可獲取這些響應性資料當使用 Object.defineProperty()
定義一個屬性時,該屬性只能在 ES5 及以上版本的 JavaScript 中使用。然而,大多數現代瀏覽器都支援 ES5,因此在大多數瀏覽器中可以使用 Object.defineProperty()。
當使用 Proxy API 時,該 API 只能在 ES6 及以上版本的 JavaScript 中使用。這意味著某些舊瀏覽器不支援 Proxy API,例如:IE11 及以下版本的瀏覽器不支援 (但 IE 沒了,可喜可賀)。為了向下兼容,可以使用 polyfill 或垫片庫,這些庫提供 Proxy API 的類似實現,讓較舊的瀏覽器也能夠使用 Proxy API。
在向上兼容方面,使用 Object.defineProperty()
定義的屬性可以在 ES5 及以上版本的瀏覽器中正常使用,也可以在支援 ES6 及以上版本的瀏覽器中使用;而使用 Proxy API 定義的屬性只能在 ES6 及以上版本的瀏覽器中使用。
總體而言,Object.defineProperty()
與 Proxy API 兩者並不完全相容,因為它們要求的最低 JavaScript 版本不同。然而,這兩種方法都可以用於實現資料雙向繫結,只是使用 Proxy API 通常更簡單和方便。
<template>
沒有特殊指令的標記(v-if/else-if/else、v-for
或 v-slot
)的 <template>
現在被視為普通元素,並將生成原生的 <template>
元素,而不是渲染其內部內容。
注:markdown 不可直接打 < + template + >,須加上 "`",否則會默認成本體程式一部分而結束。
props
,而子傳父事件是使用 Emitting Events
this.$emit
傳入事件和對象。setup()
第二個參數 content
對象就有 emit
,只要在 setup()
接受第二個參數中使用分解對象法取出 emit
就可以在 setup()
中任意取用了。slot
。v-for
與 v-if
優先權高的是 v-for 指令,不建議一起使用,可能衝突。v-slot
。v-for
與 v-if
只會把當前 v-if 當作 v-for 中的判斷語句,不會衝突。v-on
修飾符,也不支持 config.keyCode
。v-on.native
修飾符、過濾器 filter
。假設目前有兩個資料變數:
# Vue2
<script>
export default {
props: {
title: String,
},
data() {
return {
username: "",
password: "",
};
},
mounted() {
console.log('title: ' + this.title)
},
computed: {
lowerCaseUsername() {
return this.username.toLowerCase()
},
},
methods: {
login() {
this.$emit('login', {
username: this.username,
password: this.password,
})
},
},
};
</script>
// access them like title, username and password
# Vue3
<script>
import { reactive, onMounted, computed } from 'vue';
export default {
props: {
title: String,
},
setup(props, { emit }) {
const state = reactive({
username: "",
password: "",
lowerCaseUsername: computed(() => state.username.toLowerCase()),
});
onMounted(() => {
console.log('title: ' + props.title);
});
const login = () => {
emit('login', {
username: state.username,
password: state.password,
});
};
return {
state,
login,
};
},
};
</script>
// access them like state.title, state.username and state.password
Vue3.x 使用 proxy 代替 Vue2.x defineProperty
在 Options API 中,即使是 data 裡定義的變數只是單純做為 setInterval 等功能的變數,Vue2.x 仍然會將它們做成雙向綁定,而在 Composition API 中,能夠更好地區分哪些變數應該被 Vue3.x 用於雙向綁定,哪些變數只需作為單純變數使用。
Object.defineProperty()
在對象上定義一個屬性,該屬性的 get
和 set
函數被觸發時,會通過觀察者模式(Observer Pattern)將資料變化通知給 Vue 的更新機制。v-model
或表單元素等,使用的是 input
事件和 value
屬性繫結,當表單元素的值發生變化時,會通過 input
事件觸發 Vue 的更新機制。Object.defineProperty()
有一些限制,例如無法監聽屬性的添加和刪除,對於嵌套對象的監聽也不方便。最大缺點:為什麼 Vue2.x 無法深度監聽陣列物件?
因為每次元件渲染時,Vue2.x 是透過 defineProperty 雙向繫結 data 中的資料。如果一個屬性沒有在一開始定義時就加入,它就不會被綁定,也不會觸發更新和重新渲染。
v-model
或表單元素等,使用的是 modelValue
和 update:modelValue
兩個 props 進行繫結,當表單元素的值發生變化時,會觸發 update:modelValue
事件並將變化的值作為參數傳入,從而觸發 Vue 的更新機制。Vue3.x 使用 ES6 的 Proxy API 來對資料進行代理,相較於 defineProperty
的方式,使用 proxy
有以下優勢:
for in
和 Object.keys
等方式遍歷對象的步驟,提高了效率比較 Vue2.x 和 Vue3.x 在使用 Object.defineProperty() 和 Proxy API 時的差異:
# Vue2.x:使用 Object.defineProperty()
const obj = {
name: 'Herry',
age: 22,
};
// 通過 Object.defineProperty() 監聽對象屬性的變化
Object.keys(obj).forEach(key => {
let value = obj[key];
Object.defineProperty(obj, key, {
get() {
console.log(`讀取 ${key} 屬性值: ${value}`);
return value;
},
set(newValue) {
console.log(`設置 ${key} 屬性值: ${newValue}`);
value = newValue;
},
});
});
// 設置 name 屬性值: Bob
obj.name = 'Bob';
// 讀取 name 屬性值: Bob
console.log(obj.name);
# Vue3.x:使用 Proxy API
const obj = {
name: 'Herry',
age: 22,
};
// 通過 Proxy 監聽整個對象的變化
const handler = {
get(target, key, receiver) {
console.log(`讀取 ${key} 屬性值: ${target[key]}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`設置 ${key} 屬性值: ${value}`);
return Reflect.set(target, key, value, receiver);
};
};
const proxy = new Proxy(obj, handler);
// 設置 name 屬性值: Bob
proxy.name = 'Bob';
// 讀取 name 屬性值: Bob
console.log(proxy.name);
可以看到 Vue2.x 使用 Object.defineProperty()
監聽對象屬性的變化,但是需要逐個設置每個屬性,而且無法監聽整個對象的變化。而 Vue3.x 使用 Proxy API 可以監聽整個對象的變化,並且配置選項多,可以做更細緻的事情。
總結而言,Vue3.x 的資料雙向繫結原理通過 Proxy 對象實現,相較於 Vue2.x 使用 Object.defineProperty()
方法實現的方式,更加高效和精確。
當 Virtual DOM 中的資料發生變化時,Vue2.x 和 Vue3.x 都會透過 Diff 演算法比較 Virtual DOM 與真實 DOM 的差異,然後只更新差異部分的真實 DOM。
以下為取自官方文檔實例解釋:
在 Vue2.x 中,不支持多個根節點,只可以多個元件包裝在 <div>
中。
<!-- Layout.vue -->
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
在 Vue3.x 中,元件可以設置多個根節點,然而這需要開發者去明確定義應該在哪裡分配屬性。
<!-- Layout.vue -->
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
Vue 3.x 的 Teleport 功能可以讓你將一個元件的內容「傳送」(teleport)到 DOM 樹中的任何位置,而不需要將該元件包裝在其他元件中。這樣可以讓你更方便地控制元件的位置和渲染效果,不需要為了實現這些效果而添加額外的 DOM 結構或使用複雜的 CSS。
Teleport 具有多種用途,例如將對話框顯示在頁面的中心位置,或將提示信息顯示在畫面的任何位置。它還可以與動畫和過渡效果一起使用,以實現更加流暢和吸引人的使用者體驗。
Teleport 的使用方法非常簡單,只需要在模板中使用 Teleport
元素包裝你想要傳送的元件,並指定目標位置的選擇器即可。在元件被渲染時,它的內容就會自動傳送到目標位置。
範例可以參考官方文件說明。
[1] Introduction | Vue.js
[2] Vue2 to Vue3 — What’s changed?. This article gives a brief introduction… | by Thilanka Dilakshi | Embla Tech | Medium
https://medium.com/emblatech/vue2-to-vue3-whats-changed-5572514da20d
[3] Building the Same Component in Vue2 vs. Vue 3 - LearnVue | LearnVue
https://learnvue.co/tutorials/vue-2-vs-vue-3
以上部分為譯文,結合個人理解與查閱各論壇與官方文件等所產生,如果有錯誤的地方歡迎透過 Email 指出 (留言區還在待辦計劃)。
因為面試題目所以來惡補實體知識了QQ,雖然腦袋知道有什麼不同但就是禿然一片空白,希望下次可以流暢的答出來。
總結努力打好基礎學會並努力記起每一種語法,GO GO GO!
[Day 11] Vue3.x 相對於 Vue2.x 的新特性和改進 - 嘗試 30 日寫文充版挑戰
Author
Released
2216 Words (12min read)
2161 Words (11min read)
1851 Words (10min read)