vue3 學習筆記 -- 尚矽谷張天禹視訊
vue3 工程結構
- vue3,元件中的模版結構可以沒有根標籤div
// 引入的不再是Vue建構函式(通常大寫),引入的是一個名為createApp的工廠函式 import { createApp } from 'vue' import App from './App.vue' // 直接呼叫工廠函式,傳入外殼元件App // createApp(App).mount('#app') // 建立應用例項物件app,(類似於之前的Vue2的vm,但app比vm更輕 ) const app=createApp(App) // 掛載 app.mount('#app') // 解除安裝 setTimeout(()=>{ app.unmount('#app') },1000) // 以前的寫法。vue3不相容之前的寫法 // const vm=new Vue ( // { // render:h=>h(App) // } // ) // vm.$mount('#app')
拉開序幕的setup
- 是vue3中一個新的配置項(data,methods,computed等),值是一個函式
- 是所有composition api (組合api)的表演舞臺
- data,methods等都要配置再setup中
- 兩種返回值
- 若返回一個物件,則物件中的屬性,方法,在模版中均可以直接使用
- 弱返回一個渲染函式,則可以自定義渲染內容
- 儘量不要和vue2配置混合使用
- vue2 配置中可以訪問到setup中的屬性和方法
- 在setup中不能訪問到vue2配置(data,methods等)
- 如果有重名,setup優先
- setup不能是一個async函式,因為返回值不再是return物件了,而是promise,模版看不到return物件中的屬性。後期可以返回一個promise例項,但是需要suspense和非同步元件的配合。
ref函式
- 作用:定義一個響應式的資料
- 語法: const a=ref(b)
- 建立一個包含響應式資料引用物件(reference物件)
- js操作資料,a.value
- 模版中讀取資料,不需要.vule,直接使用變數名
- 接受的資料(b),可以是基本型別,可以是物件型別
- 基本型別的資料,響應式依然是依靠Object.defineProperty()的get和set完成的
- 物件型別的資料,內部求助了vue3的一個新函式,reactive函式
reactive函式
- 作用:定義一個物件型別的響應式資料(基本型別不要用它,要用ref函式)
- 語法:const 代理物件=reactive(源物件)接收一個物件(陣列),返回一個代理物件(Proxy的例項物件,簡稱proxy物件)
- reactive定義的響應式資料是深層次的
- 內部基於ES6的Proxy實現,通過代理物件操作源物件內部資料進行操作
vue2的響應式原理
- 實現原理
- 物件型別:通過Object.defineProperty()對屬性的讀取,修改進行攔截(資料劫持)
Object.defineProperty(data,'count',{
有人讀取時呼叫
get(){
return
},
有人修改時呼叫
set(){
return
}
}) - 陣列型別:通過重寫更新陣列的一系列方法來實現攔截。(對陣列的變更方法進行了包裹)
- 存在問題:
- 新增屬性,刪除屬性,介面不會更新。(set可以解決)
- 直接通過下標修改陣列,介面不會自動更新(set,splice可以解決)
vue3的響應式原理
-
通過Proxy(代理),攔截物件中任意屬性的變化,包括:屬性值的讀寫,屬性的新增,屬性的刪除
-
通過reflect(反射):對源物件的屬性進行操作
-
模擬vue3中實現響應式
const p=new Proxy(person,{
// 有人讀取p的某個屬性時呼叫
get(target,propName){
console.log(有人讀取了p身上的${propName}屬性,我要去更新介面了
)
// return target[propName]// 使用reflect方式 return Reflect.get(target,prop)
},
// 有人修改p的某個屬性,或者增加一個屬性,跳用
set(target,propName,value){
console.log(有人修改了p身上的${propName}屬性,我要去更新介面了
)
// target[propName]=valuereturn Reflect.set(target,prop,value)
},
// 有人刪除的某個屬性時呼叫
deleteProperty(target,propName){
console.log(有人刪除了p身上的${propName}屬性,我要去更新介面了
)
// return delete target[propName]return Reflect.deleteProperty(target,prop)
}
})reactive 對比ref
- 從定義資料角度
- ref用來定義,基本資料型別
- reactive用來定義,物件和陣列資料型別
- ref也可以用來定義物件和陣列,它內部會自動通過reactive轉為代理物件
- 從原理角度對比
- ref通過Object.defineProperty()的get和set來實現響應式(資料劫持)
- reactive通過使用Proxy來實現響應式(資料劫持),並且通過Reflect操作源物件內部的資料
- 從使用角度對比
- ref定義的資料:操作資料需要.value,模版中直接讀取不需要.value
- reactive定義的資料:操作資料和讀取資料都不要.value
setup的注意點
- setup執行的時機,在beforeCreate之前執行一次,this是undefined
- setup的引數
- props:值是物件,包含元件外部傳進來的同時元件內部接收了的屬性
- context:上下文物件
- attrs:值是物件,包含元件外部傳遞過來,但沒有在props配置中宣告的屬性,相當於this.$attrs
- slots:收到的插槽內容,相當於this.$slots
- emit:分發自定義事件的函式,相當於this.$emit
computed
- 和vue2配置功能一致
- 寫法
import { reactive,computed } from 'vue' // 簡寫 person.fullName=computed(()=>{ return person.firstName+'-'+person.lastName }) // 完整寫法 person.fullName=computed({ get(){ return person.firstName+'-'+person.lastName }, set(value){ let nameArr=value.split('-') person.firstName=nameArr[0] person.lastName=nameArr[1] } })
watch函式
- 和vue2配置功能一致
- 寫法
// 監視一個ref資料 watch(sum,(newvalue,oldvalue)=>{ console.log(newvalue,oldvalue) },{immediate:true}) // 監視多個ref資料 watch([sum,msg],(newvalue,oldvalue)=>{ console.log(newvalue,oldvalue) },{immediate:true}) // 監視reactive資料,全部屬性。無法正確的獲取oldvalue。強制開啟了深度監視,deep配置無效 watch(person,(newvalue,oldvalue)=>{ console.log(newvalue,oldvalue) },{immediate:true}) // 監視reactive資料,一個屬性。 watch(()=>person.age,(newvalue,oldvalue)=>{ console.log(newvalue,oldvalue) },{immediate:true}) // 監視reactive資料,多個屬性。 watch([()=>person.age,()=>person.name],(newvalue,oldvalue)=>{ console.log(newvalue,oldvalue) },{immediate:true}) // 監視reactive資料,這個屬性是物件。這是開啟deep配置有效 watch([()=>person.job,()=>person.name],(newvalue,oldvalue)=>{ console.log(newvalue,oldvalue) },{immediate:true,deep:true})
watch監視ref定義的物件
- 加.value,監視的就是物件裡面的資料,
- 不加.value,監視的就是物件的地址,如果要監視資料,就要加deep:true
watchEffect函式
- watch:既要指明監視啥,也要指明回撥函式
- watchEffect:不用指明監視啥,回撥函式中用到啥,就是監視的啥
- 類似computed,但computed側重計算返回的值必須寫返回值,watchEffect側重執行回撥不用寫返回值
watchEffect(()=>{ const x1=sum.value; const x2=person.age console.log(1111) })
vue3生命週期鉤子
-
通過配置項的形式使用生命週期鉤子
beforeCreate(){
console.log('beforeCreate');
},
created(){
console.log('created');
},
beforeMount(){
console.log('beforeMount');
},
mounted(){
console.log('Mounted');
},
beforeUpdate(){
console.log('beforeUpdate');
},
updated(){
console.log('updated');
},
beforeUnmount(){
console.log('beforeUnmount');
},
unmounted(){
console.log('unmounted');
} -
通過組合式api的形式,用哪個就引入哪個,
beforeCreate => setup
created => setup
beforeMount => onBeforeMount
mounted => onMounted
beforeUpdate => onBeforeUpdate
updated => onUpdated
beforeUnmount => onBeforeUnmount
unmounted => onUnmounted
import {onBeforeMount} from 'vue'
放在setup函式中
onBeforeMount(()=>{
console.log(111);
})自定義hook函式
- hook,本質是一個函式,把setup函式中使用的組合式api進行了封裝
- 類似於vue2的mixin
- 優勢,複用程式碼,讓setup中的邏輯更清楚易懂
toRef和toRefs
- 作用:建立一個ref物件,其value值指向另一個物件中的某個屬性
- 語法
name:toRef(person,'name'), age:toRef(person,'age'), salary:toRef(person.job.jobs,'salary'), ...toRefs(person)
- 應用:要將響應式物件中的某個屬性單獨提供給外部使用
- toRef功能一致,可以批量建立多個ref物件
shallowReactive和shallowRef
- shallowReactive 只處理物件最外層屬性的響應式(淺響應式)。如果一個數據結構比較深,變化只是最外層屬性,就用shallowReactive。
- shallowRef 只處理基本資料型別的響應式,不進行物件的響應式處理。如果一個物件資料,不會修改屬性,而是生成新的物件來替換,就用shallowRef。
readonly 和 shallowReadonly
- readonly 讓一個響應式資料變成只讀的,深只讀的
- shallowReadonly 讓一個響應式資料變成只讀的,淺只讀的,第一層屬性是隻讀的
- 應用場景是不希望資料被修改
person =readonly(person)
toRaw 和 markRaw
- toRaw
- 作用:將一個由reactive生成的響應式物件轉為普通物件
- 使用場景:用於讀取響應式物件對應的普通物件,對這個普通物件的所有操作,不會引起頁面更新
- markRaw
- 作用:標記一個物件,使其永遠不會再成為響應式物件
- 應用場景:有些值不應該被設定為響應式的,比如第三方庫。當渲染不會變化的大列表,跳過響應式轉換可以提高效能
const p=toRaw(person)
customRef
- 作用:建立一個自定義的ref,並對其依賴項和更新觸發進行顯式控制
- 實現防抖效果,eg
setup(){ function myRef(value,delay){ let timer return customRef((track,trigger)=>{ return { get(){ track() return value }, set(newValue){ clearTimeout(timer) timer=setTimeout(()=>{ value=newValue trigger() },delay) } } }) } let keyWord=myRef('hello',500)
provide 和 inject
- 作用:實現祖和後代元件的通訊
- 父元件用provide提供資料,後代元件使用inject來接收資料
import {provide,inject} from 'vue' provide('car',car) const car=inject('car')
響應式資料的判斷
- isRef 檢查一個值是否是一個ref物件
- isReactive 檢查一個物件是否由reactive建立的響應式代理
- isReadonly 檢查一個物件是否是由readonly建立的只讀代理
- isProxy 堅持一個物件是否由reactive或者readonly建立的proxy代理
傳統Options API 存在的問題
- 新增或者修改一個需求,就需要分別在data,methods,computed裡面修改
composition API的優勢
- 更優雅的組織程式碼,讓相關功能的程式碼更有序的組織在一起
fragment
- vue2 元件必須有個根標籤
- vue3 元件可以沒有根標籤,多個標籤包含在一個fragment虛擬元素中
- 減少標籤層級,減少記憶體佔用
teleport
- 能將我們的元件移動到指定的位置
- body 是移動到的位置,可以是#id,可以是標籤名
<teleport to='body'> <div> </div> </teleport>
suspense
- 等待非同步元件或者網速慢時渲染一些額外內容,提高使用者體驗
- 非同步引入元件
import {defineAsyncComponent} from 'vue' const Child = defineAsyncComponent(()=>import('./components/Child.vue'))
- 使用suspense包裹元件,並配置好default和fallback
<Suspense> <template> <Child/> </template> <template> <h3>載入中。。。。。</h3> </template> </Suspense>
vue3 對全域性api的調整app上了
- Vue.config app.config
- Vue.config.productionTip 移除
- Vue.component app.component
- Vue.directive app. directive
- Vue.mixin app.use
- Vue.use app.use
- Vue.prototype app.config.globalProperties
vue3 其他改變
- data始終被宣告為一個函式
- 過度類名的更改
.v-enter 要改為 .v-enter-from - 移除keyCode作為v-on的修飾符,比如@keyup.13
- 不再支援config.keyCodes
- 移除v-on.native修飾符
移除過濾器filter
- filter需要自定義語法,有學習成本和實現成本
- 建議使用方法或者計算屬性替代