当前位置 博文首页 > Echoyya、:Vue2.0 多种组件传值方法-不过如此的 Vuex

    Echoyya、:Vue2.0 多种组件传值方法-不过如此的 Vuex

    作者:Echoyya、 时间:2021-02-15 22:31

    码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14404397.html

    在vue项目中了解组件间通讯很重要,也是最基础的面试题,可以大致总结为以下几种情况和方法:

    一、父向子传值

    父 to 子 :通过动态绑定属性的方式,子组件在props中去接收,占为已用

    // father.vue
    <child :name="name" />
    
    data() {
        return {
            name:'Echoyya',
        }
    }
    
    // child.vue
    export default {
        props: ['name'],
        mounted() {
            console.log(this.name)  // Echoyya
        }
    }
    

    二、子向父传值 - 1. 事件分发 emit

    子 to 父 :通过.emit('事件名称', 传递的参数)事件分发的方式, 父组件当中,调用子组件标签上绑定自定义事件,其中包含一个参数,即子组件传递过来的数据

    1. emit 只能接受两个参数,其余不生效,第一个参数:事件名称,第二个: 传递的数据

    2. 事件分发,不一定要通过点击事件,也可使用钩子函数等

    3. 需要传递多个参数时,emit第二个参数可选择数组或是对象

    // father.vue
    <child @send="getChildData" />
    
    methods: {
        getChildData(data){
            console.log('子组件传递过来的数据:' + data);
        }
    }
    
    // child.vue
    <button @click="send">发送数据</button>
    
    export default {
        data() {
            return {
              year:2021
            };
          },
        methods:{
            send(){
              this.$emit('send',this.year)
            }
        }
    };
    

    三、子向父传值 - 2. 父组件动态绑定方法

    1. 除了平时常见的emit 事件分发的方式实现子向父传值,还有一种不太常见的方式

    2. 在前面有提到过,父向子传值可以通过在标签上动态绑定属性的方式,同样也可以动态绑定方法,在子组件props中接收

    3. 在根据实际情况,在子组件中调用该方法,并传值,此时父组件执行对应的方法及参数的处理,该参数便是子向父传递的数据

    // father.vue
    <child :fn="getChildData" />
    
    methods: {
        getChildData(data){ // 子组件传递过来的数据
            console.log(data); // 子组件 - 2021
        }
    }
    
    // child.vue
    export default {
      props: ['fn'],
      mounted(){
        this.fn('子组件 - 2021') 
      }
    };
    

    四、(主动)父组件主动获取子组件的数据和方法

    1. 父组件中调用子组件,绑定一个ref属性

    2. 主动获取属性:this.$refs.ref名称.属性

    3. 主动调用方法:this.$refs.ref名称.方法()

    // father.vue
    <child :name="name" ref="child" />
    
    mounted() {
        console.log(this.$refs.child.year); // 2021
        this.$refs.child.showName()   // Echoyya:2021
    }
    
    
    // child.vue
    export default {
      data(){
        return {
          year:2021
        }
      },
      methods:{
        showName(){
          console.log('Echoyya:'+ this.year);
        }
      }
    };
    

    五、(主动)子组件主动获取父组件的数据和方法

    1. 子组件中调用父组件,使用 this.$parent

    2. 主动获取属性:this.$parent.属性

    3. 主动调用方法:this.$parent.方法(),还可以向父组件传递参数

    // father.vue
    <div >
      <child />
    </div>
    
    <script>
    import child from "./components/child";
    export default {
      components: {
        child
      },
      data() {
        return {
          name:'Echoyya',
        };
      },
      methods: {
        parentMethod(data){
          // 可以接收子组件调用时传递的参数
          console.log(data);   // 2021
        }
      }
    };
    </script>
    
    // child.vue
    export default {
       mounted(){
          console.log(this.$parent.name);   // Echoyya
          this.$parent.parentMethod(2021);  // 2021
      }
    };
    

    六、EventBus 传值

    多层级组件之间相互传值,或兄弟组件之间传值,通常使用EventBus,实际上就是 vm,即 Vue 的实例,通过发布订阅者模式,实现任意两组件之间的通讯,父子亦可。

    1. 首先创建EventBus文件,导出vm 实例

    2. 在需要通讯的两组件中分别引入该文件

    3. 通过发布订阅 .$emit.$on 实现传值 ,操作的事件名需相同。

    // EventBus.js
    import Vue from 'vue'
    var vm = new Vue()
    
    export default vm
    
    // App.vue
    <template>
      <div >
        <bus-one />
        <bus-two />
      </div>
    </template>
    
    <script>
    import busOne from "./components/bus1";
    import busTwo from "./components/bus2";
    export default {
      components: {
        busOne,
        busTwo
      }
    }
    </script>
    
    // bus1.vue
    <template>
      <div>
        组件 1 发布
        <button @click="sendNum">通讯发送数据</button>
      </div>
    </template>
    
    <script>
    import eb from "../EventBus";
    export default {
      methods:{
        sendNum(){
          eb.$emit('sendData',111)
        }
      }
    };
    </script>
    
    // bus2.vue
    <template>
      <div>
        组件 2 订阅 
      </div>
    </template>
    
    <script>
    import eb from "../EventBus";
    export default {
      created(){
        eb.$on('sendData',function(data){
          console.log(data);  // 111
        })
      }
    };
    </script>
    

    EventBus 内部实现原理

    1. 手动模拟一个 EventBus,实现发布订阅的效果,创建一个 myEventBus 文件

    2. 创建一个对象,分别设置发布、订阅的事件,以及事件存储的对象,通过事件订阅将事件存储至事件对象中,

    3. 事件发布时,自动调用事件对象中相同 key 的所有事件

    myEventBus.html

    <script>
        var eventbus = {
            fnObj:{
                //abc:[fn1,fn2], 可以多次订阅同一个事件,因此每个 key 对应一个数组
            },
            $on: function (id, fn) {
                if(!this.fnObj[id]){
                    this.fnObj[id] = [fn]
                }else{
                    this.fnObj[id].push(fn)
                }
            },
            $emit: function (id, data) {
                if(this.fnObj[id]){
                    var fnArr = this.fnObj[id]
                    // 当事件触发时,循环执行每个 key 对应的 所有function
                    for(var i = 0;i<fnArr.length;i++){
                        var fn = fnArr[i]
                        fn(data)
                    }
                }else{
                    console.log('异常,函数不存在')
                }
            }
        }
        eventbus.$on('abc',function(data){
            console.log(1)
        })
        eventbus.$on('abc',function(data){
            console.log(2)
        })
        eventbus.$on('abc',function(data){
            console.log(3)
        })
        eventbus.$on('abc',function(data){
            console.log(4)
        })
        eventbus.$emit('abc','123')
    </script>
    

    七、不过如此的 Vuex

    1. Vuex 应用程序开发的 状态管理模式,集中式管理应用所有组件的状态,进行组件通讯

    2. 安装插件,引入 Vuex,创建 store 实例,配置到 Vue 实例中

    3. 为防止状态,方法声明等过分重复和冗余,Vuex 提供了一些辅助函数,mapState,mapGetters,mapMutations,mapActions,可使用扩展运算符展开,其中:

      • mapState,mapGetters声明在 computed 中

      • mapMutations,mapActions 声明在 methods 中

    4. 创建 store 实例,核心属性:

      • State:一个对象,包含全部的应用状态。作为唯一数据源存在。store实例会注入到根组件下的所有子组件

      • Getters:state 中数据派生出一些状态类似计算属性,返回值会根据它的依赖被缓存起来,只有依赖发生改变时才会被重新计算,state 作为其第一个参数.

      • Mutations:更改 store 中的状态,store.commit('事件名',payload参数可选)触发,只做同步操作,

      • Actions:类似于Mutations, 提交的是 mutation,而不是直接变更状态,可以包含任意异步操作。this.$store.dispatch分发,异步执行完毕,返回封装的promise 对象。接受一个与 store 实例具有相同方法和属性的对象

      • Modules:为防止 store 对象过于复杂,可将其分割成模块,每个模块都有自己的State,Getters,Mutations,Actions,甚至可以嵌套子模块

    // main.js
    import Vue from 'vue'
    import App from './App.vue'
    import Vuex from 'vuex'
    import numModule from './numModule.js'
    Vue.use(Vuex)
    
    var store = new Vuex.Store({
      modules:{
        numModule
      }
    })
    new Vue({
      store,
      render: h => h(App)
    }).$mount('#app')
    
    // numModule.js
    export default {
        state: {
            num: 1
        },
        getters: {
            getNum(state) { // 可以返回一些派生状态
                return state.num
            }
        },
        mutations: {  
            // 同步修改状态的函数
            changeNum(state, payload) { 
                state.num += payload.num
            },
            // 异步时,开发工具会遗漏快照,不便于调试(不推荐)
            testAsync(state, data) {
                // 模拟服务器获取数据
                setTimeout(function () {
                    state.num += data
                }, 0)
            }
        },
        actions: {
            // 异步获取数据,提交给 mutation 进行修改
            incNumByService(store) {
                setTimeout(function () {
                    var serviceData = 0.1
                    store.commit('changeNum', {
                        num: serviceData
                    })
                }, 0)
            }
        }
    }
    
    // App.vue
    <template>
      <div >
        第一组件
        <p>state:{{$store.state.numModule.num}}</p>
        <!-- getters:方式为只读,数据更安全 -->
        <p>getters:{{$store.getters.getNum}}</p>
        <p>MapGetters:{{gn}}</p>
    
        <button @click="changeNum">mutation同步</button>
        <button @click="testAsync">mutation异步</button>
        <button @click="testActions">action异步</button>
         <Son />
      </div>
    </template>
    
    <script>
    import { mapGetters } from "vuex";
    import Son from "./components/son";
    export default {
      components: {
        Son
      },
      methods: {
        changeNum() {
          this.$store.commit({
            type: "changeNum",
            num: 1
          });
          
          //效果同上
          // this.$store.commit("changeNum",{
          //   num: 1
          // });
        },
        testAsync() {
          this.$store.commit("testAsync", 10);
        },
        testActions() {
          // dispatch 异步执行完毕,返回 封装的promise 对象
          this.$store.dispatch("incNumByService").then(res => {
            alert("dispatch执行完毕");
          });
        }
      },
      computed: {
        //使用对象展开运算符将 getter 混入 computed 对象中
        //如果想将一个 getter 属性另取一个名字,使用对象形式:
        ...mapGetters({
          gn: "getNum"  // 把 `this.gn` 映射为 `this.$store.getters.getNum`
        })
      }
    };
    </script>
    
    // Son.vue
    <template>
      <div >
        第二组件
        <button>state:{{$store.state.numModule.num}}</button>
        <!-- getters:方式为只读,数据更安全 -->
        <button>getters:{{$store.getters.getNum}}</button>
        <button @click="clickMe">MapGetters:{{gn}}</button>
      </div>
    </template>
    
    <script>
    import { mapGetters } from "vuex";
    export default {
      computed: {
        ...mapGetters({
          gn: "getNum"
        })
      }
    };
    </script>
    
    bk
    下一篇:没有了