「快速學(xué)習(xí)系列」我熬夜整理了Vue3.x響應(yīng)性API

前言

Vue3.x正式版發(fā)布已經(jīng)快半年了,相信大家也多多少少也用Vue3.x開發(fā)過(guò)項(xiàng)目。那么,我們今天就整理下Vue3.x中的響應(yīng)性API。
響應(yīng)性API
reactive

作用: 創(chuàng)建一個(gè)響應(yīng)式數(shù)據(jù)。

本質(zhì): 傳入數(shù)據(jù)(復(fù)雜類型:數(shù)組和json對(duì)象)包裝成一個(gè)Proxy對(duì)象。如果傳入其他對(duì)象,默認(rèn)情況下修改對(duì)象,界面不會(huì)自動(dòng)更新,如果想更新,可以通過(guò)重新賦值(創(chuàng)建一個(gè)新的對(duì)象)的方式。

<template>
  <div class="reactive">
    <button @click="fn">點(diǎn)擊</button>
    <p>{{ state }}</p>
    <button @click="fn1">點(diǎn)擊1</button>
    <p>{{ timeState }}</p>
  </div>
</template>

<script>
import { reactive } from "vue";
export default {
  name: "Reactive",
  setup() {
    let state = reactive({
      name: "123",
    });
    function fn() {
      console.log(state);
      state.name = "456";
    }

    let timeState = reactive({
      time: new Date(),
    });

    function fn1() {
      const newTime = new Date(timeState.time.getTime());
      newTime.setDate(timeState.time.getDate() + 1);
      timeState.time = newTime;
      console.log(timeState.time);
    }

    return {
      state,
      fn,
      timeState,
      fn1,
    };
  },
};
</script>



ref

作用: 創(chuàng)建一個(gè)響應(yīng)式數(shù)據(jù)。修改對(duì)應(yīng)的值必須在后面加上.value。
注意點(diǎn): 在template標(biāo)簽內(nèi)不用加.value。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
    <button @click="fn1">點(diǎn)擊1</button>
    <p>{{arrState}}</p>
  </div>
</template>

<script>
import {ref} from "vue";
export default {
  name:"Ref",
  setup(){
    let state = ref(123);
    function fn () {
      state.value = 345;
    }
    let arrState = ref([]);
    function fn1 (){
      arrState.value.push("1");
    }
    return {
      state,
      fn,
      arrState,
      fn1
    }
  }
}
</script>



shallowReactive

作用: 創(chuàng)建一個(gè)響應(yīng)式 proxy,跟蹤其自身 property的響應(yīng)性,但不執(zhí)行嵌套對(duì)象的深度響應(yīng)式轉(zhuǎn)換 (暴露原始值)。

本質(zhì): 對(duì)于嵌套對(duì)象不做響應(yīng),值跟蹤自身的第一層property。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <button @click="fn1">點(diǎn)擊1</button>
    <p>{{state}}</p>
  </div>
</template>

<script>
import { shallowReactive } from "vue"
export default {
  name:"ShallowReactive",
  setup(){
    let state = shallowReactive({
      name:"maomin",
      age:{
        number:20
      }
    })
    function fn(){
      state.name = "123"; // 響應(yīng)性
    }
    function fn1(){
      state.age.number = 23; // 無(wú)響應(yīng)性
    }
    return {
      state,
      fn,
      fn1
    }
  }
}
</script>



shallowRef

作用: 創(chuàng)建一個(gè) ref,它跟蹤自己的.value 更改,但不會(huì)使其值成為響應(yīng)式的。不會(huì)將其值轉(zhuǎn)化為Proxy對(duì)象。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{ state }}</p>
    <button @click="fn1">點(diǎn)擊1</button>
    <p>{{ state1 }}</p>
  </div>
</template>

<script>
import {
  shallowRef,
  ref,
  // triggerRef
} from "vue";
export default {
  name: "ShallowRef",
  setup() {
    let state = shallowRef({
      name: "maomin",
    });
    let state1 = ref({});
    function fn() {
      state.value.name = "345";
      console.log(state.value); // {name: "345"}, 但是UI界面不會(huì)變。
      // triggerRef(state); // 如果想觸發(fā)UI界面,可以使用它。
    }
    function fn1() {
      state1.value = {
        name: "123",
      };
      // eslint-disable-next-line no-irregular-whitespace
      console.log(state1.value); // Proxy {name: "123"}
    }
    return {
      state,
      fn,
      state1,
      fn1,
    };
  },
};
</script>



readonly

作用: 獲取一個(gè)對(duì)象 (響應(yīng)式或純對(duì)象) 或 ref 并返回原始 proxy的只讀 proxy。只讀 proxy是深層的:訪問(wèn)的任何嵌套 property也是只讀的。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{os}}</p>
    <p>{{state}}</p>
  </div>
