總體來說 性能提升 重寫了虛擬DOM的實現(跳過靜態節點,只處理動態節點) update性能提高1.3~2倍 服務端渲染速度提高了2~3倍 樹搖(Tree shaking) 可以將無用模塊“剪輯”掉,僅打包需要的 原理: ES6 Module引入進行靜態分析,故而編譯的時候正確判斷到底載入了那些模塊 ...
目錄
總體來說
性能提升
- 重寫了虛擬DOM的實現(跳過靜態節點,只處理動態節點)
- update性能提高1.3~2倍
- 服務端渲染速度提高了2~3倍
樹搖(Tree shaking)
可以將無用模塊“剪輯”掉,僅打包需要的
原理:
- ES6 Module引入進行靜態分析,故而編譯的時候正確判斷到底載入了那些模塊
- 靜態分析程式流,判斷那些模塊和變數未被使用或者引用,進而刪除對應代碼
碎片化節點(Fragment)
在 vue2.x 中,每個組件只能有一個根,所以,寫每個組件模板時都要套一個父元素。
在 vue3 中,為了更方便的書寫組件模板,新增了一個類似 dom 的標簽元素
這樣做的好處在於:減少標簽層級, 減小記憶體占用。
傳送門 (Teleport)
可以將一個組件內部的一部分模板“傳送”到該組件的 DOM 結構外層的位置去。
Suspense
用於協調對組件樹中嵌套的非同步依賴的處理。
如果在渲染時遇到非同步依賴項 (非同步組件和具有 async setup() 的組件),它將等到所有非同步依賴項解析完成時再顯示預設插槽。
https://cn.vuejs.org/api/built-in-components.html#suspense
更好的TypeScript支持
Composition API
組合式API,替換原有的 Options API
根據邏輯相關性組織代碼,提高可讀性和可維護性
更好的重用邏輯代碼(避免mixins混入時命名衝突的問題)
相容了VUE2中的寫法,原有Options API依然可以延用
響應式原理
不再基於 Object.defineProperty而是基於ES6中的Proxy
開發時差異
0. setup
組合式API以setup函數作為組件的入口
<script setup>
是在單文件組件 (SFC) 中使用組合式 API 的編譯時語法糖。當同時使用 SFC 與組合式 API 時該語法是預設推薦。相比於普通的 <script>
語法,它具有更多優勢:
- 更少的樣板內容,更簡潔的代碼。
- 能夠使用純 TypeScript 聲明 props 和自定義事件。
- 更好的運行時性能 (其模板會被編譯成同一作用域內的渲染函數,避免了渲染上下文代理對象)。
- 更好的 IDE 類型推導性能 (減少了語言伺服器從代碼中抽取類型的工作)。
// setup 函數的第一個參數是組件的 props
// 沒有vue2.x中的this指向vue對象了
export default {
props: {
title: String
},
setup(props) {
console.log(props.title)
}
}
1. 生命周期調用
<script setup>
// 在vue中解構出方法
import { onMounted, onUpdated } from 'vue'
onMounted(() => {
...
})
onUpdated(() => {
...
})
</script>
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
2. 響應式(數據)API
ref
接受一個參數值並返回一個響應式且可改變的 ref 對象
- ref 對象擁有一個指向內部值的單一屬性 .value
- 當ref在模板中使用的時候,它會自動解套,無需在模板內額外書寫 .value
import { ref } from "vue";
export default {
setup() {
let Num = ref(0),
isShow = ref(false);
return {
Num,
isShow
};
}
};
reactive
- 接收一個普通對象然後返回該普通對象的響應式代理等同於 2.x 的 Vue.observable()
- 響應式轉換是“深層的”:會影響對象內部所有嵌套的屬性
使用toRefs(state)
可將其創建的傳為Refs類型,方便在模板中直接取值,而不用.value
import { ref, reactive } from "vue";
export default {
props: { title: String },
setup() {
let state = reactive({
Num: 0,
arr: [1, 2]
});
let change = () => {
state.arr[0] = state.arr[0] + 1;
state.name = "flytree";
};
return {
state,
change
};
}
};
相關的一些方法:
unref / toRef / toRefs / isRef / isProxy / isReactive / isReadonly
3.計算屬性computed
傳入一個 getter 函數,返回一個預設不可手動修改的 ref 對象
const count = ref(1);
const plusOne = computed(() => count.value + 1);
console.log(plusOne.value); //2
plusOne.value++; //錯誤!
或者傳入一個擁有 get 和 set 函數的對象,創建一個可手動修改的計算狀態
const count = ref(1);
const plusOne = computed({
get: () => count.value + 1,
set: val => {
count.value = val - 1;
}
});
plusOne.value = 1;
console.log(count.value); //0
4.偵聽器watch
watchEffect
立即執行傳入的一個函數,並響應式追蹤其依賴,併在其依賴變更時重新運行該函數
export default {
props: {
title: String,
},
setup(props) {
watchEffect(() => {
console.log(`title is: ` + props.title);
});
}
};
watch
- watch API 完全等效於 2.x this.$watch
- watch 需要偵聽特定的數據源,併在回調函數中執行副作用
- 預設情況是懶執行的,也就是說僅在偵聽的源變更時才執行回調偵聽單個數據源
點擊查看代碼
// 偵聽器的數據源可以是一個擁有返回值的 getter 函數,也可以是 ref
const state = reactive({ count: 0 })
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
);
const count = ref(0)
watch(count, (count, prevCount) => {
/* ... */
});
import { ref, reactive, toRefs, computed, watch } from "vue";
export default {
setup() {
....
let ratio = ref("--");
watch(state, (state, preState) => {
let total = state.supNum + state.oppNum;
ratio.value =
total === 0 ? "--" : ((state.supNum / total) * 100).toFixed(2) + "%";
});
return {
...,
ratio
};
}
};
5. 獲取DOM元素refs
模板的Refs
當使用組合式 API 時,reactive refs 和 template refs 的概念已經是統一的
點擊查看代碼
<template>
<div ref="root"></div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const root = ref(null);
onMounted(() => {
console.log(root.value);
});
return {
root
};
}
}
</script>
6. 路由使用, VUEX ,VUE掛載
先要解構出方法,再去創建對應路由,store或vue對象,其它一樣
Vue Router 4.x
import { createRouter, createWebHashHistory } from 'vue-router';
import routes from './routes';
const router = createRouter({
history: createWebHashHistory(),
routes
});
router.beforeEach(async (to, from, next) => { ... })
export default router;
Vue Router 3.x
import VueRouter from 'vue-router'
import routes from './routes';
const router = new VueRouter({
routes
})
export default router;
VUEX 4.x
import { createStore, createLogger } from 'vuex';
export default createStore({
state: {},
mutations: {},
actions: {},
plugins: [createLogger()]
})
VUEX 3.x
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {},
state: {},
mutations: {},
actions: {},
})
export default store
實例創建
VUE 3.x
import { createApp } from 'vue';
const app = createApp(App);
app.use(store);
app.use(router);
app.mount('#app');
VUE 2.x
import Vue from 'vue'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
7. 自定義指令處理技巧
export default function directive(app) {
app.directive('xxx', {
// 指令首次綁定到元素且在安裝父組件之前...「等同於bind」
beforeMount(el, binding, vnode, prevVnode) {
// binding:數據對象
// + arg:傳給指令的參數 v-xxx:n -> arg:"n"
// + modifiers:修飾符對象 v-xxx.stop -> modifiers:{stop:true}
// + value:指令綁定的值 v-xxx="1+1" -> value:2
// + oldValue:之前綁定的值
},
// 安裝綁定元素的父組件時...「等同於inserted」
mounted() {},
// 在包含組件的VNode更新之前...
beforeUpdate() {},
// 在包含組件的VNode及其子VNode更新後...「等同於componentUpdated」
updated() {},
// 在卸載綁定元素的父組件之前...
beforeUnmount() {},
// 指令與元素解除綁定且父組件已卸載時...「等同於unbind」
unmounted() {}
});
};
// main.js
import {
createApp
} from 'vue';
import App from './App.vue';
import directive from './directive';
const app = createApp(App);
directive(app);
app.mount('#app');