新聞中心
寫在前面
在體驗Vue3之前,我們先來了解一下Vu3到底有哪些亮點之處。

創(chuàng)新互聯(lián)成立于2013年,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務公司,擁有項目網(wǎng)站建設、網(wǎng)站制作網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元高港做網(wǎng)站,已為上家服務,為高港各地企業(yè)和個人服務,聯(lián)系電話:18980820575
總共有6大特點:
- Performance(性能比vue2的runtime快2倍左右)
- Tree shaking support(按需打包模塊)
- Better TypeScript support(更好的TS代碼支持)
- Composition API(組合API)
- Custom Renderer API(自定義渲染API)
- Fragment, Teleport, Suspense
一、vue3的起源setup
1.理解:vue3.0的組合api舞臺是setup
2.setup所有的數(shù)據(jù)方法等都配置在setup中
3.setup有兩種返回值:
- 若返回一個對象,則對象中的屬性、方法,在模板中均可直接使用
- 若返回一個渲染函數(shù),則可以自定義渲染內(nèi)容
4.注意:
- vue3.0盡量不要和vue2的配置混用
- vue2配置(data、methods、computed等)是可以訪問vue3的setup屬性方法
- setup中不可訪問vue2的配置(data、methods、computed等)
- 如果重名,則vue3為準
5.setup不能是一個async函數(shù),因為返回值不再是return的對象,而是promise,模板看不到return對象中的屬性(后期也可以使用suspense和異步組件的配合得到promise)
二、ref函數(shù)
作用:用于定義一個響應式數(shù)據(jù)
語法:const name = ref(initValue)
- 創(chuàng)建一個包含響應式數(shù)據(jù)的引用對象(reference對象,簡稱ref對象)
- js中操作數(shù)據(jù):name.value
- 模板中讀取數(shù)據(jù):不需要value,直接{{name}}
注意:
- 接受的數(shù)據(jù):基本類型、對象類型
- 基本數(shù)據(jù)類型:響應式是靠Object.defineProperty()的get與set進行設置和取值操作
- 對象類型的數(shù)據(jù):內(nèi)部使用了vue3中的新函數(shù)--reactive函數(shù)(其實就是proxy代理)
三、reactive函數(shù)
- 作用:用于定義一個對象類型的響應式數(shù)據(jù)(基本類型不要用它,要用ref函數(shù))
- 語法:const 代理對象(proxy) = reactive(源對象)接收一個對象或數(shù)組,返回一個代理對象(proxy的實例對象,簡稱proxy對象)
- reactive定義的響應式是『深層次的』,對象中的所有對象都是響應式的
- 內(nèi)部基于es6的proxy實現(xiàn),通過代理對象內(nèi)部數(shù)據(jù)進行操作
- 經(jīng)過reactive處理的對象是不能使用解構(gòu)語法的,因為會失去響應式特性,可以使用toRefs將reactive每個屬性都轉(zhuǎn)成ref響應式,這樣就可以進行響應式處理。
ref和reactive的差異
reactive就是用于將對象數(shù)據(jù)轉(zhuǎn)為響應式,與vue2的observable類似,而ref用于獲得單獨或為基礎數(shù)據(jù)類型轉(zhuǎn)為響應式。
為什么在vue3中會有兩個將數(shù)據(jù)轉(zhuǎn)為響應式數(shù)據(jù)的api,我們來進行詳細說明:
- ref接受一個內(nèi)部值并返回一個響應式且可變的ref對象,對象具有指向內(nèi)部值的單個property.value
- reactive接受一個對象,并返回對象的響應式副本,其將對象中的每個元素都轉(zhuǎn)為ref對象
簡而言之,reactive可以將對象數(shù)據(jù)轉(zhuǎn)為響應式,可以將對象中的每個元素都轉(zhuǎn)為ref響應式數(shù)據(jù);而ref不僅可以將對象數(shù)據(jù)轉(zhuǎn)為響應式,還可以處理基礎數(shù)據(jù)類型(string、boolean等)。
之所以會有此差異,那時因為vue3中的響應式是基于proxy實現(xiàn)的,而對于proxy的target必須是引用數(shù)據(jù)類型,即存放在堆內(nèi)存中通過指針進行引用的對象。為什么是代理的引用數(shù)據(jù)類型,那是因為簡單數(shù)據(jù)類型每次賦值都是全新的數(shù)據(jù),根本無法進行代理,因此難以實現(xiàn)簡單數(shù)據(jù)類型的響應式。
如果我們想要獲取簡單數(shù)據(jù)類型的響應式,應該如何做呢?
vue3中也考慮到這點,可以通過ref進行簡單數(shù)據(jù)類型的響應式處理。ref通過創(chuàng)建內(nèi)部狀態(tài),將值掛到value上,因此ref生成的對象要通過.value獲取使用。而重寫get/set獲得的監(jiān)聽,同時對對象的處理也依賴了reactive的實現(xiàn)。
正是如此,ref既可以處理簡單數(shù)據(jù)類型,又可以將引用數(shù)據(jù)類型進行響應式處理。在實踐中,我們應該避免將reactive當作vue2的data在頂部將所有變量進行聲明,而是應該結(jié)合具體的應用和邏輯進行就近聲明。
- class RefImpl
{ - private _value: T
- public readonly __v_isRef = true
- constructor(private _rawValue: T, public readonly _shallow = false) {
- this._value = _shallow ? _rawValue : convert(_rawValue)
- }
- get value() {
- track(toRaw(this), TrackOpTypes.GET, 'value')
- return this._value
- }
- set value(newVal) {
- if (hasChanged(toRaw(newVal), this._rawValue)) {
- this._rawValue = newVal
- this._value = this._shallow ? newVal : convert(newVal)
- trigger(toRaw(this), TriggerOpTypes.SET, 'value', newVal)
- }
- }
- }
- ...
- const convert =
(val: T): T => - isObject(val) ? reactive(val) : val
- ...
四、Vue3中的響應式原理
vue2的響應式原理:
- 對象類型:通過Object.defineProperty()對屬性的讀取、修改進行攔截(數(shù)據(jù)劫持)
- 數(shù)組類型:通過重寫更新數(shù)組的一系列方法來實現(xiàn)攔截。(對數(shù)組的變更方法進行包裹)
- Object.defineProperty(data,"count",{
- get(){},
- set(){}
- })
存在問題:
- 新增屬性、刪除屬性,界面不會同步更新
- 直接通過下表修改數(shù)組,界面不會自動更新
vue3的響應式原理
- 通過proxy代理:攔截對象中任意屬性的變化,包括:屬性值的讀寫、屬性值的添加、刪除等
- 通過reflect反射:對被代理對象的屬性進行操作
五、reactive和ref的差異
定義數(shù)據(jù)
- ref:用于定義基本數(shù)據(jù)類型
- reactive:用于定義引用數(shù)據(jù)類型
- 注意:ref也可以用于定義引用數(shù)據(jù)類型,內(nèi)部會自動通過ractive轉(zhuǎn)為代理對象
原理
- ref:通過Object.defineProperty()的set和get屬性實現(xiàn)響應式(數(shù)據(jù)劫持)
- reactive:通過Proxy來實現(xiàn)響應式(數(shù)據(jù)劫持),并通過Reflect操作愿對象內(nèi)部的數(shù)據(jù)
使用
- ref:ref定義的數(shù)據(jù)需要通過.value進行操作,模板讀取數(shù)據(jù)時不需要使用.value
- reactive:reactive定義的數(shù)據(jù)無需.value,即可進行設置和讀取
六、計算屬性和監(jiān)視
與vue2中的computed配置功能一致
寫法:
- import {computed} from "vue";
- setup(){
- const sum = computed(()=>{
- return num1 + num2;
- })
- }
- import {computed,reactive} from "vue"
- setup(){
- const person = reactive({
- firstName:"wen",
- lastName:"bo"
- })
- //簡寫
- let fullName = computed(()=>{
- return person.firstName + "-" + person.lastName;
- });
- //完整
- let fullName = computed(()=>{
- get(){
- return person.firstName + "-" + person.lastName;
- },
- set(value){
- const newArr = value.split("-");
- person.firstName = nameArr[0];
- person.lastName = nameArr[1];
- }
- })
- }
七、watch函數(shù)
與vue2中的watch配置功能一致
注意:
- 監(jiān)視reactive定義的響應式數(shù)據(jù)時,oldValue無法正確獲取,強制開啟了深度監(jiān)視(deep配置失敗)
- 監(jiān)視reactive定義的響應式數(shù)據(jù)中某個屬性時,deep配置有效
- let person = reactive({
- name:"wenbo",
- age:18,
- job:{
- job1:{
- salary:20
- }
- }
- })
- // 情況一:監(jiān)視ref定義的響應式數(shù)據(jù)
- watch(sum,(newValue,oldValue)=>{
- console.log("sum變了",newValue,oldValue);
- },{immediate:true})
- // 情況二:監(jiān)視多個ref定義的響應式數(shù)據(jù)
- watch([sum,msg],(newValue,oldValue)=>{
- console.log("sum或msg變了",newValue,oldValue);
- })
- /*情況三:監(jiān)視reactive定義的響應式數(shù)據(jù)
- 若watch監(jiān)視的是reactive定義的響應式數(shù)據(jù),則無法正確獲得oldValue
- 若watch監(jiān)視的是reactive定義的響應式數(shù)據(jù),則強制開啟了深度監(jiān)視
- 此時的deep不生效
- */
- watch(person,(newValue,oldValue)=>{
- console.log("perosn變了",newValue,oldValue);
- },{immediate:true,deep:false})
- //情況四:監(jiān)視reactive所定義的一個響應式數(shù)據(jù)中的某個屬性
- watch(()=>person.age,(newValue,oldValue)=>{
- console.log("person的age變化了",newValue,oldValue)
- })
- //情況五:監(jiān)視reactive所定義的一個響應式數(shù)據(jù)中的某些屬性
- watch([()=>person.name,()=>person.age],(newValue,oldValue)=>{
- console.log("person的name或age變化了",newValue,oldValue)
- })
- //特殊情況-監(jiān)聽的對象的屬性還是個對象,此時deep生效,可以進行深度監(jiān)聽
- watch(()=>person.job,(newValue,oldValue)=>{
- console.log("person的job變化了",newValue,oldValue)
- },{deep:true})
注意:
watch監(jiān)聽reactive定義的對象有五種情況
watch監(jiān)聽ref定義的響應式數(shù)據(jù)有兩種情況:
- ref定義的是基本類型數(shù)據(jù),此時不能對.value進行監(jiān)聽。如const num = ref(0),此時對num.value監(jiān)聽的話相當于直接對num的值(0)進行監(jiān)聽了,想想0有啥變化的。
- ref定義的是對象數(shù)據(jù)類型,此時ref處理后的數(shù)據(jù)是ref實例的。要監(jiān)聽對象的變化,需要對對象的.value進行監(jiān)聽,因為ref對象.value是借助reactive處理的響應式數(shù)據(jù)proxy。
八、watchEffect函數(shù)
watch的套路:既要指明監(jiān)視的屬性,又要指明監(jiān)視的回調(diào)
watchEffect的套路:不用指明監(jiān)視的哪個屬性,監(jiān)視的回調(diào)中用到哪個屬性,就監(jiān)視哪個屬性
watchEffect有點類似computed:
- computed注重的是計算的值,即回調(diào)函數(shù)的返回值,所以必須要寫返回值
- watchEffect注重的是計算的過程,即回調(diào)函數(shù)的函數(shù)題,所以不用寫返回值
- let person = reactive({
- name:"wenbo",
- age:18,
- job:{
- job1:{
- salary:20
- }
- }
- })
- // warchEffect所指定的回調(diào)中用到的數(shù)據(jù)只要發(fā)生變化,則直接重新執(zhí)行回調(diào)
- watchEffect(()=>{
- const x = person.name
- const y = person.job.job1.salary
- console.log("watchEffect觸發(fā)了")
- })
九、自定義hooks函數(shù)
- hook函數(shù):本質(zhì)上是一個函數(shù),把setup函數(shù)中是用的組合api進行封裝
- 類似于vue2中mixins
- 優(yōu)點:復用代碼、使得setup中的邏輯更清晰易懂
pageX:{{point.pageX}},pageY:{{point.pageY}}hooks文件 usePoint.js
- import {reactive} from "vue"
- const handleClickPoint = ()=>{
- //實現(xiàn)鼠標“打點”相關(guān)數(shù)據(jù)
- let point = reactive({
- pageX:0,
- pagey:0
- })
- //實現(xiàn)鼠標“打點”相關(guān)方法
- const handlePoint = (event)=>{
- point.pageX = event.pageX;
- point.pageY = event.pageY;
- console.log(event.pageX,event.pageY)
- }
- //實現(xiàn)鼠標打點的相關(guān)周期函數(shù)
- onMounted(()=>{
- window.addEventListener("click",handlePoint)
- })
- onBeforeUnmounted(()=>{
- window.removeEventListener("click",handlePoint)
- })
- }
十、toRef函數(shù)
- 經(jīng)過ref、reactive處理的對象不能直接使用解構(gòu)進行處理,否則就會失去響應式特性
- 作用:創(chuàng)建一個ref對象,其value值指向另一個對象中的某個屬性
- 語法:const name = toRef(person,"name")
- 應用:要將響應式對象中的某個屬性單獨提供給外部使用時
- 擴展:toRefs與toRef功能一致,但是可以批量創(chuàng)建多個ref對象,語法:toRefs(person)
十一、VUE3生命周期
其它組合式api
1. shallowReactive與shallowRef
shallowReactive:只處理對象最外層屬性的響應式(淺響應式)
shallowRef:只處理基本數(shù)據(jù)類型的響應式,不進行對象的響應式處理
使用時機:
- 如果有一個對象數(shù)據(jù),結(jié)構(gòu)比較深,但變化時只是外層屬性變化==》shallowReactive
- 如果有一個對象數(shù)據(jù),后續(xù)功能不會修改該對象中的屬性,而是生成新的對象來替換==》shallowRef
2. readonly與shallowReadonly
- readonly:讓一個響應式數(shù)據(jù)變?yōu)橹蛔x數(shù)據(jù)(深只讀)
- shallowReadonly:讓一個響應式數(shù)據(jù)變?yōu)橹蛔x(淺只讀)
- 應用場景:不希望數(shù)據(jù)被修改時
3. toRaw與markRaw
toRaw
- 作用:將一個由reactive生成的響應式對象轉(zhuǎn)為普通對象
- 使用場景:用于讀取響應式對象對應的普通對象,對這個普通對象的所有操作,不會引起頁面的更新
markRaw
- 有些值不應該被設置為響應式的,例如:復雜的第三方庫
- 當渲染具有不可變數(shù)據(jù)源的大列表時,跳過響應式轉(zhuǎn)換可以提高性能
- 作用:標記一個對象,使其永遠不會稱為響應式對象
- 應用場景:
4. customRef
作用:創(chuàng)建一個自定義ref,并對其依賴項跟蹤和更新觸發(fā)進行顯式控制
示例:
當前的值:{{str}}
5. provide與inject
- 作用:實現(xiàn)祖孫組件間的通信
- 應用場景:父組件有一個provide選項提供數(shù)據(jù),子組件有一個inject來獲取使用數(shù)據(jù)
6. 響應式數(shù)據(jù)的判斷
- isRef:檢查一個值是否為ref對象
- isReactive:檢查一個值是否為reactive創(chuàng)建的響應式代理
- isReadonly:檢查一個對象是否是由readonly創(chuàng)建的只讀代理
- isProxy:檢查一個對象是否是由reactive或readonly創(chuàng)建的代理
vue3中使用hook注意點
hook中存在異步問題
其實在使用中可以發(fā)現(xiàn),其實hook本質(zhì)上就是進行抽取的函數(shù),靈活性比較高,但是在涉及到異步邏輯時考慮不周全就會導致很多問題。hook是可以覆蓋異步情況的,但是必HTML須在setup當中執(zhí)行時返回有效對象不能被阻塞。
通常異步具有兩種風格:
外部沒有其它依賴,只是交付渲染的響應變量。對于此種情況,可以通過聲明、對外暴露響應變量,在hook中使用異步修改的方式。
外部具有依賴,需要在使用側(cè)進行加工。對于此種情形,可以通過對外暴露Promise的方式,使得外部獲取到同步操作的能力。
this的問題
由于setup處于生命周期的beforeCreate和created階段之間,因此不能獲取到this。當然我們可以通過setup的第二個參數(shù)context獲取到類似與this的部分能力,但是對于想操作路由、vuex等能力則受到了限制,為此最新的router@4、vuex@4都提供了組合式的api。
由于vue2底層限制使得無法使用這些hook,為此可以通過引用實例的方式獲取一定的操作能力,也可以通過getCurrentInstance獲得組件實例上掛載的對象。
雖然組合式api的響應式原理是通過Object.defineProperty改寫屬性的,與vue的響應式原理相同,但是在具體實現(xiàn)方式上存在差異,因此在setup中與vue原生的響應式并不互通。正因此,導致即使拿到相應的實例也沒有辦法監(jiān)聽它們的響應式,只能通過在選項配置進行使用。
vue3中常見的組件
1. Fragment組件
在vue2中:組件必須有一個根標簽
在vue3中:組件可以沒有根標簽,內(nèi)部會將多個標簽包裹在Fragment虛擬元素中
好處:減少標簽層級,減少內(nèi)存占用
2. Teleport
Teleport時倚重能夠?qū)⒔M件html結(jié)構(gòu)移動到指定位置的技術(shù)
語法:
main.vue
modal.vue
this is a modal 3. suspense
等待異步組件時渲染一些額外內(nèi)容,讓應用有更好的用戶體驗。
使用步驟:
- 異步引入組件
- 使用suspense包裹組件,并配置好default與fallback。
- import {defineAsyncComponent} from "vue"
- const child =defineAsyncComponent(()=>import("./components/child.vue"))
【編輯推薦】
- 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)
- 云中的容器:你有哪些選擇?
- 5G消息全力邁入發(fā)展期
- 什么是域名劫持?遇到域名劫持要怎么處理
- 自學編程,到底先選什么語言?
- 一文看完網(wǎng)絡爬蟲發(fā)展史
當前標題:一網(wǎng)打盡──Vue3Composition-api新特性
文章起源:http://m.fisionsoft.com.cn/article/djjeges.html


咨詢
建站咨詢
