<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          vue3之糟糕透頂Api——Reactivity

          共 9983字,需瀏覽 20分鐘

           ·

          2021-06-06 12:16



          前端獵手
           鏈接每一位開發(fā)者,讓編程更有趣兒!
          關(guān)注

          Reactivity可以說是vue3中最復(fù)雜的一個(gè)地方,當(dāng)然也是功能最強(qiáng)大的一個(gè)點(diǎn),聽起來是塊硬骨頭哈??,這讓我想起亮劍中李云龍面對(duì)小日本挑釁說的話:就是閻王爺來了,我也得摟他幾根胡子下來。那么今天咱們也摟摟Reactivity的胡子。

          在vue3當(dāng)中,它是把數(shù)據(jù)響應(yīng)式Api全都暴露出來了,在vue2中是沒有這樣做的,那vue2是怎么做的呢?它是將數(shù)據(jù)配置到data當(dāng)中,data中的數(shù)據(jù)會(huì)自動(dòng)變成響應(yīng)式數(shù)據(jù),我們把這個(gè)過程叫做注入,它會(huì)注入到組件實(shí)例當(dāng)中去,如下:

          {
              //data中的數(shù)據(jù)都是響應(yīng)式的,會(huì)被注入到組件實(shí)例當(dāng)中
              data(){
                  return{
                      name:"法醫(yī)",
                      idol:"喬布斯",
                      publicNumber:"前端獵手"
                  }
              }
          }

          然而在vue3中,它不再是data了,而是setup函數(shù),我們把它稱之為composition Api,我們要在setup函數(shù)中使用響應(yīng)式數(shù)據(jù),就不可避免需要暴露出響應(yīng)式的Api供我們使用,接下來,我們來看看vue3提供了哪些跟數(shù)據(jù)響應(yīng)式相關(guān)的函數(shù)。

          ?? 獲取響應(yīng)式數(shù)據(jù)

          可以獲取響應(yīng)式數(shù)據(jù)的api:

          • reactive

            這個(gè)api會(huì)傳入一個(gè)對(duì)象,然后返回一個(gè)對(duì)象代理proxy,并且它是可以深度代理對(duì)象中的所有成員的,也就是說對(duì)象里面再嵌套一個(gè)對(duì)象,也是響應(yīng)式的數(shù)據(jù)

            「舉個(gè)栗子??:」

            import { reactive } from 'vue'
            const state = reactive({name:"法醫(yī)",age:18})
            window.state = state;

            「效果展示:」

            現(xiàn)在這個(gè)對(duì)象就變成響應(yīng)式的了,對(duì)對(duì)象進(jìn)行操作,vue就可以收到通知了

          • readonly

            這個(gè)api需要傳入一個(gè)對(duì)象或者是一個(gè)代理,同樣也會(huì)返回一個(gè)對(duì)象代理,它只能讀取代理對(duì)象中的成員,而不能修改,也就是只能get,不可以set,它也是可以深度代理對(duì)象中所有成員的。

            「舉個(gè)栗子??:」

            import { reactive,readonly } from 'vue'
            const imState = readonly({name:"法醫(yī)",age:18})
            window.imState = imState;

            「效果展示:」

            看見沒,報(bào)了一個(gè)錯(cuò)誤,提示說要修改的目標(biāo)是只讀的,修改失敗

            剛才有說readonly可以傳一個(gè)對(duì)象,也可以傳一個(gè)代理,我們?cè)倏纯磦饕粋€(gè)代理會(huì)怎么樣

            import { reactive,readonly } from 'vue'
            const state = reactive({name:"法醫(yī)",age:18})
            const imState = readonly(state);//傳一個(gè)代理進(jìn)去
            window.imState = imState;

            「效果展示:」

            傳一個(gè)代理的時(shí)候也是不可以進(jìn)行賦值操作的,反正只要經(jīng)過readonly后就只能讀不能賦值,但是,我們可以在readonly之前修改值,然后讓它進(jìn)行代理就可以了。

            ??注意:reactivereadonly,這兩個(gè)api是代理對(duì)象的,是沒辦法代理普通值的,會(huì)報(bào)錯(cuò)

            「舉個(gè)栗子??:」

            import { reactive,readonly } from 'vue'
            const state = reactive("法醫(yī)")

            「效果展示:」

            報(bào)了一個(gè)警告,原始值是沒法代理的,那么如果需要代理普通值,那該咋辦呢?那就要用到refapi了

          • ref

            ref中可以傳入任何數(shù)據(jù),最終會(huì)將數(shù)據(jù)放到一個(gè)對(duì)象中的value中,比如這種{ value : ...},對(duì)value的訪問是響應(yīng)式的,如果給value的值是一個(gè)對(duì)象的話,它會(huì)通過reactive函數(shù)進(jìn)行代理

            「舉個(gè)栗子??:」

            import { reactive,readonly,ref } from 'vue'
            const state = ref({name:"法醫(yī)",age:18})
            window.state = state;

            「效果展示:」

            還有一種情況是:如果說傳入的value值已經(jīng)是代理了,那么會(huì)直接使用代理

            「舉個(gè)栗子??:」

            import { reactive,readonly,ref } from 'vue'
            const state = reactive({name:"法醫(yī)",age:18});
            const imState = ref(state)
            window.imState = imState;

            「效果展示:」

          • computed

            computed需要傳入一個(gè)函數(shù)function,它返回的值跟ref一樣,也是{value:...},當(dāng)讀取value值的時(shí)候,它會(huì)根據(jù)情況決定是否要運(yùn)行函數(shù),這個(gè)情況就是有沒有用到這個(gè)函數(shù),并且它是有緩存的,當(dāng)依賴的響應(yīng)式數(shù)據(jù)沒有變化的時(shí)候,拿到的是緩存里面的值,只有當(dāng)state.name或者state.age發(fā)生改變時(shí)才會(huì)重新運(yùn)行。

            「舉個(gè)栗子??:」

            import { reactive,readonly,ref ,computed} from 'vue'
            const state = reactive({name:"法醫(yī)",age:18})

            const result = computed(()=>{
                console.log("computed");
                return state.name + state.age
            })
            console.log(result.value);

            「效果展示:」

            反正以上四個(gè)api不管它怎么進(jìn)行處理,就一個(gè)目的,那就是把數(shù)據(jù)變成響應(yīng)式數(shù)據(jù)。

            那么在開發(fā)中到底用哪個(gè)?

            ?? 注意:用ref的時(shí)候要拿到數(shù)據(jù)必須是ref.value哈,不然拿不到,切記!??

            1. 如果想要一個(gè)對(duì)象變?yōu)轫憫?yīng)式數(shù)據(jù),可以使用reactive或者ref
            2. 如果說讓對(duì)象所有屬性只能讀,就用readonly
            3. 如果想讓一個(gè)非對(duì)象數(shù)據(jù)變成響應(yīng)式數(shù)據(jù),就用ref
            4. 如果想要根據(jù)已知的響應(yīng)式數(shù)據(jù)得到一個(gè)新的響應(yīng)式數(shù)據(jù),就用computed

          ?? 監(jiān)聽數(shù)據(jù)變化

          • watchEffect
          const stop = watchEffect(() => {
              //watchEffect 函數(shù)會(huì)立即執(zhí)行,然后監(jiān)聽函數(shù)中會(huì)用到的響應(yīng)式數(shù)據(jù),響應(yīng)式數(shù)據(jù)變化后會(huì)再次執(zhí)行
          })

          //通過調(diào)用stop函數(shù)就會(huì)停止監(jiān)聽
          stop();//停止監(jiān)聽

          「舉個(gè)栗子??:」

            import { reactive, ref, watchEffect } from "vue";

            const state = reactive({a1,b2,});
            const count = ref(0);

            watchEffect(() =>{
                console.log(state.a,count.value);//先會(huì)立即執(zhí)行一次
            })
            
            state.a++;//這里依賴數(shù)據(jù)改變?cè)俅螆?zhí)行一次

          「效果展示:」

          watchEffect函數(shù)之所以知道依賴變化了,是因?yàn)槔锩娴臄?shù)據(jù)是響應(yīng)式的,當(dāng)讀取數(shù)據(jù)的時(shí)候用的是get方法,get會(huì)收集依賴。還要注意的是如果說依賴的數(shù)據(jù)同時(shí)改變很多次,最終結(jié)果是會(huì)顯示一次,因?yàn)檫\(yùn)行過程是異步的,是會(huì)到微隊(duì)列中執(zhí)行的,等數(shù)據(jù)變完之后才會(huì)運(yùn)行,如下例子:

          「舉個(gè)栗子??:」

            import { reactive, ref, watchEffect } from "vue";

            const state = reactive({a1,b2,});
            const count = ref(0);

            watchEffect(() =>{
                console.log(state.a,count.value);
            })
            //運(yùn)行多次
            state.a++;
            state.a++;
            state.a++;
            state.a++;
            state.a++;
            state.a++;
            state.a++;
            state.a++;

          「效果展示:」

          從最終結(jié)果可以看出最終只運(yùn)行兩次,一次是立即執(zhí)行,第二次是數(shù)據(jù)改變后

          • watch

          這個(gè)watch相當(dāng)于vue2的$watch,這個(gè)watch有點(diǎn)麻煩,因?yàn)樗枰謩?dòng)去指定監(jiān)控哪些值的變化,當(dāng)變化的時(shí)候,它會(huì)把新的值舊的值同時(shí)給你

          「舉個(gè)栗子??:」

            import { reactive, ref, watch } from "vue";

            const state = reactive({a1,b2,});
            const count = ref(0);

            watch(()=>state.a,(newValue,oldValue) =>{
                console.log("新值",newValue,"舊值",oldValue);
            })
            state.a++;//修改依賴

          「效果展示:」

          ?? 注意:在這里需要注意的是watchwatchEffect不同的是watch不會(huì)立即運(yùn)行函數(shù),只有當(dāng)依賴的值改變時(shí)才會(huì)執(zhí)行,watch中會(huì)傳兩個(gè)參數(shù),在上面例子中傳的是()=>state.a,那為什么不直接傳state.a呢?如果直接傳state.a的話,那么相當(dāng)于傳了一個(gè)1進(jìn)去,這樣數(shù)據(jù)就不是響應(yīng)式的了,如下:

          「舉個(gè)栗子??:」 直接傳state.a

            import { reactive, ref, watch } from "vue";

            const state = reactive({a1,b2,});
            const count = ref(0);

            watch(state.a,(newValue,oldValue) =>{
                console.log("新值",newValue,"舊值",oldValue);
            })
            state.a++;//修改依賴

          「效果展示:」

          如果直接傳state.a,這里就會(huì)報(bào)警告,翻譯過來是:「無效的監(jiān)視源:1監(jiān)視源只能是getter/effect函數(shù)、ref、被動(dòng)對(duì)象或這些類型的數(shù)組」,意思就是說這里參數(shù)只能是響應(yīng)式數(shù)據(jù)。

          當(dāng)傳一個(gè)()=>state.a函數(shù)進(jìn)去,它是在watch里面運(yùn)行的,這樣就會(huì)收集依賴。當(dāng)使用reactiveapi的時(shí)候就要傳一個(gè)這樣函數(shù)進(jìn)去,當(dāng)使用refapi的時(shí)候,watch中第一個(gè)參數(shù)可以寫成count,因?yàn)閏ount是一個(gè)對(duì)象,如下:

          「舉個(gè)栗子??:」 直接傳count

            import { reactive, ref, watch } from "vue";

            const state = reactive({a1,b2,});
            const count = ref(0);

            watch(count,(newValue,oldValue) =>{
                console.log("新值",newValue,"舊值",oldValue);
            })
            count.value++;//修改依賴

          「效果展示:」

          如果說傳了一個(gè)count.value進(jìn)去,那么也會(huì)報(bào)錯(cuò),因?yàn)閏ount.value拿到的也是屬性值了,如下:

          「舉個(gè)栗子??:」 直接傳count

            import { reactive, ref, watch } from "vue";

            const state = reactive({a1,b2,});
            const count = ref(0);

            watch(count.value,(newValue,oldValue) =>{
                console.log("新值",newValue,"舊值",oldValue);
            })
            count.value++;//修改依賴

          「效果展示:」

          從運(yùn)行結(jié)果看,也報(bào)了一個(gè)警告,所以說這塊得注意下??

          對(duì)了,差點(diǎn)忘了,watch是可以監(jiān)控多個(gè)數(shù)據(jù)的,如下:

          「舉個(gè)栗子??:」count()=>state.a

            import { reactive, ref, watch } from "vue";

            const state = reactive({a1,b2,});
            const count = ref(0);

            watch([()=>state.a,count],([newValue1,newValue2],[oldValue1,oldValue2]) =>{
                console.log("新值",newValue1,newValue2,"舊值",oldValue1,oldValue2);
            })
            count.value++;
            state.a++;

          「效果展示:」

          ?? 注意:無論是watch還是watchEffect,當(dāng)依賴變化時(shí),回調(diào)函數(shù)都是異步執(zhí)行的,當(dāng)然也會(huì)到微隊(duì)列等待執(zhí)行。

          總得來說watchEffect是最方便的,因?yàn)樗鼤?huì)自動(dòng)跟蹤依賴的變化,不需要手動(dòng)指定,但是有時(shí)候卻不得不使用watch,比如說:我們不希望回調(diào)函數(shù)一開始就執(zhí)行,只想讓它當(dāng)數(shù)據(jù)改變的時(shí)候才執(zhí)行,這時(shí)候就只能用watch了,還有一種就是數(shù)據(jù)改變時(shí),我們需要知道舊值是什么?這時(shí)也需要使用watch,最后一種就是我們需要監(jiān)控一些回調(diào)函數(shù)中用不到的數(shù)據(jù),比方說,輸出console.log("我要變了"),這在watchEffect中是做不到的

          ?? 判斷

          在獲取響應(yīng)式數(shù)據(jù)的時(shí)候有四種api,分別是reactive、readonlyref、computed,返回有兩種形式,一個(gè)是對(duì)象代理,另一個(gè)是{value:...}這種形式,有時(shí)候我們可能沒睡醒,大腦混亂,于是vue3提供了4種api用于區(qū)分到底是哪種方式獲取的響應(yīng)式數(shù)據(jù):

          • isProxy : 用于判斷某個(gè)數(shù)據(jù)是否是由reactivereadonly獲取的

          • isReative : 判斷某個(gè)數(shù)據(jù)是否是通過reative創(chuàng)建的,具體可以看這個(gè)鏈接: https://v3.vuejs.org/api/basic-reactivity.html#isreactive

          • isReadonly :判斷某個(gè)數(shù)據(jù)是否是通過readonly創(chuàng)建的

          • isRef :判斷某個(gè)數(shù)據(jù)是否是一個(gè)ref對(duì)象

          ?? 轉(zhuǎn)換

          有時(shí)候我們拿到一個(gè)數(shù)據(jù),我們也不知道它是個(gè)啥,可能有時(shí)候也顧不上,到底是ref呢,還是proxy,鬼都不知道它是個(gè)啥!

          這時(shí)我們可以用unref

          • unref

          unref 等同于:isRef(val) ? val.value : val,如果是ref的話那就把val.value值拿出來,如果不是就拿val

          「舉個(gè)栗子??:」

           function useNewTodo(todos){
               todos = unref(todos);
               //...其它代碼
           }
          • toRef

          toRef會(huì)把一個(gè)響應(yīng)式對(duì)象中的某個(gè)屬性變成ref格式的數(shù)據(jù)

          「舉個(gè)栗子??:」

          import { reactive,  toRef } from "vue";

          const state = reactive({ name"法醫(yī)"age18 });

          const nameRef = toRef(state,"name");

          console.log(nameRef);//最終是 {value:...}這種形式

          nameRef.value = "前端獵手";//當(dāng)修改這個(gè)值后,state里面的數(shù)據(jù)也會(huì)受到影響
          console.log(state.name);

          「效果展示:」

          • toRefs

          把一個(gè)響應(yīng)式對(duì)象的所有屬性轉(zhuǎn)換為ref格式,然后包裝到plain-object普通對(duì)象中返回

          「舉個(gè)栗子??:」

          import { reactive, toRefs } from "vue";

          const state = reactive({ name"法醫(yī)"age18 });

          const stateAsRefs = toRefs(state);
          console.log(stateAsRefs);

          /*
          stateAsRefs 它不是一個(gè)代理對(duì)象了,而是一個(gè)普通對(duì)象,如下格式:

          {
             name:{value:ObjectRefImpl},
             age:{value:ObjectRefImpl}
          }

          */

          「效果展示:」

          為什么要這么做?

          「舉個(gè)栗子??:」我們需要把兩個(gè)響應(yīng)式數(shù)據(jù)混合在一起,如果直接使用展開運(yùn)算符那么就完蛋了,數(shù)據(jù)會(huì)失去響應(yīng)式

          setup() {
             const state1 = reactive({a:1,b:2});
             const state2 = reactive({c:3,d:4});
             return {
               ...state1,// 失去響應(yīng)式,相當(dāng)于在這寫了一個(gè)a:1,b:2
               ...state2,// 失去響應(yīng)式,相當(dāng)于在這寫了一個(gè)c:3,d:4
             };
           },

          那么如何解決呢?外面套一個(gè)toRefs就好了

          setup() {
            const state1 = reactive({a:1,b:2});
            const state2 = reactive({c:3,d:4});
            return {
              ...toRefs(state1),// 具有響應(yīng)式
              ...toRefs(state2),// 具有響應(yīng)式
            };
          },

          ?? 好了, 以上就是我的分享,希望能對(duì)大家有所幫助,歡迎大家在評(píng)論區(qū)討論鴨~

          希望小伙伴們點(diǎn)贊 ?? 支持一下哦~ ??,我會(huì)更有動(dòng)力的 ??,晚安!

          瀏覽 23
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本青娱乐 | 操逼操123首页 | 91沈三级电影 | 99热免费 | 射射蜜桃av免费电影 |