1. 程式人生 > 程式設計 >詳解vue元件之間相互傳值的方式

詳解vue元件之間相互傳值的方式

概述

我們都知道 vue 作為一個輕量級的前端框架,其核心就是元件化開發。Vue 就是由一個一個的元件構成的,元件化是它的精髓,也是最強大的功能之一。而元件例項的作用域是相互獨立的,這就意味著不同元件之間的資料無法相互引用。

但在實際專案開發過程中,我們需要訪問其他元件的資料,這樣就就有了元件通訊的問題。在 vue 中元件之間的關係有:父子,兄弟,隔代。針對不同的關係,怎麼實現資料傳遞,就是接下來要講的。

一、父元件向子元件傳值

即父元件通過屬性的方式向子元件傳值,子元件通過 props 來接收。

在父元件的子元件標籤中繫結自定義屬性

// 父元件
<user-detail :myName="name" />
    
export default {
    components: {
        UserDetail
    }
    ......
}

在子元件中使用props(可以是陣列也可以是物件)接收即可。可以傳多個屬性。

// 子元件
export default {
    props: ['myName']
}
​
/*
props: { myName: String } //這樣指定傳入的型別,如果型別不對會警告
props: { myName: [String,Number] } // 多個可能的型別
prosp: { myName: { type: String,requires: true } } //必填的的字串
props: { 
    childMsg: { 
        type: Array,default: () => [] 
    }
}  // default指定預設值
如果 props 驗證失敗,會在控制檯發出一個警告。
*/

子元件接收的父元件的值分為引用型別和普通型別兩種:

普通型別:字串(String)、數字(Number)、布林值(Boolean)、空(Null)

引用型別:陣列(Array)、物件(Object)

基於 vue 的單向資料流,即元件之間的資料是單向流通的,子元件是不允許直接對父元件傳來的值進行修改的,所以應該避免這種直接修改父元件傳過來的值的操作,否則控制檯會報錯。

如果傳過來的值是簡單資料型別,是可以在子元件中修改,也不會影響其他兄弟元件內同樣呼叫了來自該父元件的值。

具體操作是可以先把傳過來的值重新賦值給data中的一個變數,然後再更改那個變數。

// 子元件
export default {
    props: ['myName'],data() {
        return {
            name : this.myName    // 把傳過來的值賦值給新的變數
        }
    },watch: {
        myName(newVal) {
            this.name = newVal //對父元件傳過來的值進行監聽,如果改變也對子元件內部的值進行改變
        }
    },methods: {
        changeName() {  
            this.name = 'Lily'  // 這裡修改的只是自己內部的值,就不會報錯了
        },}
}

注:如果不使用 watch 來監聽父元件傳遞的 myName 值,子元件中的 name 值是不會隨著父元件程式設計客棧的 myName 值進行改變,因為 data 中 name: this.myName 僅僅只是定義了一個初始值。

如果引用型別的值,當在子元件中修改後,父元件的也會修改,因其資料是公用的,其他同樣引用了該值的子元件也會跟著被修改。可以理解成父元件傳遞給子元件的值,就相當於複製了一個副本,這個副本的指標還是指向父元件中的那個,即共享同一個引用。所以除非有特殊需要,否則不要輕易修改。

二、子元件向父元件傳值

1.子元件繫結一個事件,通過 this.$emit() 來觸發

在子元件中繫結一個事件,並給這個事件定義一個函式

// 子元件
<button @click="changeParentName">改變父元件的name</button>
​
export default {
    methods: {
        //子元件的事件
        changeParentName: function() {
            this.$emit('handleChange','Jack') // 觸發父元件中handleChange事件並傳參Jack
            // 注:此處事件名稱與父元件中繫結的事件名稱要一致
        }
    }
}

在父元件中定義並繫結 handleChange 事件

// 父元件
<child @handleChange="changeName"></child>
​
methods: {
    changeName(name) {  // name形參是子元件中傳入的值Jack
        this.name = name
    }
}

2.通過 callback 函式

先在父元件中定義一個callback函式,並把 callback 函式傳過去

// 父元件
<child :callback="callback"></child>
​
methods: {
    callback: function(name) {
        this.name = name
    }
}

在子元件中接收,並執行 callback 函式

// 子元件
<button @click="callback('Jack')">改變父元件的name</button>
​
props: {
    callback: tdAXSvdmiLFunction,}

3. 通過 $parent / $children 或 $refs 訪問元件例項

這兩種都是直接得到元件例項,使用後可以直接呼叫元件的方法或訪問資料。

// 子元件
export default {
  data () {
    return {
      title: '子元件'
    }
  },methods: {
    sayHello () {
        console.log('Hello');
    }
  }
}
// 父元件
<template>
  <child ref="childRef" />
</template>
​
<script>
  export default {
    created () {
      // 通過 $ref 來訪問子元件
      console.log(this.$refs.childRef.title);  // 子元件
      this.$refs.childRef.sayHello(); // Hello
      
      // 通過 $children 來呼叫子元件的方法
      this.$children.sayHello(); // Hello 
    }
  }
</script>

注:這種方式的元件通訊不能跨級。

三、兄弟元件之間傳值

1. 還是通過 $emit 和 props 結合的方式

在父元件中給要傳值的兩個兄弟元件都繫結要傳的變數,並定義事件

// 父元件
<child-a :myName="name" />
<child-b :myName="name" @changeName="editName" />  
    
export default {
    data() {
        return {
            name: 'John'
        }
    },components: {
        'child-a': ChildA,'child-b': ChildB,},methods: {
        editName(name) {
            this.name = name
        },}
}

