Vue組件之間傳遞數(shù)據(jù)的五種方式

Vue 組件之間數(shù)據(jù)傳遞的幾種方式:

    1. 父組件向子組件傳遞數(shù)據(jù)章办,使用props屬性批狐;子組件向父組件中傳遞數(shù)據(jù),在子組件中使用$emit派發(fā)事件鸳谜,父組件中使用v-on
      監(jiān)聽事件盅惜;缺點(diǎn):組件嵌套層次多的話并巍,傳遞數(shù)據(jù)比較麻煩休偶。
    1. 祖先組件通過依賴注入(inject / provide)的方式,向其所有子孫后代傳遞數(shù)據(jù)懒棉;缺點(diǎn):無法監(jiān)聽數(shù)據(jù)修改的來源,不支持響應(yīng)式览绿。
    1. 通過屬性root /parent / $children /
      ref,訪問根組件穗慕、父級(jí)組件饿敲、子組件中的數(shù)據(jù);缺點(diǎn):要求組件之間要有傳遞性逛绵。
    1. 通過事件總線(event
      bus)的方式怀各,可以實(shí)現(xiàn)任意兩個(gè)組件間進(jìn)行數(shù)據(jù)傳遞;缺點(diǎn):不支持響應(yīng)式术浪,這個(gè)概念是vue1.0版本中的瓢对,現(xiàn)在已經(jīng)廢棄。
  1. 通過 VueJs 的狀態(tài)管理模式 Vuex胰苏,實(shí)現(xiàn)多個(gè)組件進(jìn)行數(shù)據(jù)共享硕蛹,推薦使用這種方式進(jìn)行項(xiàng)目中各組件間的數(shù)據(jù)傳遞。
    下面詳細(xì)介紹數(shù)據(jù)傳遞的幾種方式:

1.props/$emit

prop 是在組件上注冊(cè)的一些自定義的 attribute硕并。就像下面的 :sub-num 法焰。

<div :sub-num="num"></div>

1.1 父組件向子組件中傳遞數(shù)據(jù)

1、一個(gè)組件默認(rèn)可以擁有任意數(shù)量的 prop倔毙,任何值都可以傳遞給任何 prop埃仪。
2、當(dāng)一個(gè)值傳遞給一個(gè) prop attribute 的時(shí)候陕赃,它就變成了那個(gè)組件實(shí)例的一個(gè)屬性(例如下面的 subNum)卵蛉,通過屬性名,我們就能夠在組件實(shí)例中訪問這個(gè)值么库。
父組件向子組件中傳遞數(shù)據(jù)傻丝,可以在子組件中通過設(shè)置props屬性來接收傳遞過來的數(shù)據(jù)。

<div id="app">
    <div>{{num}}</div>
    <!-- 將父組件中的num诉儒,傳遞給子組件中的sub-num -->
    <blog-count :sub-num="num" :sub-user="user"></blog-count>
</div>

<script>
   const blogCount={
        //子組件中通過props屬性接收父組件傳遞過來的數(shù)據(jù)
       props:["subNum","subUser"],
       template:`<div>
                   <p>這是從父組件傳進(jìn)來的數(shù)字:{{subNum}}</p>
                   <p>這是從父組件傳進(jìn)來的對(duì)象:{{subUser.name}}-{{subUser.age}}</p>
               </div>`,

   }
   var vm=new Vue({
       el:"#app",
       data:{
           num:2,
           user:{
               name:"zhangsan",
               age:18
           }
       },
       components:{
            blogCount
       }
   })
</script>

1.2 子組件向父組件傳遞數(shù)據(jù)

子組件向父組件傳遞數(shù)據(jù)桑滩,通過 $emit派發(fā)事件,父組件中通過 v-on 接收該事件允睹,拿到傳遞的數(shù)據(jù)运准。

語法:$emit(eventName,data)缭受,第一個(gè)參數(shù)為事件名稱胁澳,需要跟父組件中 v-on 監(jiān)聽的事件名稱一致;第二個(gè)參數(shù)為要傳遞的數(shù)據(jù)米者。

<div id="app">
    <div>{{num}}</div>
    <!-- 通過 v-on 監(jiān)聽事件-->
    <blog-count @countchange="changeHandle"></blog-count>
</div>

<script>
    const blogCount={
        template:`<button @click="clickHandle">點(diǎn)擊接收子組件傳遞過來的數(shù)據(jù)</button>`,
        data(){
            return {num:6}
        },
        methods: {
            clickHandle(){
                //使用 $emit派發(fā)事件
                this.$emit("countchange",this.num);
            }
        }
    }
    var vm=new Vue({
        el:"#app",
        data:{
            num:0
        },
        methods: {
            changeHandle(data){
                this.num=data;
            }
        },
        components:{
            blogCount
        }
    })
</script>

