vue的provide / inject 有什麼用?

1.前言

vue的父子組件通訊用什麼?
:prop和$emit的組合。
若是是爺孫組件呢?
:那麼就要用父組件來轉發數據和事件了。
若是是太爺爺和孫子組件呢?
:固然是vuex啦
emmm 好的,沒我啥事了,我這就走。vue

不行,我還能再掙扎一下子!確定有一部分兄弟作的項目比較小,組件通訊的狀況不是不少,懶得引入vuex,那麼provide/inject就是爺孫(不限於爺孫/父子,中間隔了多少級均可以)通訊問題的最好解決方案啦!vue-router

2.官方文檔抄過來的介紹

這對選項須要一塊兒使用,以容許一個祖先組件向其全部子孫後代注入一個依賴,不論組件層次有多深,並在起上下游關係成立的時間裏始終生效。vuex

provide 選項應該是api

  • 一個對象或返回一個對象的函數。該對象包含可注入其子孫的屬性。在該對象中你可使用 ES2015 Symbols 做爲 key,可是隻在原生支持 Symbol 和 Reflect.ownKeys 的環境下可工做。

inject 選項應該是:數組

  • 一個字符串數組,或
  • 一個對象(詳情點擊這裏)

3.基本用法

// 祖先組件 提供foo
//第一種
export default {
  name: "grandfather",
  provide(){
    return{
      foo:'halo'
    }
  },
}
//第二種
export default {
  name: "grandfather",
  provide:{
    foo:'halo~~~~'
  },
}
//後代組件 注入foo
export default {
  inject:['foo'],
}

上面的兩種用法有什麼區別嗎?瀏覽器

  • 若是你只是傳一個字符串,像上面的‘halo’,那麼是沒有區別的,後代都能讀到。
  • 若是你須要傳一個對象(以下所示代碼),那麼第二種是傳不了的,後代組件拿不到數據。因此建議只寫第一種
//當你傳遞對象給後代時
provide(){
    return{
      test:this.msg
    }
  },
注意:一旦注入了某個數據,好比上面示例中的 foo,那這個組件中就不能再聲明 foo 這個數據了,由於它已經被父級佔有。

4.何時纔是可響應的?

若是你使用過vuex,那麼你會認爲,上面的例子中,若是我把foo:'halo'改爲foo:'world',子組件會及時響應數據變動,視圖完成更新。app

惋惜,沒有ide

在vue官方文檔中有這麼一句話函數

提示:provide 和 inject 綁定並非可響應的。這是刻意爲之的。然而,若是你傳入了一個可監聽的對象,那麼其對象的屬性仍是可響應的。

這裏不探討vue爲何要這麼設計,我這裏只展現啥時候provide/inject可響應this

provide(){
  return{
    test:this.msg
  }
},
data() {
  return {
    msg: "Welcome to Your Vue.js App",
  }
}
mounted(){
  setTimeout(()=>{
    this.msg = "halo world";
    console.log(this._provided.msg)
    //log:Welcome to Your Vue.js App
  },3000)
},

如上所示,這樣作是不行的,打印出來的 provided 中的數據並無改,子組件取得值也沒變。
你甚至能夠直接給 this._provided.msg 賦值,可是即便_provided.msg 裏面的值改變了,子組件的取值,依然沒有變。

當你像下面這樣作的時候,就能夠響應了

provide(){
  return{
    test:this.activeData
  }
},
data() {
  return {
    activeData:{name:'halo'},
  }
}
mounted(){
  setTimeout(()=>{
    this.activeData.name = 'world';
  },3000)
}

這就是vue中寫道的對象的屬性是能夠響應的

然而,若是你傳入了一個可監聽的對象,那麼其對象的屬性仍是可響應的。

5.實現全局變量

全局變量?provide/inject不是隻能從祖先傳遞給後代嗎?沒錯,咱們只要綁定到最最頂層的組件便可。

就是你啦! app.vue
咱們把一整個實例都扔給後代!

//app.vue
export default {
  name: 'App',
  provide(){
    return{
      app:this
    }
  },
  data(){
    return{
      text:"it's hard to tell the night time from the day"
    }
  },
  methods:{
    say(){
      console.log("Desperado, why don't you come to your senses?")
    }
  }
}
//其餘全部子組件,須要全局變量的,只須要按需注入app便可
export default {
  inject:['foo','app'],
  mounted(){
    console.log(this.app.text);//獲取app中的變量
    this.app.say();//能夠執行app中的方法,變身爲全局方法!
  }
}
這個也是可響應的噢~

6.實現頁面刷新

1 . 用vue-router從新路由到當前頁面,頁面是不進行刷新的
2 . 採用window.reload(),或者router.go(0)刷新時,整個瀏覽器進行了從新加載,閃爍,體驗很差

那咱們怎麼作呢?

跟上面的原理差很少,咱們只在控制路由的組件中寫一個函數(使用v-if控制router-view的顯示隱藏,這裏的原理不做贅述),而後把這個函數傳遞給後代,而後在後代組件中調用這個方法便可刷新路由啦。

//app.vue
<router-view v-if="isShowRouter"/>

export default {
  name: 'App',
  provide(){
    return{
      reload:this.reload
    }
  },
  data(){
    return{
      isShowRouter:true,
    }
  },
  methods:{
    reload(){
      this.isShowRouter = false;
      this.$nextTick(()=>{
        this.isShowRouter = true;
      })
    }
  }
}
//後代組件
export default {
  inject:['reload'],  
}

7.結尾

vue中有這樣的提示

注意: provide 和 inject 主要爲高階插件/組件庫提供用例。並不推薦直接用於應用程序代碼中。

provide/inject平時用的比較少,多數用於開發組件,但某些狀況下仍是很好用的。
業務龐大而複雜的,仍是建議使用vuex~

若是文章有錯誤或者建議,請在評論區指出,很是感謝~

相關文章
相關標籤/搜索