參考:
https://vue-composition-api-rfc.netlify.app/zh/#%E6%A6%82%E8%BF%B0
https://www.yuque.com/gdnnth/vue-v3/xvrc7e
https://www.jianshu.com/p/4725441aff5f
https://blog.csdn.net/wanghuan1020/article/details/109362810
一、 Vue3.0六大兩點
- Performance:性能比Vue2.x快1.2~2倍
- Tree shaking support:按需編譯,體積比Vue2.x更小
- Composition API:組合API(類似React Hooks)
- Better TypeScript support:更好的 Ts 支持
- Custom Renderer API:暴露了自定義渲染API
- Fragment,Teleport(Protal),Suspense:更先進的組件
二、VUE3.0是如何變快的
1.diff方法優化:http://vue-next-template-explorer.netlify.app/
-
- Vue2 中的虛擬dom是進行全量的對比
- Vue3 新增了靜態標記(PatchFlag)
在與上次虛擬節點進行對比時候,只對比帶有 patch flag 的節點
並且可以通過 flag 的信息得知當前節點要對比的具體內容
在創建虛擬dom的時候,會根據DOM中的內容會不會發生變化,添加靜態標記
2.hoistStatic 靜態提升
-
- Vue2中無論元素是否參與更新,每次都會重新創建,然后再渲染
- Vue3中對不參與更新的元素,會做靜態提升,只會被創建一次,在渲染時直接復用即可
3.cacheHandlers 事件偵聽緩存
-
- 默認情況下 onClick 會被視為動態綁定,所以每次都會去追蹤它的變化
- 但是因為是同一個函數,所以沒有追蹤變化,直接緩存起來復用即可
- 在Vue3中的diff算法中,只有存在靜態標記的節點才會進行追蹤,事件偵聽緩存本質上是去除了不必要的diff比較
4.SSR渲染
-
- 當有大量靜態的內容時候,這些內容會被當做純字符串推進一個Buffer里面,即使存在動態的綁定,會通過模板插值嵌入進去。這樣會比通過虛擬dom來渲染快上很多。
- 當靜態內容達到一定量級時候,會使用_createStaticVNode方法在客戶端dom來渲染一個static node,這些靜態node,會被直接innerHtml,就不需要創建對象,然后根據對象渲染。
三、VUE3.0的創建
1.VUE-CLI
1 npm install -g @vue/cli
2 vue create projectName 3 cd projectName 4 npm run serve
2.Webpack
git clone https://github.com/vuejs/vue-next-webpack-preview.git projectName
cd projectName npm install npm run dev
3.Vite
Vite是Vue作者開發的一款意圖取代webpack的工具
其實現原理是利用ES6的import會發送請求去加載文件的特性,攔截這些請求,做一些預編譯,省去webpack冗長的打包時間
// 1. 安裝Vite
npm install -g create-vite-app // 2. 創建Vue3項目
create-vite-app projectName // 3. 安裝依賴運行項目
cd projectName npm install npm run dev
四、Composition API
<template>
<div>
<p>{{ count }}</p>
<button @click="myFun">按鈕</button>
</div>
</template>
<script>
// ref函數只能監聽簡單類型的變化,不能監聽復雜類型的變化(對象,數組),監聽復雜類型引入並使用reactive
import { ref } from 'vue' export default { name: 'App', // setup 函數是組合api的入口函數,執行時間在beforeCreate和created之間
setup() { // 定義了一個count變量,初始值為0
// 該變量發生改變后,Vue會自動更新UI
let count = ref(0) // 在組合api中,如果定義方法,不需要定義到methods,直接定義即可
function myFun() { count.value += 1 // 修改值,count.value而不是count
} /** * 注意點: * 在組合api中定義的變量/方法,要想在外界使用,必須通過return { xxx, xxx } 暴露出去 */
return { count, myFun } }, } </script>
1.setup:入口函數
-
- 由於在執行setup函數的時候,還沒有執行 created 生命周期方法,所以在 setup 函數中,是無法使用 data 和 methods
- 由於不能再 setup 函數中使用 data 和 methods,所以 Vue 為了避免我們的錯誤使用,它直接將 setup 函數中的 this 修改成了 undefined
- setup 函數只能是同步的,不能是異步的,async setup() {} 錯誤使用
2.ref :響應式數據(基本數據類型)
2.1. 什么是 ref?
ref 和 reactive 一樣,也是用來實現響應式數據的方法
由於 reactive 必須傳遞一個對象,所以導致在企業開發中,如果我們只想讓某個變量實現響應式的時候會非常麻煩,
所以 Vue3 就給我們提供了 ref 方法,實現對簡單值的監聽
2.2. ref 本質
ref 底層的本質其實還是 reactive,系統會自動根據我們給 ref 傳入的值將它轉換成 ref(xx) -> reactive({value: xx})
2.3. ref 注意點
在 Vue 中使用 ref 的值不用通過 value 獲取
在 JS 中使用 ref 的值必須通過 value 獲取
3.reactive:響應式數據(json/arr)
<template>
<div>
<p>{{time}}</p>
<button @click="myFn">按鈕</button>
</div>
</template>
<script>
/* reactive的用法與ref的用法相似,也是將數據變成響應式數據,當數據發生變化時UI也會自動更新。 不同的是ref用於基本數據類型,而reactive是用於復雜數據類型,比如對象和數組 */
// toRefs解構
import { reactive,toRefs } from 'vue' export default { name: 'App', setup() { // 賦值
let state = reactive({ time: new Date(), }) function myFn() { /* reactive中傳遞的參數必須是json對象或者數組,如果傳遞了其他對象(比如new Date()),在默認情況下修改對象, 界面不會自動更新,如果也需要具有響應式,可以通過重新賦值的方式實現 */ const newTime = new Date(state.time.getTime()) newTime.setDate(newTime.getDate() + 1) state.time = newTime // state.time.setDate(state.time.getDate() + 1) ,頁面不更新
console.log(state) // reactive將傳遞的對象包裝成了proxy對象
} return { ...toRefs(state), myFn, } }, } </script>
3.1. 什么是 reactive?
-
- reactive 是 Vue3 中提供的實現響應式數據的方法
- 在 Vue2 中響應式數據是通過 defineProperty 來實現的,而在 Vue3 中響應式數據是通過 ES6 的 Proxy 來實現的
3.2. reactive 注意點
-
- reactive 參數必須是對象 (json/array)
- 如果給 reactive 傳遞了其他對象
- 默認情況下修改對象,界面不會自動更新
- 如果想更新,可以通過重新賦值的方式
4.isRef 和 isReactive :判斷一個數據是 ref 類型還是 reactive 類型
import { ref, reactive, isRef, isReactive } from 'vue' export default { name: 'App', setup() { let age = ref(18) // let age = reactive({
// value: 18
// })
function myFun() { console.log(isReactive(age)) console.log(isRef(age)) age.value += 2 } return { age, myFun } } }
5.只讀:readonly,shallowReadonly,isReadonly
-
- readonly:用於創建一個只讀的數據,並且是遞歸只讀
- shallowReadonly: 用於創建一個只讀數據,但是不是遞歸只讀的
- isReadonly:對於 readonly 和 shallowReadonly 創建的數據,返回結果均為 true
- const 和 readonly 的區別:
const: 賦值保護,不能給變量重新賦值
readonly:屬性保護,不能給屬性重新賦值
import { readonly, isReadonly, shallowReadonly } from 'vue' export default { name: 'App', setup() { // 用於創建一個只讀的數據,並且是遞歸只讀
let state = readonly({ name: 'lnj', attr: { age: 18, height: 1.88, }, }) function myFn() { state.name = '驚蟄' state.attr.age = 666 state.attr.height = 1.66 console.log(state) console.log(isReadonly(state)) } return { state, myFn, } }, }
6.shallowRef和shallowReactive:非遞歸監聽
遞歸監聽
默認情況下,無論是通過 ref 還是 reactive 都是遞歸監聽
每一層都包裝成了一個 proxy 對象
遞歸監聽存在的問題
如果數據量比較大,非常消耗性能
非遞歸監聽
shallowReactive
非遞歸監聽下,第一層被包裝成了 proxy
這意味着:只有第一層的數據發生改變,才會觸發 UI界面 的更新
shallowRef
如果是通過 shallowRef 創建數據,那么 Vue監聽的是 .value 的變化,並不是第一層的變化
如果想在修改其內部數據后觸發界面的更新,可以調用 triggerRef 方法
應用場景
一般情況下 使用 ref 和 reactive 即可
只有在需要監聽的數據量比較大的時候,我們才使用 shallowRef / shallowReactive
<template>
<div>
<p>{{ state.a }}</p>
<p>{{ state.gf.b }}</p>
<p>{{ state.gf.f.c }}</p>
<p>{{ state.gf.f.s.d }}</p>
<button @click="myFun">按鈕</button>
</div>
</template>
<script> import { ref, shallowRef, triggerRef } from 'vue' export default { name: 'App', setup() { // shallowRef 本質上還是 shallowReative
// shallowRef(10) -> shallowReactive({ value: 10 })
// 所以如果是通過 shallowRef 創建的數據,它監聽的是 .value 的變化
let state = shallowRef({ a: 'a', gf: { b: 'b', f: { c: 'c', s: { d: 'd' } } } }) function myFun() { /** * triggerRef * 注意點: * + Vue3 只提供了 triggerRef 方法,沒有提供 triggerReactive 方法 * + 所以如果是 reactive 類型的數據,那么是無法主動觸發界面更新的 */
// state.value.a = 1
// state.value.gf.b = 2
state.value.gf.f.c = 3 state.value.gf.f.s.d = 4 // 如果想在修改該數據時,觸發界面的更新,可以調用 triggerRef 方法主動觸發
triggerRef(state) /** * shallowRef * 注意點: * + 如果是通過 shallowRef 創建數據,那么 Vue監聽的是 .value 的變化,因為底層本質上.value才是第一層 */
// state.value = {
// a: '1',
// gf: {
// b: '2',
// f: {
// c: '3',
// s: {
// d: '4'
// }
// }
// }
// }
console.log(state) console.log(state.value.gf) console.log(state.value.gf.f) console.log(state.value.gf.f.s) } return { state, myFun } } } </script>
7.toRef & toRefs、toRaw & markRaw
- ref->復制,將原始數據轉化為響應式,修改響應式數據不會影響原始數據(將原始數據深拷貝);數據發生改變,界面就會自動更新
- toRef->引用,將原始數據轉化為響應式,修改響應式數據會影響以前的數據(將原始數據淺拷貝);數據發生改變,界面也不會自動更新
- toRaw->toRaw(state.value),拿到響應式數據的原始數據,數據發生改變,界面也不會自動更新
- markRaw->被markRaw標記的原始數據,不會被追蹤變為響應式
- toRefs->解構
8.customRef函數
<template>
<div>
<p>{{age}}</p>
<button @click="myFun">按鈕</button>
</div>
</template>
<script>
/** * custemRef返回一個 ref 對象,可以顯式的控制依賴追蹤和觸發響應 */ import { ref, customRef } from 'vue'
function myRef(value) { return customRef((track, trigger) => { // track -> 追蹤 trigger -> 觸發
return { get() { track() // 告訴 Vue 這個數據是需要追蹤變化的
console.log('get', value) return value }, set(newValue) { console.log('set', newValue) value = newValue trigger() // 告訴 Vue 觸發界面更新
} } }) } export default { name: 'App', setup() { // let age = ref(18) // reactive({value: 18})
let age = myRef(18) function myFun() { age.value += 1 } return { age, myFun } } } </script>