記錄一下 vue 的組件之間通訊方式

props 父傳子和 $emit 子傳父

該應用場景多用於父子組件之間的通訊。

注意⚠️:

單向數據流html

全部的 prop 都使得其父子 prop 之間造成了一個單向下行綁定:父級 prop 的更新會向下流動到子組件中,可是反過來則不行。這樣會防止從子組件意外變動父級組件的狀態,從而致使你的應用的數據流向難以理解。vue

額外的,每次父級組件發生變動時,子組件中全部的 prop 都將會刷新爲最新的值。這意味着你應該在一個子組件內部改變 prop。若是你這樣作了,Vue 會在瀏覽器的控制檯中發出警告。es6

步驟

父傳子

  • 在父組件內,引入子組件並掛載
  • 在父組件掛載的子組件標籤上,經過綁定屬性 v-bind(縮寫::)的方式,將數據傳遞給該子組件
  • 在子組件經過 props 來接收父組件傳遞的數據

子傳父

  • 在子組件內,將(改變後的)數據經過 this.$emit 觸發當前實例上的事件,附加參數都會傳給監聽器回調
  • 在父組件掛載的子組件標籤上,經過綁定事件監聽器 v-on(縮寫:@)的方式,監聽 this.$emit 觸發的事件,在 methods 中定義監聽器回調方法來獲取數據

實例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>場景一:父子組件通訊</title>
  <!-- 開發環境版本,包含了有幫助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <!-- 父組件掛載到 #app 元素下 -->
    <m-self></m-self>
  </div>
</body>

<script>
  // 聲明子組件
  Vue.component('m-son', {
    // 子組件接收父組件經過綁定屬性(v-bind/:)綁定的屬性
    // 注意⚠️:通常 props 接收的數據做爲初始化數據
    props: {
      b: {
        type: String,
        default: ''
      }
    },
    // :value="b",而不用 v-model="b" 是由於 vue 中 prop 的使用,形成其父子 prop 之間造成了一個單向下行綁定,父級 prop 的更新會向下流動到子組件中,可是反過來則不行。這樣會防止從子組件意外變動父級組件的狀態,從而致使你的應用的數據流向難以理解。
    template: `<div>
    <input type="text" :value="b" @input="onInput" />
    </div>`,
    methods: {
      onInput(e) {
        let data = e.target.value
        // 子組件中更改了數據,經過 $emit 觸發當前實例上的事件。附加參數都會傳給監聽器回調。
        this.$emit('onChangeB', data)
      }
    }
  })

  // 聲明父組件,並將父組件掛載到 #app 上
  let mSelf = new Vue({
    el: '#app',
    data() {
      return {
        a: '將要傳遞給子組件的數據'
      }
    },
    // 父組件經過 v-bind(/:) 的方式,傳遞數據到子組件
    // 經過 v-on(/@) 的方式,監聽子組件中 $emit 觸發的事件
    template: `<div>
      <p>{{a}}</p>
      <m-son :b="a" @onChangeB="onChange"></m-son>
    </div>`,
    methods: {
      onChange(data) {
        this.a = data
      }
    }
  })
</script>

</html>

$parent 子訪問/綁定父 和 $children 父訪問/綁定子

$parent,父實例,若是當前實例有的話。
$parent,當前實例的直接子組件。 須要注意 $children 並不保證順序,也不是響應式的。若是你發現本身正在嘗試使用 $children 來進行數據綁定,考慮使用一個數組配合 v-for來生成子組件,而且使用 Array 做爲真正的來源。

注意⚠️:

節制地使用 $parent$children - 它們的主要目的是做爲訪問組件的應急方法。更推薦用 props 和 events 實現父子組件通訊。vuex

步驟

$parent 子訪問/綁定父

  • 在子組件內,經過 this.$parent 獲取父組件的實例(若是有父組件的話),實例上面固然能夠獲取到其掛載的數據
  • 在子組件內,經過 v-model 綁定父組件的數據,利用數據響應式完成子傳父

