在父子元件傳遞資料時,通常使用的是 props 和 emit,父傳子時,使用的是 props,如果是父元件傳孫元件時,就需要先傳給子元件,子元件再傳給孫元件,如果多個子元件或多個孫元件使用時,就需要傳很多次,會很麻煩。
像這種情況,可以使用 provide 和 inject 解決這種問題,不論元件巢狀多深,父元件都可以為所有子元件或孫元件提供資料,父元件使用 provide 提供資料,子元件或孫元件 inject 注入資料。同時兄弟元件之間傳值更方便。
一、Vue2 的 provide / inject 使用
provide :是一個物件,裡面是屬性和值。如:
provide:{ info:"值" }
如果 provide 需要使用 data 內的資料時,這樣寫就會報錯。訪問元件例項 property 時,需要將 provide 轉換為返回物件的函式。
provide(){ return{ info: this.msg } }
inject :是一個字串陣列。如:
inject: [ 'info' ]
接收上邊 provide 提供的 info 資料,也可以是一個物件,該物件包含 from 和 default 屬性,from 是可用做的注入內容中搜尋用的 key,default 屬性是指定預設值。
在 vue2 中 project / inject 應用:
//父元件 export default{ provide:{ info:"提供資料" } } //子元件 export default{ inject:['info'], mounted(){ console.log("接收資料:", this.info) // 接收資料:提供資料 } }
provide / inject 類似於訊息的訂閱和釋出。provide 提供或傳送資料, inject 接收資料。
二、Vue3 的 provide / inject 使用
在組合式 API 中使用 provide/inject,兩個只能在 setup 期間呼叫,使用之前,必須從 vue 顯示匯入 provide/inject 方法。
provide 函式接收兩個引數:
provide( name,value )
name:定義提供 property 的 name 。
value :property 的值。
使用時:
import { provide } from "vue" export default { setup(){ provide('info',"值") } }
inject 函式有兩個引數:
inject(name,default)
name:接收 provide 提供的屬性名。
default:設定預設值,可以不寫,是可選引數。
使用時:
import { inject } from "vue" export default { setup(){ inject('info',"設定預設值") } }
完整例項1:provide/inject例項
//父元件程式碼 <script> import { provide } from "vue" export default { setup(){ provide('info',"值") } } </script> //子元件 程式碼 <template> {{info}} </template> <script> import { inject } from "vue" export default { setup(){ const info = inject('info') return{ info } } } </script>
三、新增響應性
為了給 provide/inject 新增響應性,使用 ref 或 reactive 。
完整例項2:provide/inject 響應式
//父元件程式碼 <template> <div> info:{{info}} <InjectCom ></InjectCom> </div> </template> <script> import InjectCom from "./InjectCom" import { provide,readonly,ref } from "vue" export default { setup(){ let info = ref("今天你學習了嗎?") setTimeout(()=>{ info.value = "不找藉口,立馬學習" },2000) provide('info',info) return{ info } }, components:{ InjectCom } } </script> // InjectCom 子元件程式碼 <template> {{info}} </template> <script> import { inject } from "vue" export default { setup(){ const info = inject('info') setTimeout(()=>{ info.value = "更新" },2000) return{ info } } } </script>
上述示例,在父元件或子元件都會修改 info 的值。
provide / inject 類似於訊息的訂閱和釋出,遵循 vue 當中的單項資料流,什麼意思呢?就是資料在哪,修改只能在哪,不能在資料傳遞處修改資料,容易造成狀態不可預測。
在訂閱元件內修改值的時候,可以被正常修改,如果其他元件也使用該值的時候,狀態容易造成混亂,所以需要在源頭上規避問題。
readonly 只讀函式,使用之前需要引入,如果給變數加上 readonly 屬性,則該資料只能讀取,無法改變,被修改時會發出警告,但不會改變值。
使用方法:
import { readonly } from "vue" let info = readonly('只讀info值') setTimout(()=>{ info="更新info" //兩秒後更新info的值 },2000)
執行兩秒後,瀏覽器發出警告,提示 info 值不可修改。
所以我們就給provide發射出去的資料,新增一個只讀屬性,避免發射出去的資料被修改。
完整例項2的 provide 處新增 readonly 。
provide('info', readonly(info))
在子元件修改值的時候,會有一個只讀提醒。
修改值的時候,還是需要在 provide 釋出資料的元件內修改資料,所以會在元件內新增修改方法,同時也釋出出去,在子元件處呼叫就可以了。如:
//釋出 let info = ref("今天你學習了嗎?") const changeInfo = (val)=>{ info.value = val } provide('info',readonly(info)) provide('changeInfo',changeInfo) //訂閱 const chang = inject('changeInfo') chang('衝向前端工程師')
完整示例3:修改資料
// 父元件程式碼 <template> <div> info:{{info}} <InjectCom ></InjectCom> </div> </template> <script> import InjectCom from "./InjectCom" import { provide,readonly,ref } from "vue" export default { setup(){ let info = ref("今天你學習了嗎?") const changeInfo = (val)=>{ info.value = val } provide('info',readonly(info)) provide('changeInfo',changeInfo) return{ info } }, components:{ InjectCom } } </script> //InjectCom 子元件程式碼 <template> <div> <button @click="chang('衝向前端工程師')">更新值</button> </div> </template> <script> import { inject } from "vue" export default { setup(){ const info = inject('info') const chang = inject('changeInfo') return{ info, chang } } } </script>