1.3 .sync 修飾符

如果使用 update:myPropName 的模式觸發(fā)事件韭畸,上面的代碼可以寫成下面這樣:

<div id="app">
   <div>{{num}}</div>
    <blog-count :num="num" @update:countchange="num=$event"></blog-count>
    <!--最終可以簡(jiǎn)寫為-->
    <!--  <blog-count :countchange.sync="num"></blog-count>  -->
</div>

<script>
    const blogCount={
        template:`<button @click="$emit('update:countchange',num)">點(diǎn)擊接收子組件傳遞過來的數(shù)據(jù)</button>`,
        data(){
            return {num:6}
        }
    })
    var vm=new Vue({
        el:"#app",
        data:{num:0}
    }
</script>

提取出組件的代碼為:

<blog-count :num="num" @update:countchange="num=$event"></blog-count>```
為了方便起見宇智,我們?yōu)檫@種模式提供一個(gè)縮寫,即 .sync 修飾符:

```javascript
<blog-count :countchange.sync="num"></blog-count>

子組件向父組件傳遞數(shù)據(jù)胰丁,也可以通過使用 .sync 修飾符來完成随橘。

2. 依賴注入 provide inject

  1. 這對(duì)選項(xiàng)是2.2.0版本新增的。需要一起使用锦庸,它允許一個(gè)祖先組件向其所有子孫后代注入一個(gè)依賴机蔗,不論組件層次有多深,并在起上下游關(guān)系成立的時(shí)間里始終生效甘萧。
  2. 主要解決了跨級(jí)組件間的通信問題萝嘁。
  3. 在祖先組件中增加屬性 provide,它的屬性值是一個(gè)對(duì)象或返回一個(gè)對(duì)象的函數(shù)扬卷。該對(duì)象包含了給子組件要傳遞的數(shù)據(jù)牙言。
  4. 在子組件中增加屬性 inject ,用來接收數(shù)據(jù)怪得,它的選項(xiàng)是一個(gè)字符串?dāng)?shù)組咱枉,或一個(gè)對(duì)象。
<div id="app">
    <div>{{num}}</div>
    <blog-count></blog-count>
</div>

<script>
    const blogCount={
        //子組件中使用inject屬性來接收數(shù)據(jù)
        inject:["num"],
        template:`<div>{{num}}</div>`
    }
    var vm=new Vue({
        el:"#app",
        data:{
            num:10
        },
        //父組件中使用provide屬性存放要傳遞的數(shù)據(jù)
        provide:function(){
            return {
                num:this.num
            }
        },
        components:{
            blogCount
        }
    })
</script>

依賴注入 provide inject徒恋,這種方式的缺點(diǎn):

  1. 祖先組件不需要知道哪些后代組件使用它提供的屬性庞钢。
  2. 后代組件不需要知道被注入的屬性來自哪里。
  3. 會(huì)將應(yīng)用程序中的組件與它們當(dāng)前的組織方式耦合起來因谎,使重構(gòu)變得更加困難基括。
  4. 所提供的屬性是非響應(yīng)式的。

3. root /parent / $children / ref

  1. 通過 $root 屬性訪問根實(shí)例 new Vue()财岔。
  2. 通過$parent 屬性訪問父組件的實(shí)例风皿。
  3. 通過children 屬性訪問當(dāng)前實(shí)例的直接子組件。需要注意children 并不保證順序匠璧,也不是響應(yīng)式的桐款。
  4. 通過 r e f s 屬 性 訪 問 子 組 件 中 的 數(shù) 據(jù) , 子 組 件 標(biāo) 簽 上 加 r e f 的 屬 性 夷恍。 例 如 在 子 組 件 的 d o m 元 素 上 增 加 屬 性 r e f = " a b c " 魔眨, 就 可 以 使 用 t h i s . refs 屬性訪問子組件中的數(shù)據(jù),子組件標(biāo)簽上加 ref 的屬性酿雪。例如在子組件的dom元素上增加屬性 ref = "abc"遏暴,就可以使用this.refs屬性訪問子組件中的數(shù)據(jù),子組件標(biāo)簽上加ref的屬性指黎。例如在子組件的dom元素上增加屬性ref="abc"朋凉,就可以使用this.refs.abc 拿到這個(gè)子組件的實(shí)例。
  5. ref醋安,如果在普通的 DOM 元素上使用杂彭,引用指向的就是 DOM 元素墓毒;如果用在子組件上,引用就指向組件實(shí)例亲怠。
  6. $refs 只會(huì)在組件渲染完成之后生效所计,并且它們不是響應(yīng)式的。
<div id="app">
    <blog-count></blog-count>
</div>