$children 父訪問/綁定子

  • 在子組件內,經過 this.$parent 拿到父組件的數據,並賦值給子組件,完成數據初始化
  • 在子組件內,經過 v-model 綁定子組件中的數據
  • 在父組件內,mounted 掛載後,經過 this.$children 獲取到子組件實例數組,將某一子組件實例賦值給父組件的某個變量,利用對象的特性,實現數據響應式。

實例

$parent 子訪問/綁定父

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>場景二:父子組件通訊</title>
  <!-- 開發環境版本,包含了有幫助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <!-- 父組件掛載到 #app 元素下 -->
    <m-self></m-self>
  </div>
</body>

<script>
  // 聲明子組件
  Vue.component('m-son', {
    // 子組件中,經過 this.$parent 訪問父組件實例
    template: `<div>
    <input type="text" v-model="$parent.a" />
    </div>`
  })

  // 聲明父組件,並將父組件掛載到 #app 上
  let mSelf = new Vue({
    el: '#app',
    data() {
      return {
        a: '將要傳遞給子組件的數據'
      }
    },
    template: `<div>
      <p>{{a}}</p>
      <m-son></m-son>
    </div>`
  })
</script>

</html>

$children父訪問/綁定子

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>場景二:父子組件通訊</title>
  <!-- 開發環境版本,包含了有幫助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <!-- 父組件掛載到 #app 元素下 -->
    <m-self></m-self>
  </div>
</body>

<script>
  // 聲明子組件
  Vue.component('m-son', {
    data() {
      return {
        // 子組件中,經過 this.$parent 獲取父組件實例,完成數據初始化
        b: this.$parent.a
      }
    },
    template: `<div>
    <input type="text" v-model="b" />
    </div>`
  })

  // 聲明父組件,並將父組件掛載到 #app 上
  let mSelf = new Vue({
    el: '#app',
    data() {
      return {
        a: '將要傳遞給子組件的數據',
        children: null
      }
    },
    template: `<div>
      <p>{{children && children.b}}</p>
      <m-son></m-son>
    </div>`,
    mounted() {
      // 實例掛載後,將子組件實例賦值給父組件中定義的數據
      // 注意⚠️:$children 在掛載後纔會取到值,並且不是響應式的。爲了達到響應式的效果,這裏採用從新賦值,利用了對象的特性
      this.children = this.$children && this.$children[0]
    }

  })
</script>

</html>

$root 請參考 $parent

$root 當前組件樹的根 Vue 實例。若是當前實例沒有父實例,此實例將會是其本身。

v-bind="$attrs" 祖傳孫和 v-on="$listeners" 孫傳子

$attr 包含了父做用域中不做爲 prop 被識別 (且獲取) 的 attribute 綁定 ( classstyle 除外)。當一個組件沒有聲明任何 prop 時,這裏會包含全部父做用域的綁定 ( classstyle 除外),而且能夠經過 v-bind="$attrs"傳入內部組件——在建立高級別的組件時很是有用。
$listeners 包含了父做用域中的 (不含 .native 修飾器的) v-on 事件監聽器。它能夠經過 v-on="$listeners" 傳入內部組件——在建立更高層次的組件時很是有用。
該應用場景可用於更高層次組件之間的通訊。

注意⚠️:

v-bind="$attrs"v-bind="$attrs" 的寫法,不能採用簡寫。npm

步驟

祖傳孫

  • 在祖組件內,經過綁定屬性 v-bind (簡寫::) 的方式來傳遞數據
  • 在各級子組件內,綁定 $attrs,即 v-bind="$attrs",獲取父級的綁定屬性
  • 在最後的孫組件內,經過 $attrs 去獲取祖組件傳遞的數據

孫傳祖

  • 在祖組件內,經過綁定事件 v-on (簡寫:@) 的方式來接收孫組件回調數據
  • 在各級子組件內,綁定 $listenersv-on="$listeners",獲取父級的事件監聽器
  • 在最後的孫組件內,經過 this.$listeners 獲取祖組件的事件監聽器,在數據改變後,執行該監聽器傳遞數據。或者採用 $emit 去觸發

實例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>場景三:父子孫子等更高級別組件通訊</title>
  <!-- 開發環境版本,包含了有幫助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <!-- 父組件掛載到 #app 元素下 -->
    <m-self></m-self>
  </div>
</body>