</template>

<script>
import {reactive, readonly} from "vue";
export default {
  name:"Readonly",
  setup(){
    let state = reactive({
      name:"maomin",
      age:{
        number:18
      }
    })
    let os = readonly(state);
    function fn(){
      os.name = "123";
    }
    return{
      os,
      state,
      fn
    }
  }
}
</script>



shallowReadonly

作用: 創(chuàng)建一個(gè)proxy,使其自身的 property為只讀,但不執(zhí)行嵌套對(duì)象的深度只讀轉(zhuǎn)換 (暴露原始值)。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{os}}</p>
    <p>{{state}}</p>
  </div>
</template>

<script>
import {
  reactive,
  shallowReadonly,
  isReadonly
  } from "vue";
export default {
  name:"ShallowReadonly",
  setup(){
    let state = reactive({
      name:"maomin",
      age:{
        number:18
      }
    })

    let os = shallowReadonly(state);

    function fn(){
       console.log(isReadonly(os.name)) //false
       os.age.number = 20;
    }
    return{
      state,
      os,
      fn,
    }
  }
}
</script>



toRaw

作用: 響應(yīng)式對(duì)象轉(zhuǎn)普通對(duì)象。

本質(zhì): 返回由reactive或 readonly 方法轉(zhuǎn)換成響應(yīng)式代理的普通對(duì)象。這是一個(gè)還原方法,可用于臨時(shí)讀取,訪問(wèn)不會(huì)被代理/跟蹤,寫入時(shí)也不會(huì)觸發(fā)更改。不建議一直持有原始對(duì)象的引用。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
  </div>
</template>

<script>
import { reactive, toRaw } from "vue";
export default {
  name:"ToRaw",
  setup(){
    const obj = {
      name:"maomin"
    };
    let state = reactive(obj);
    function fn(){
      console.log(toRaw(state) === obj); //true
      let obj1 = toRaw(state);
      obj1.name = "123";
      // eslint-disable-next-line no-irregular-whitespace
      console.log(state); // Proxy {name: "123"}. 值雖改變,但是頁(yè)面沒(méi)有變化。
    }

    return {
      state,
      fn
    }
  }
}
</script>



markRaw

作用: 標(biāo)記一個(gè)對(duì)象,使其永遠(yuǎn)不會(huì)轉(zhuǎn)換為 proxy。返回對(duì)象本身。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
  </div>
</template>

<script>
import {markRaw,reactive} from "vue"
export default {
  name:"MarkRaw",
  setup(){
    let obj = {name:'maomin', age: 20};
    obj = markRaw(obj);
    let state = reactive(obj);
    function fn(){
      state.name = '123';
      console.log(state); //這里雖然打印出name:123,但是UI界面不會(huì)改變。
    }
    return{
      state,
      fn
    }
  }
}
</script>



toRef

如果使用ref,我們修改響應(yīng)式的數(shù)據(jù)是不會(huì)影響到原始數(shù)據(jù)的(復(fù)制)。
如果使用toRef,我們修改響應(yīng)式的數(shù)據(jù)是會(huì)影響到原始數(shù)據(jù)的(引用)。

作用: 可以用來(lái)為源響應(yīng)式對(duì)象上的 property新創(chuàng)建一個(gè)ref。然后可以將 ref傳遞出去,從而保持對(duì)其源 property的響應(yīng)式連接。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
    <button @click="fn1">點(diǎn)擊1</button>
    <p>{{state1}}</p>
  </div>
</template>

<script>
import {reactive, ref, toRef} from "vue"
export default {
  name:"ToRef",
  setup(){
    let obj = {name:"maomin"};
    let os = reactive(obj);

    let state = ref(os.name);
    let state1 = toRef(os,'name');
    // ref
    function fn(){
      state.value = "123";
      console.log(os); // 原始數(shù)據(jù)不會(huì)發(fā)生改變
      console.log(state);
    }
    // toRef
    function fn1(){
      state1.value = "345";
      console.log(os); // 原始數(shù)據(jù)會(huì)發(fā)生改變
      console.log(state1);
    }
    return {
      state,
      fn,
      state1,
      fn1
    }
  }
}
</script>


toRefs

作用: 將響應(yīng)式對(duì)象轉(zhuǎn)換為普通對(duì)象,其中結(jié)果對(duì)象的每個(gè) property 都是指向原始對(duì)象相應(yīng) property 的ref。

用途: 當(dāng)從合成函數(shù)返回響應(yīng)式對(duì)象時(shí),toRefs 非常有用,這樣消費(fèi)組件就可以在不丟失響應(yīng)性的情況下對(duì)返回的對(duì)象進(jìn)行分解/擴(kuò)散。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
    <button @click="fn1">點(diǎn)擊1</button>
    <p>{{foo}}</p>
  </div>