<script>
    const blogItem={
        template:`<div>{{$root.num}}-{{$parent.num}}</div>`,
        data(){
            return {
                num:3
            }
        },
        mounted() {
            //訪問根實(shí)例中的數(shù)據(jù)
            console.log("Vue.num:"+this.$root.num);
            //訪問父級(jí)組件中的數(shù)據(jù)
            console.log("blogCount.num:"+this.$parent.num);
            //調(diào)用父級(jí)組件中的方法
            this.$parent.start()
        },
    }
    const blogCount={
        template:`<div><blogItem ref="refItem"></blogItem></div>`,
        data(){
            return {num:6}
        },
        components:{
            blogItem
        },
        methods: {
            start(){
                console.log("blogCount start...");
                console.log(this.$children[0].num)
            }
        },
        mounted() {
            //訪問子組件的實(shí)例
            console.log(this.$refs.refItem.num);
        },
    }
    var vm=new Vue({
        el:"#app",
        data:{
            num:0
        },
        components:{
            blogCount
        }
    })
</script>

4. event bus 事件總線

事件的觸發(fā)器团秽,vue 1.0版本使用比較多主胧,現(xiàn)在已經(jīng)不用。

這種方法可以看作是通過一個(gè)空的實(shí)例 new Vue()作為事件總線(事件中心)徙垫,用它來派發(fā)和監(jiān)聽事件,可以實(shí)現(xiàn)任何組件間的通信放棒,包括父子姻报、兄弟、跨級(jí)间螟。缺點(diǎn):這種傳遞數(shù)據(jù)的方式不是響應(yīng)式吴旋。

<div id="app">
    <div>{{num}}</div>
    <blog-count></blog-count>
    <button @click="clickHandle">send</button>
</div>

<script>
    //創(chuàng)建一個(gè)空的vue實(shí)例
    const eventbus=new Vue();
    //子組件
    const blogCount={
        data(){
            return {num:1}
        },
        template:`<div>{{num}}</div>`,
        mounted() {
            //監(jiān)聽事件
            eventbus.$on("message",(msg)=>{
                this.num=msg;
            })
        }
    }
    var vm=new Vue({
        el:"#app",
        data:{
            num:10
        },
        components:{
            blogCount
        },
        methods: {
            clickHandle(){
                //派發(fā)事件
                eventbus.$emit('message',this.num)
            }
        }
    })
</script>

5. vuex

Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它的狀態(tài)存儲(chǔ)是響應(yīng)式的厢破。采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài)荣瑟,也就是說,對(duì)數(shù)據(jù)的所有操作都要在vuex中進(jìn)行摩泪。

5.1 vuex 原理

vuex 實(shí)現(xiàn)了一個(gè)單向數(shù)據(jù)流笆焰,在全局擁有一個(gè) State 用來存放數(shù)據(jù),當(dāng)組件用同步的方式更改 State 中的數(shù)據(jù)時(shí)见坑,必須通過 Mutation 進(jìn)行嚷掠。當(dāng)使用異步方式(例如 ajax請(qǐng)求)修改數(shù)據(jù),必須先經(jīng)過 Actions 荞驴,再由 Actions 經(jīng)過 Mutation來操作不皆。

[圖片上傳失敗...(image-f4a68b-1650376415186)]

5.2 store

每一個(gè) Vuex 應(yīng)用的核心就是 store(倉庫)⌒苈ィ“store”基本上就是一個(gè)容器霹娄,它包含著你的應(yīng)用中大部分的狀態(tài) (state)。

const store = new Vuex.Store({
    state: {
        //相當(dāng)于自定義組件中的data
    },
    getters:{
        //相當(dāng)于自定義組件中的computed
    },
    mutations: {
        //相當(dāng)于自定義組件中的methods鲫骗,只能做同步的操作
        //對(duì)state中的數(shù)據(jù)進(jìn)行修改
    },
    actions: {
        //異步操作犬耻,例如ajax請(qǐng)求
        //使用commit 觸發(fā) mutations
    }
})

5.3 vuex的核心概念

5.3.1 State

  1. State相當(dāng)于自定義組件中的data,用來存放數(shù)據(jù)执泰,頁面中所有的數(shù)據(jù)都是從該對(duì)象中進(jìn)行讀取香追。
  2. 在組件中使用 store.state / this.$store.state 來讀取vuex中的數(shù)據(jù)。