<script>
  // 聲明曾孫組件
  Vue.component('m-great-grandson', {
    // 孫組件經過 $attrs 獲取祖組件傳遞的數據
    template: `<div>
    <input type="text" :value="$attrs.b" @input="onInput" />
    </div>`,
    methods: {
      onInput(e) {
        let data = e.target.value
        // 孫組件中更改了數據,經過 $emit 觸發當前實例上的事件。附加參數都會傳給監聽器回調。
        // this.$emit('onChangeB', data)
        // 或者經過 $listeners 獲取事件監聽器,執行監聽器
        this.$listeners.onChangeB(data)
      }
    }
  })

  // 聲明孫組件
  Vue.component('m-grandson', {
    // 每層子組件都綁定 $attrs (v-bind="$attrs"),將數據傳給子組件
    // 每層子組件都綁定 $listeners (v-on="$listeners"),將事件監聽器傳給子組件
    template: `<div>
    <m-great-grandson v-bind="$attrs" v-on="$listeners"></m-great-grandson>
    </div>`
  })

  // 聲明子組件
  Vue.component('m-son', {
    // 每層子組件都綁定 $attrs (v-bind="$attrs"),將數據傳給子組件
    // 每層子組件都綁定 $listeners (v-on="$listeners"),將事件監聽器傳給子組件
    template: `<div>
    <m-grandson v-bind="$attrs" v-on="$listeners"></m-grandson>
    </div>`
  })

  // 聲明父組件,並將父組件掛載到 #app 上
  let mSelf = new Vue({
    el: '#app',
    data() {
      return {
        a: '將要傳遞給曾孫組件的數據'
      }
    },
    // 父組件內,經過 v-bind(/:) 綁定屬性 (:b="a")。每層子組件都綁定 $attrs (v-bind="$attrs"),實現數據的祖傳孫
    // 父組件內,經過 v-on(/@) 綁定事件 (@onChangeB="onChange")。每層子組件都綁定 $listeners (v-on="$listeners"),實現事件監聽器的祖傳孫
    template: `<div>
      <p>{{a}}</p>
      <m-son :b="a" @onChangeB="onChange"></m-son>
    </div>`,
    methods: {
      onChange(data) {
        this.a = data
      }
    }
  })
</script>

</html>

eventbus 中央事件管理

經過一個公共的區域,來管理數據。可用於非父子組件通訊。

步驟

  • 聲明兩個子組件 a 組件和 b 組件,在同一父組件中引入
  • 聲明 bus 中央事件管理, let bus = new Vue()
  • a 組件傳值到 b 組件
  • 在 a 組件內,建立後,將數據賦值給 bus,同時開啓事件監聽器。觸發事件後,回調處理參數
  • 在 b 組件內,建立後,將 bus 數據賦值給 b 組件,初始化數據。在數據改變後經過 bus.$emit 觸發事件監聽器,回調數據,完成數據傳遞

實例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>場景四:非父子組件通訊</title>
  <!-- 開發環境版本,包含了有幫助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <!-- 父組件掛載到 #app 元素下 -->
    <m-self></m-self>
  </div>
</body>

<script>
  // 中央事件管理
  let bus = new Vue()

  // 聲明子組件 a
  Vue.component('m-son-a', {
    data() {
      return {
        a: '傳給組件 b 數據'
      }
    },
    template: `<div>
    <p>{{a}}</p>
    </div>`,
    created() {
      //  將組件 a 數據賦值給 bus,並開啓事件監聽器。觸發後,回調數據即爲 組件 b 傳遞
      bus.a = this.a
      bus.$on('onChangeA', (data) => {
        this.a = data
      })
    },
    methods: {
    }
  })

  // 聲明子組件 b
  Vue.component('m-son-b', {
    data() {
      return {
        b: ''
      }
    },
    template: `<div>
    <input type="text" :value="b" @input="onInput" />
    </div>`,
    created() {
      // 數據初始化
      this.b = bus.a
    },
    methods: {
      onInput(e) {
        let data = e.target.value
        // 組件 b 中更改了數據,經過 $emit 觸發自定義事件。附加參數都會傳給監聽器回調。
        bus.$emit('onChangeA', data)
      }
    }
  })

  // 聲明父組件,並將父組件掛載到 #app 上
  let mSelf = new Vue({
    el: '#app',
    template: `<div>
      <m-son-a></m-son-a>
      <m-son-b></m-son-b>
    </div>`
  })
