vue組件之間的多種通訊方式

一、父組件傳遞參數給子組件

  • 1、父組件傳遞參數

    <template>
      <div>
        <Son1
          :name="name"
          :age="20"
          :bookList="['三國演義', '西遊記']"
          :details="{'name': '張三', address: '廣東深圳'}"
          :isActive="false"
        />
      </div>
    </template>
    
  • 2、在子組件中定義接收

    <template>
      <div>
        <hr />
        <h3>我是子組件</h3>
        <p>{{name}}</p>
        <p>{{age}}</p>
        <p>{{bookList}}</p>
        <p>{{details}}</p>
        <p>{{isActive}}</p>
      </div>
    </template>
    
    <script>
      export default {
        name: 'son1',
        props: {
          name: {
            type: String,
            default: '',
          },
          age: {
            type: Number,
            default: 0,
          },
          bookList: {
            // 如果是對象或者數組必須使用下函數生成的方式
            type: Array,
            default: () => [],
          },
          details: {
            type: Object,
            default: () => ({}),
          },
          isActive: {
            type: Boolean,
            default: false,
          },
        },
        mounted() {
          console.log(this);
        },
      };
    </script>
    
    <style scoped></style>
    

二、子組件修改父組件傳遞的數據

由於vue屬於單向數據流,子組件中不直接修改父組件的數據,而是在組件觸發事件或者派發通知到父組件,通知父組件修改數據,父組件數據被修改了,傳遞到子組件中,子組件的數據也被修改了。

方式一、直接在子組件中定義方法,然後通過emit發出一個事件到父組件中修改(類似回調函數的方式)

  • 1、在子組件中

    <template>
      <div>
        <hr />
        <h3>我是子組件</h3>
        <p>{{name}}</p>
        <button @click="change">修改</button>
      </div>
    </template>
    
    <script>
      export default {
        name: 'son1',
        props: {
          name: {
            type: String,
            default: '',
          },
        },
        mounted() {
          console.log(this);
        },
        methods: {
          change() {
            // 對父組件派發一個change事件
            this.$emit('change', '哈哈,我是子組件過來的');
          },
        },
      };
    </script>
    
    <style scoped></style>
    
  • 2、在父組件中使用,然後修改數據

    <template>
        <div>
        	<!--在父組件中使用change事件-->  
          <Son1 :name="name" @change="change" />
        </div>
    </template>
      
      <script>
        import Son1 from './Son1';
        export default {
          name: 'parent1',
          data() {
            return {
              name: '哈哈',
            };
          },
          methods: {
            change(value) {
              this.name = value;
            },
          },
          mounted() {
            console.log(this);
          },
          components: {
            Son1,
          },
        };
    </script>
      
      <style scoped></style>
    

方式二、子組件中派發的事件名稱叫input:value(和方式一一樣的)

  • 1、子組件中

      // 子組件中派發一個input:value的事件給父組件
      this.$emit('input:value', '?哈哈');
    
  • 2、父組件中

    html
      <template>
        <div>
          <!--父組件接收子組件的input:value事件-->
          <Son1 :name="name" @input:value="change"></Son1>
          <!-- 
            或者直接將函數寫在行間
            <Son1
              :name="name"
              @input:value="(val)=>this.name=val"
            ></Son1> 
          -->
        </div>
    </template>
      
      <script>
        import Son1 from './Son1';
        export default {
          name: 'parent1',
          data() {
            return {
              name: '哈哈',
            };
          },
          methods: {
            change(value) {
              this.name = value;
            },
          },
          mounted() {
            console.log(this);
          },
          components: {
            Son1,
          },
        };
    </script>
      
    <style scoped></style>
    

方式三、使用修飾符sync數據同步,注意點:子組件必須派發的是update:屬性事件

  • 1、子組件中派發的事件名稱

    <template>
      <div>
        <h1>
          我是子組件內容
        </h1>
        <button @click="change1">使用sync的方式</button>
      </div>
    </template>
    <script>
      export default {
        methods: {
          change1() {
            this.$emit('update:name', '?哈哈');
          },
        },
      };
    </script>
    
  • 2、父組件中

    <template>
      <div>
        <!-- 使用的方式一 -->
        <Son1 :name="name" @update:name="(val)=>this.name=val"></Son1>
        <!-- 使用的方式二 -->
        <Son1 :name.sync="name"></Son1>
      </div>
    </template>
    