</template>

<script>
import {reactive, toRefs} from "vue"
export default {
  name:"ToRefs",
  setup(){
    let obj = {
      name:"maomin",
      age:20
    }
    let os = reactive(obj);
    let state = toRefs(os);

    function fn(){
      state.name.value = "123";
      os.name = "234";
      console.log(os);
      console.log(state);
      console.log(state.name.value === os.name); //true
    }

    const { foo, bar } = useFeatureX();
 
    function fn1(){
      foo.value = "2";
    }
    return {
      fn,
      state,
      foo,
      bar,
      fn1
    }
  }
}


function useFeatureX() {
  const state = reactive({
    foo: 1
  })
  // 返回時(shí)轉(zhuǎn)換為ref
  return toRefs(state)
}
</script>



customRef

作用: 創(chuàng)建一個(gè)自定義的ref,并對(duì)其依賴項(xiàng)跟蹤和更新觸發(fā)進(jìn)行顯式控制。它需要一個(gè)工廠函數(shù),該函數(shù)接收 track 和 trigger 函數(shù)作為參數(shù),并應(yīng)返回一個(gè)帶有 get 和 set 的對(duì)象。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
  </div>
</template>

<script>
import {customRef} from "vue";

function myRef(value){
  return customRef((track, trigger)=>{
    return {
      get(){
        track();
        console.log('get',value);
        return value;
      },
      set(newValue){
        console.log('set',newValue);
        value = newValue;
        trigger();
      }
    }
  })
}
export default {
  name:"CustomRef",
  setup(){
    let state = myRef(18);

    function fn(){
      state.value = 19;
    }
    return {
      state,
      fn
    }
  }
}
</script>



computed

作用: 依賴項(xiàng)變化時(shí),其賦予的值也就相應(yīng)改變。

注意點(diǎn): 直接修改computed是不可以的。

<template>
  <div>
    <p>{{state}}</p>
    <p>{{os}}</p>
    <button @click="fn">點(diǎn)擊</button>
  </div>
</template>

<script>
import {computed,ref} from "vue"
export default {
  name: "Computed",
  setup(){
    let state = ref(12);
    let os = computed(() => state.value + 1);

    function fn(){
      state.value = 23; // os的值也會(huì)相應(yīng)改變
      // os.value = 26; // Write operation failed: computed value is readonly
    }
    return{
      state,
      os,
      fn
    }
  }
}
</script>



watchEffect

作用: 在響應(yīng)式地跟蹤其依賴項(xiàng)時(shí)立即運(yùn)行一個(gè)函數(shù),并在更改依賴項(xiàng)時(shí)重新運(yùn)行它。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
    <p>{{state1}}</p>
    <p>{{num}}</p>
  </div>
</template>

<script>
import { reactive, ref, watchEffect } from "vue"
export default {
  name:"WatchEffect",
  setup(){
    let state = ref(0);
    let state1 = reactive({
      name:"maomin"
    })
    let num = 1;
    // 首次運(yùn)行時(shí)會(huì)執(zhí)行它,如果響應(yīng)依賴項(xiàng)改變時(shí),會(huì)重新執(zhí)行它。
    watchEffect(()=>{
      // console.log(num);
      console.log(state.value);
      console.log(state1);
    })

    function fn() {
      state.value = 3;
      state1.name = "123";
      num = 2;
    }

    return{
      fn,
      state,
      state1,
      num
    }
  }
}
</script>



watch

作用: 默認(rèn)情況下,它也是惰性的——即回調(diào)僅在偵聽(tīng)源發(fā)生更改時(shí)調(diào)用。

<template>
  <div>
    <button @click="fn">點(diǎn)擊</button>
    <p>{{state}}</p>
    <button @click="fn1">點(diǎn)擊1</button>
    <p>{{state1}}</p>
  </div>
</template>

<script>
import {reactive, ref, watch} from "vue"
export default {
  name:"Watch",
  setup(){

    // reactive
    let state = reactive({
      name:"maomin"
    })

    watch(
      () => state.name,
      (count) =>{
        console.log(count); //123
      }
    )

    function fn() {
      state.name = "123";
    }

    //ref
    let state1 = ref(1);
    watch(
      state1,
      (count) =>{
        console.log(count); //2
      }
    )

    function fn1() {
      state1.value = 2;
    }

    return {
      state,
      fn,
      fn1,
      state1
    }
  }
}
</script>

作者:Vam的金豆之路

主要領(lǐng)域:前端開發(fā)

我的微信:maomin9761

微信公眾號(hào):前端歷劫之路