</script>

</html>

對象,共用同一對象

注意⚠️:

共用同一對象,可能會引發一些數據的混亂。不理解的狀況下會形成不是預期的變化。數組

步驟

  • 聲明一個公用對象
  • 在涉及的組件內引入該對象,並在組件建立以後初始化數據
  • 綁定該數據到 view 視圖

實例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>場景四:父子/非父子組件通訊</title>
  <!-- 開發環境版本,包含了有幫助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <!-- 父組件掛載到 #app 元素下 -->
    <m-self></m-self>
  </div>
</body>

<script>
  // 聲明一個公用對象,利用對象的特性完成數據共享。也能夠經過 es6 模塊 導出導入對象。index.js 中 export const data = { a: ''} ,import { data } from './index.js'
  // 將對象賦值給某個變量,這個變量修改了對象中的屬性,則原對象也會改變(對象是地址引用,引用類型)
  let data = {
    a: '傳給組件 b 數據'
  }

  // 聲明子組件 a
  Vue.component('m-son-a', {
    data() {
      return {
        data: {}
      }
    },
    template: `<div>
    <p>{{data && data.a}}</p>
    </div>`,
    created() {
      // 初始化數據
      this.data = data
    }
  })

  // 聲明子組件 b
  Vue.component('m-son-b', {
    data() {
      return {
        data: {}
      }
    },
    template: `<div>
    <input type="text" v-model="data.a" />
    </div>`,
    created() {
      // 數據初始化
      this.data = data
    }
  })

  // 聲明父組件,並將父組件掛載到 #app 上
  let mSelf = new Vue({
    el: '#app',
    template: `<div>
      <m-son-a></m-son-a>
      <m-son-b></m-son-b>
    </div>`
  })
</script>

</html>

provide/inject

該應用場景用於父子祖孫組件之間的通訊。

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

實例

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>場景三:父子孫子等更高級別組件通訊</title>
  <!-- 開發環境版本,包含了有幫助的命令行警告 -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>

<body>
  <div id="app">
    <!-- 父組件掛載到 #app 元素下 -->
    <m-self></m-self>
  </div>
</body>

<script>
  // 聲明曾孫組件
  Vue.component('m-great-grandson', {
    // inject 引入 provide 注入的屬性
    inject: ['a', 'onChangeB'],
    // 孫組件經過 inject 獲取祖組件傳遞的數據
    template: `<div>
    <input type="text" :value="a" @input="onInput" />
    </div>`,
    methods: {
      onInput(e) {
        let data = e.target.value
        // 孫組件中更改了數據,經過 $emit 觸發當前實例上的事件。附加參數都會傳給監聽器回調。
        // this.$emit('onChangeB', data)
        // 或者
        this.onChangeB(data)
      }
    }
  })

  // 聲明孫組件
  Vue.component('m-grandson', {
    template: `<div>
    <m-great-grandson></m-great-grandson>
    </div>`
  })

  // 聲明子組件
  Vue.component('m-son', {
    template: `<div>
    <m-grandson></m-grandson>
    </div>`
  })

  // 聲明父組件,並將父組件掛載到 #app 上
  let mSelf = new Vue({
    el: '#app',
    data() {
      return {
        b: '將要傳遞給曾孫組件的數據'
      }
    },
    // provide 能夠是對象,也能夠是返回一個對象函數,該對象包含可注入的屬性
    provide() {
      return {
        a: this.b,
        onChangeB: this.onChange
      }
    },
    // 父組件內,經過 provide 聲明注入屬性
    // 父組件內,經過 v-on(/@) 綁定事件 (@onChangeB="onChange")
    template: `<div>
      <p>{{b}}</p>
      <m-son @onChangeB="onChange" ></m-son>
    </div>`,
    methods: {
      onChange(data) {
        this.b = data
      }
    }
  })
</script>

</html>

vuex

相關文章
相關標籤/搜索