方式四、子組件中接收的是value屬的時候,派發的事件是input

  • 1、子組件

    <template>
      <div>
        <hr />
        <h3>我是子組件</h3>
        <p>{{value}}</p>
        <button @click="change">修改</button>
      </div>
    </template>
    
    <script>
      export default {
        name: 'son1',
        props: {
          value: {
            // 必須是value屬性
            type: String,
            default: '',
          },
        },
        mounted() {
          console.log(this);
        },
        methods: {
          change() {
            // 發送的事件也是定死的input
            this.$emit('input', '哈哈,我是子組件過來的');
          },
        },
      };
    </script>
    
    <style scoped></style>
    
  • 2、父組件中

    <template>
      <div>
        <Son1 :value="name" @input="(val)=>this.name=val"></Son1>
        <!-- 可以進一步簡寫成這樣 -->
        <Son1 v-model="name"></Son1>
      </div>
    </template>
    <script>
      import Son1 from './Son1';
      export default {
        name: 'parent1',
        data() {
          return {
            name: '哈哈',
          };
        },
      };
    </script>
    

三、孫組件修改父父組件的數據

主要的思路是孫組件派發事件通知父組件修改數據,父組件派發事件通知父組件修改

方式一、孫組件中使用$parent.$emit派發事件

  • 1、孫組件

    ...
    changeVal () {
      this.$parent.$emit('input', '孫組件修改的');
    },
    changeAge () {
      this.$parent.$emit('changeAge', 40);
    }
    ...
    
  • 2、父組件

    <template>
      <div>
        <hr />
        <h3>我是子組件</h3>
        <p>{{value}}==={{age}}</p>
        <hr />
        <h3>孫組件</h3>
        <Grandson1 :value="value" :age="age" />
      </div>
    </template>
    <script>
      import Grandson1 from './Grandson1';
      export default {
        name: 'son1',
        props: {
          value: {
            type: String,
            default: '',
          },
          age: {
            type: Number,
            default: 0,
          },
        },
        methods: {},
        components: {
          Grandson1,
        },
      };
    </script>
    
    <style scoped></style>
    
  • 3、父父組件

    <template>
      <div>
        <Son1 v-model="name" :age="age" @changeAge="val => this.age = val"></Son1>
      </div>
    </template>
    

方式二、使用自己擴展到Vue原型上的$dispatch方法

  • 1、擴展方法

    import App from './App';
    import Vue from 'vue';
    
    Vue.prototype.$dispatch = function(eventName, value) {
      let parent = this.$parent;
      // 循環遞歸派發事件
      while (parent) {
        parent.$emit(eventName, value);
        parent = parent.$parent;
      }
    };
    const vm = new Vue({
      el: '#app',
      render: (h) => h(App),
    });
    
  • 2、孫組件中使用

    ...
    methods: {
      changeVal () {
        this.$dispatch('input', '孫孫組件修改的')
      },
      changeAge () {
        this.$dispatch('changeAge', 40)
      }
    },
    ...
    
  • 3、父父組件中

    <Son1 v-model="name" :age="age" @changeAge="val => this.age = val"></Son1>
    

四、自定義廣播

類似angularjs中的廣播,其實也就是一個發佈者與訂閱者的關係,發佈一個消息,訂閱的組件就執行

  • 1、在main.js中擴展方法

    Vue.prototype.$broadcast = function(enevtName, value) {
      // 遞歸查找全部的子組件
      const broadcast = (children) => {
        children.forEach((child) => {
          child.$emit(enevtName, value);
          if (child.$children) {
            broadcast(child.$children);
          }
        });
      };
      broadcast(this.$children);
    };
    
  • 2、組件中訂閱廣播

    <template>
      <div>
        <Grandson1 :value="value" :age="age" @say="say" />
      </div>
    </template>
    <script>
      ...
      methods: {
        say (val) {
          console.log(`我是孫組件中的廣播,接收的消息是:${val}`)
        }
      },
      ...
    </script>
    
  • 3、發出一個廣播通知

    ...
    mounted () {
      this.$broadcast('say', '放假了');
    },
    ...
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章