//創(chuàng)建 vuex 實(shí)例
const store = new Vuex.Store({
    state: {
        count:3
    }
}
//創(chuàng)建 vue 實(shí)例
const app = new Vue({
    el: '#app',
    store,
    components: { Counter },
    template: `<div class="app"><counter></counter></div>`
})
//自定義組件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

5.3.2 getter

  1. getter 相當(dāng)于自定義組件中的computed坦胶,getter 的返回值會(huì)根據(jù)它的依賴被緩存起來透典,且只有當(dāng)它的依賴值發(fā)生了改變才會(huì)被重新計(jì)算晴楔。
  2. Getter 接受 state 作為其第一個(gè)參數(shù),也可以接受其他 getter 作為第二個(gè)參數(shù)峭咒。
  3. 可以使用 store.getters / this.$store.getters 訪問這些值税弃。
const store = new Vuex.Store({
    state: {
        sourceList:[],
        pageNo:1,
        pageSize:5,
    },
    getters: {
        dataList:(state)=>{
           //計(jì)算分頁后的數(shù)據(jù)
            let start=(state.pageNo-1)*state.pageSize;
            let end=start+state.pageSize;
            let result=state.sourceList.slice(start,end);
            return result;
        },
        pages:(state,getters)=>{
            //計(jì)算頁碼的范圍
            return Math.ceil(getters.dataList.length/state.pageSize);
        }
    }
})

5.3.3 Mutation

  1. mutation 相當(dāng)于自定義組件中的methods。
  2. mutation是更改 Vuex 的 State 中數(shù)據(jù)的唯一方法凑队。
  3. 通過 store.commit(type,data)調(diào)用 mutation则果,第一個(gè)參數(shù)為事件類型,需要和mutation中函數(shù)名稱一致漩氨;第二個(gè)參數(shù)為要傳遞的參數(shù)西壮。
  4. mutation中的函數(shù)接受 state 作為其第一個(gè)參數(shù)。
const store = new Vuex.Store({
    state: {
      count: 1
    },
    mutations: {
      increment (state) {
        // 變更狀態(tài)
        state.count++
      }
    }
})

const vm = new Vue(){
    methods:{
        change(){
            store.commit("increment");
        }
}

5.3.4 Action

  1. action 主要用來操作所有的異步請(qǐng)求叫惊。
  2. action 不能直接對(duì)State 中的數(shù)據(jù)進(jìn)行操作款青,只能通過commit(type,data) 方法調(diào)用 mutation。
  3. action 函數(shù)接受一個(gè)與 store 實(shí)例具有相同方法和屬性的 context 對(duì)象霍狰,因此你可以調(diào)用 context.commit 提交一個(gè) mutation抡草,或者通過 context.state 和 context.getters 來獲取 state 和 getters。
  4. 通過 store.dispatch(type)方法觸發(fā)action蔗坯,參數(shù)為事件類型康震,需要和action中函數(shù)名稱一致。
const store = new Vuex.Store({
    state: {
        dataList:[]
    },
    mutations: {
        render(state,data){
            state.dataList=data;
        },
    },
    actions: {
        getData({commit}){
            fetch("./data.json").then((res)=>res.json()).then((res)=>{
                  commit("render",res.data);
            })
**加粗樣式**        }
    }
})
const vm = new Vue(){
    created(){
        store.dispatch("getData");
    }
}

總結(jié):

父組件和子組件間通信:

  1. 父向子傳遞數(shù)據(jù)是通過props宾濒,子向父?jìng)鬟f數(shù)據(jù)是通過event($emit)腿短;
  2. 通過父鏈/子鏈進(jìn)行數(shù)據(jù)傳遞(parent /children);
  3. 通過 ref 也可以訪問組件實(shí)例绘梦;
  4. 依賴注入:provide / inject答姥;
    兄弟組件間通信:
  5. event bus
  6. Vuex
    跨級(jí)組件間通信:
  7. event bus;
  8. Vuex谚咬;
  9. 依賴注入:provide / inject鹦付;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市择卦,隨后出現(xiàn)的幾起案子敲长,更是在濱河造成了極大的恐慌,老刑警劉巖秉继,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祈噪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡尚辑,警方通過查閱死者的電腦和手機(jī)辑鲤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來杠茬,“玉大人月褥,你說我怎么就攤上這事弛随。” “怎么了宁赤?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵舀透,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我决左,道長(zhǎng)愕够,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任佛猛,我火速辦了婚禮惑芭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘继找。我一直安慰自己遂跟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布码荔。 她就那樣靜靜地躺著漩勤,像睡著了一般感挥。 火紅的嫁衣襯著肌膚如雪缩搅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天触幼,我揣著相機(jī)與錄音硼瓣,去河邊找鬼。 笑死置谦,一個(gè)胖子當(dāng)著我的面吹牛堂鲤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播媒峡,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瘟栖,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了谅阿?” 一聲冷哼從身側(cè)響起半哟,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎签餐,沒想到半個(gè)月后寓涨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氯檐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年戒良,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冠摄。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡糯崎,死狀恐怖几缭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拇颅,我是刑警寧澤奏司,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站樟插,受9級(jí)特大地震影響韵洋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜黄锤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一搪缨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鸵熟,春花似錦副编、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至打月,卻和暖如春队腐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奏篙。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工柴淘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秘通。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓为严,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親肺稀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子第股,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355