在子元件B中接收變數和繫結觸發事件

// child-b 元件
<p>姓名:{{ myName }}</p>
<button @click="changeName">修改姓名</button>
    
<script>
export default {
    props: ["myName"],methods: {
        changeName() {
            this.$emit('changeName','Lily')   // 觸發事件並傳值
        }
    }
}
</script>
// child-a 元件
<p>姓名:{{ newName }}</p>
    
<script>
export default {
    props: ["myName"],computed: {
        newName() {
            if(this.myName) { // 判斷是否有值傳過來
                return this.myName
            }
            return 'John' //沒有傳值的預設值
        }
    }
}
</script>

即:當子元件B 通過 $emit() 觸發了父元件的事件函式 editName,改變了父元件的變數name 值,父元件又可以把改變了的值通過 props 傳遞給子元件A,從而實現兄弟元件間資料傳遞。

2.通過一個空 vue 例項

建立一個 EventBus.js 檔案,並暴露一個 vue 例項

import Vue from 'Vue'
export default new Vue()

在要傳值的檔案裡匯入這個空 vue 例項,繫結事件並通過 $emit 觸發事件函式

(也可以在 main.js 中全域性引入該 js 檔案,我一般在需要使用到的元件中引入)

<template>
    <div>
        <p>姓名: {{ name }}</p>
        <button @click="changeName">修改姓名</button>
    </div>
</template>
​
<script>
import { EventBus } from "../EventBus.js"
​
export default {
 data() {
     return {
         name: 'John',}
  },methods: {
      changeName() {
          this.name = 'Lily'
          EventBus.$emit("editName",this.name) // 觸發全域性事件,並且把改變後的值傳入事件函式
      }
    }
}
</script>

在接收傳值http://www.cppcns.com的元件中也匯入 vue 例項,通過 $on 監聽回撥,回撥函式接收所有觸發事件時傳入的引數

import { EventBus } from "../EventBus.js"
​
export default {
    data() {
        return {
            name: ''
        }
    },created() {
         EvtdAXSvdmiLentBus.$on('editName',(name) => {
             this.name = name
         })
    }
}

這種通過建立一個空的 vue 例項的方式,相當於建立了一個事件中心或者說是中轉站,用來傳遞和接收事件。這種方式同樣適用於任何元件間的通訊,包括父子、兄弟、跨級,對於通訊需求簡單的專案比較方便,但對於更復雜的情況,或者專案比較大時,可以使用 vue 提供的更復雜的狀態管理模式 Vuex 來進行處理。

四、多層父子元件通訊

有時需要實現通訊的兩個元件不是直接的父子元件,而是祖父和孫子,或者是跨越了更多層級的父子元件,這種時候就不可能由子元件一級一級的向上傳遞引數,特別是在元件層級比較深,巢狀比較多的情況下,需要傳遞的事件和屬性較多,會導致程式碼很混亂。

這時就需要用到 vue 提供的更高階的方法:provide/inject。

這對選項需要一起使用,以允許一個祖先元件向其所有子孫後代注入一個依賴,不論元件層次有多深,並在起上下游關係成立的時間裡始終生效。

provide/inject:簡單來說就是在父元件中通過provider來提供變數,然後在子元件中通過inject來注入變數,不管元件層級有多深,在父元件生效的生命週期內,這個變數就一直有效。

父元件:

export default {
  provide: { // 它的作用就是將 **name** 這個變數提供給它的所有子元件。
    name: 'Jack'
  }
}

子元件:

export default {
  inject: ['name'],// 注入了從父元件中提供的name變數
  mounted () {
    console.log(this.name);  // Jack
  }
}

注:provide 和 inject 繫結並不是可響應的。即父元件的name變化後,子元件不會跟著變。

如果想要實現 provide 和 inject 資料響應,有兩種方法:

provide 祖先元件的例項,然後在子孫元件中注入依賴,這樣就可以在後代元件中直接修改祖先元件的例項的屬性,不過這種方法有個缺點就是這個例項上掛載很多沒有必要的東西比如 props,methods

// 父元件 
<div>
      <button @click="changeName">修改姓名</buttotdAXSvdmiLn>
      <child-b />
</div>
<script>
    ......
    data() {
        return {
            name: "Jack"
        };
    },provide() {
        return {
            parentObj: this //提供祖先元件的例項
        };
    },methods: {
        changeName() {
            this.name = 'Lily'
        }
    }
</script>

後代元件中取值:  

<template>
  <div class="border2">
    <P>姓名:{{parentObj.name}}</P>
  </div>
</template>
<script>
  export default {
    inject: {
      parentObj: {
        default: () => ({})
      }
    } // 或者inject: ['parentObj']
  };
</script>

注:這種方式在函式式元件中用的比較多。函式式元件,即無狀態(沒有響應式資料),無例項化(沒有 this 上下文),內部也沒有任何生命週期處理方法,所以渲染效能高,比較適合依賴外部資料傳遞而變化的元件。

總結

父子通訊: 父向子傳遞資料是通過 props,子向父是通過 $emit;通過 $parent / $children 通訊;$ref 也可以訪問元件例項;provide / inject ;$attrs / $listeners;

兄弟通訊:EventBus;Vuex;

跨級通訊:EventBus;Vuex;provide / inject ;$attrs / $listeners;

以上就是詳解vue元件之間相互傳值的方式的詳細內容,更多關於vue的資料請關注我們其它相關文章!