了解Vue 3的provide和inject用法

前言

provide和inject在Vue 2中已經(jīng)被廣泛應(yīng)用,不是新鮮API,3.0重新認(rèn)識(shí)一下它們兩個(gè)。

一句話介紹:provide可以向所有子孫組件提供數(shù)據(jù)以及提供修改數(shù)據(jù)的方法,子孫組件用inject使用數(shù)據(jù)。

在組合式API中的用法

基本用法

似乎,組合式API的provide沒(méi)有提供批量方法, 只能每個(gè)變量寫一句。

頂級(jí)組件:

<template>
  <div>
    <son />
  </div>
</template>

<script setup>
import son from "./son.vue";
import { provide } from "vue";
provide("abc", "123");
</script>

子組件:

<template>
  <div>
    <grandson />
  </div>
</template>

<script setup>
import grandson from "./grandson.vue";
</script>

孫組件:

<template>
  <div>我是孫子</div>
</template>

<script setup>
import { inject } from "vue";
const abc = inject("abc");
console.log(abc);
</script>

針對(duì)本文的評(píng)論,說(shuō)明一下

官方說(shuō):

inject() can only be used inside setup() or functional components.

這個(gè)意思是說(shuō),inject()只能放在setup()生命周期里運(yùn)行,不能放在別的周期里運(yùn)行,也不能放在事件周期里運(yùn)行。所以你可以先在setup()周期取值。

例一:這是允許的,因?yàn)閏onsole.log是setup()生命周期里的同步代碼

function xx() {
  console.log(inject("abc"))
}
xx()

例二:這是禁止的,因?yàn)閟etTimeout是異步函數(shù),執(zhí)行console.log(inject("abc"))的時(shí)候已經(jīng)不在setup()生命周期里。

function xx() {
  setTimeout(() => {
    console.log(inject("abc"))
  })
}
xx()

例三:讓例一的xx函數(shù)作為鼠標(biāo)事件回調(diào),也是禁止的,原因也一樣。

例四:放在Promise.then()也是禁止的,比如:

Promise.resolve().then(() => {
  console.log(inject("abc"))
})

一級(jí)組件修改數(shù)據(jù),孫子組件監(jiān)聽變更

provide的變量必須是響應(yīng)式變量,孫子組件監(jiān)聽的變量也必須是響應(yīng)式變量。

一級(jí)組件:

<template>
  <div>
    <son />
    <button @click="abc = '456'">{{ abc }}</button>
  </div>
</template>

<script setup>
import son from "./son.vue";
import { provide } from "vue";
ref: abc = "123";
provide("abc", $abc);
</script>

孫子組件:

<template>
  <div>我是孫子 - {{abc}}</div>
</template>

<script setup>
import { inject, watch } from "vue";
ref: abc = inject("abc");
watch($abc, () => {
  console.log(abc + '變了');
});
</script>

孫子組件修改數(shù)據(jù),一級(jí)組件監(jiān)聽變更

一級(jí)組件除了提供數(shù)據(jù),還要提供一個(gè)修改數(shù)據(jù)的方法,孫子組件要接收并使用這個(gè)方法,這樣修改的就是一級(jí)組件的數(shù)據(jù),修改之后又會(huì)影響孫子組件的數(shù)據(jù)。

一級(jí)組件:

<template>
  <div>
    <son />
    <button @click="abc = '456'">{{ abc }}</button>
  </div>
</template>

<script setup>
import son from "./son.vue";
import { provide } from "vue";
ref: abc = "123";
function updateAbc(val) {
  abc = val;
}
provide("abc", $abc);
provide("updateAbc", updateAbc);
</script>

孫子組件:

<template>
  <div @click="updateAbc('789')">我是孫子 - {{abc}}</div>
</template>

<script setup>
import { inject, watch } from "vue";
ref: abc = inject("abc");
const updateAbc = inject('updateAbc');
watch($abc, () => {
  console.log(abc + '變了');
});
</script>

禁止孫子組件修改一級(jí)組件的數(shù)據(jù)

禁止的話,一級(jí)組件傳遞的變量必須是只讀的,可以是readonly,也可以是shallowRef。這樣孫子組件修改數(shù)據(jù)的話,一級(jí)組件不會(huì)有反應(yīng)。

一級(jí)組件:

<template>
  <div>
    <son />
    <button @click="abc = '456'">{{ abc }}</button>
  </div>
</template>

<script setup>
import son from "./son.vue";
import { shallowRef, provide } from "vue";
let abc = shallowRef("123");
function updateAbc(val) {
  abc = val;
}
provide("abc", abc);
provide("updateAbc", updateAbc);
</script>

在配置項(xiàng)式API中的用法

跟Vue 2差不多,簡(jiǎn)單說(shuō)一下。

provide

provide語(yǔ)法比在組合式API里要靈活,畢竟可以用對(duì)象。比如:

  provide: {
    user: 'John Doe',
  },

如果要把data里的數(shù)據(jù)provide,說(shuō)白了就是打算用this,provide要寫成函數(shù):

  provide() {
    return {
      user: this.user,
      updateUser: this.updateUser
    }
  },

如果要提供計(jì)算屬性,也是一樣的道理,不過(guò),如果不打算用配置項(xiàng)式計(jì)算屬性,而是想用vue的computed方法,可以寫成:

  provide() {
    return {
      userLength: computed(() => this.user.length),
      updateUser: this.updateUser
    }
  },

inject

inject的用法更簡(jiǎn)單,比如inject: ['user', 'updateUser']

雙向修改而且響應(yīng)

想要雙向修改而且響應(yīng),就必須借助reactive或者ref家族方法和computed方法。provide的數(shù)據(jù)必須是computed返回的數(shù)據(jù),才能保證數(shù)據(jù)有變化時(shí)刷新孫子組件的數(shù)據(jù)。孫子組件要用computed + unref來(lái)解包裝且計(jì)算屬性,才能得到真正需要的數(shù)據(jù)。

可以看到,由于孫子組件的inject和computed的屬性不能重名,所以我造出一個(gè)abcabc變量名,這就比較麻煩了。我們?cè)倏唇M合式API,就沒(méi)有abcabc這種中間變量,原因是ref: abc = inject("abc");讓接收和加響應(yīng)式一氣呵成,這也是組合式API的優(yōu)勢(shì)之一。

一級(jí)組件:

<template>
  <div>
    <son />
    <button @click="abc = '456'">{{ abc }}</button>
  </div>
</template>

<script>
import son from "./son.vue";
import { ref, shallowRef, computed, provide } from "vue";
export default {
  components: {
    son,
  },
  data() {
    return {
      abc: 123,
    }
  },
  provide() {
    return {
      abcabc: computed(() => this.abc),
      updateAbc: this.updateAbc
    }
  },
  methods: {
    updateAbc(val) {
      this.abc = val;
    }
  }
}
</script>

孫子組件:

<template>
  <div @click="updateAbc('789')">我是孫子 - {{ abc }}</div>
</template>

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

export default {
  inject: ["abcabc", "updateAbc"],
  computed: {
    abc() {
      return unref(this.abcabc);
    },
  },
};
</script>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容