美文网首页
vue 组件通讯

vue 组件通讯

作者: 简单tao的简单 | 来源:发表于2019-08-02 10:37 被阅读0次

    组件之间的通讯简述:
    父子组件的关系可以总结为 props 向下传递,事件event向上传递
    祖先组件和后代组件(跨多代)的数据传递,可以使用provide和inject来实现
    跨组件或者兄弟组件之间的通信,可以通过eventBus或者vuex等方式来实现

    1. 页面级组件传值
      • 通过路由带参数进行传值
      • 通过设置 sessionStorage缓存的形式进行传递
    2. 父子组件传值
      • 父传子props,子传父$emit
        • .sync
        • v-model
      • $parent$children
    3. 后代组件传值
      • provide / inject
      • $attrs$listeners
    4. 跨组件或者兄弟组件之间传值
      • 小项目少页面用eventBus
      • 大项目多页面使用 vuex

    页面级组件传值

    1. 通过路由带参数进行传值

    //两个组件 A和B,A组件通过query把orderId传递给B组件(触发事件可以是点击事件、钩子函数等)
    this.$router.push({ path: '/f4', query: { orderId: 123 } }) //跳转到f4
    //在B组件中获取A组件传递过来的参数
    this.$route.query.orderId
    
    <router-link to="/f4?orderId=123">router-link 进入Father4</router-link>
    <router-view></router-view>
    

    2. 通过设置 Session Storage缓存的形式进行传递

    //两个组件A和B,在A组件中设置缓存orderData
    const orderData = { 'orderId': 123, 'price': 88 }
    sessionStorage.setItem('orderInfo', JSON.stringify(orderData))
    
    //B组件就可以获取在A中设置的缓存了
    const orderInfo = JSON.parse(sessionStorage.getItem('orderInfo'))
    

    父子组件传值

    1. 父传子props,子传父$emit

    • .sync
    //父组件
    <template>
        <div>
            父组件:{{title}}
            <childDom :title.sync="title"></childDom>
        </div>
    </template>
    
    <script>
    import childDom from '../components/Father3-Child'
    export default {
        data(){
            return {
                title:'简单我的简单'
            }
        },
        components:{childDom}
    }
    </script>
    
    //子组件
    <template>
        <div>
            <div>子组件:{{title}}</div>
            <button @click="$emit('update:title','哈哈哈')">点我更新父组件的数据</button>
        </div>
    </template>
    
    <script>
    export default {
        props:['title'],
    }
    </script>
    
    • v-model
    //父组件
    <template>
      <div>
        <aa class="abc" v-model="test" ></aa> 
        {{'外面的值:' + test}}
        <button @click="fn">外面改变里面</button> 
      </div>
    </template>
    
    <script>
      import aa from './aa'
      export default {
        data () {
          return {
            test: ''
          }
        },
        methods: {
          fn () {
            this.test += 1 
          }
        },
        components:{
          aa
        }
      }
    </script>
    
    //子组件
    <template>
      <div>
        {{'里面的值:'+ msg}}
        <button @click="fn2">里面改变外面</button>
      </div>
    </template>
    
    <script>
      export default {
        /**
         * 使用model, 这里有2个属性
         * prop属性说,父组件的v-model的值就是msg
         * event说,我emit ‘cc’ 的时候,父组件v-model的值就是参数的值
         */
        model: {
          prop: 'msg',
          event: 'cc'
        },
        props: {
          msg: ''
        },
        methods: {
          fn2 () {
            this.$emit('cc', this.msg+2)
          }
        }
      }
    </script>
    

    2. $parent$children

    在组件内部可以直接通过子组件$parent对父组件进行操作,父组件通过$children对子组件进行操作.

    //父组件
    <template>
        <div>
            <p>fatherMessage:{{fatherMessage}}</p>
            <button @click="changeChildValue">改变子组件的数据childMessage</button >
            <child></child>
            <child2></child2>
        </div>
    </template>
    
    <script>
    import child from '../components/Father4-Child'
    import child2 from '../components/Father4-Child2'
    export default {
        data(){
          return {
            fatherMessage:'hello'
          }
        },
        components:{child,child2},
        methods:{
            changeChildValue(){
                this.$children[0].childMessage = 'hello';
                this.$children[1].childMessage = 'hello2';
            }
        }
    }
    </script>
    
    //子组件1
    <template>
        <div class="container">
            child
            <input type="text" v-model="childMessage" @input="changeValue">
        </div>
    </template>
    
    <script>
    export default {
        // props:{
        //   value:String, //v-model会自动传递一个字段为value的prop属性
        // },
        data(){
          return {
            childMessage:this.value
          }
        },
        methods:{
          changeValue(){
            this.$parent.fatherMessage = this.childMessage;//通过如此调用可以改变父组件的值
          }
        },
    }
    </script>
    
    //子组件2
    <template>
        <div class="container">
            child2
            <input type="text" v-model="childMessage" @input="changeValue">
        </div>
    </template>
    
    <script>
    export default {
        // props:{
        //   value:String, //v-model会自动传递一个字段为value的prop属性
        // },
        data(){
          return {
            childMessage:this.value
          }
        },
        methods:{
          changeValue(){
            this.$parent.fatherMessage = this.childMessage;//通过如此调用可以改变父组件的值
          }
        },
    }
    </script>
    

    后代组件传值(父组件与子、孙子、曾孙子组件传值)

    1. provide / inject

    provide / inject 可以以一个祖先组件向所有子孙后代注入依赖。
    简单的来说就是在父组件中通过provide来提供变量,然后在后代组件中通过inject来注入变量,不仅限于prop的父子组件数据交互,只要在上一层级的声明的provide,那么下一层级无论多深都能够通过inject来访问到provide的数据

    //父组件
    <template>
        <div>
            <son></son>
        </div>
    </template>
    
    <script>
    import son from '../components/son';
    export default {
        provide: {
            foo: 'bar'
        },
        components:{son}
    }
    </script>
    
    //子组件
    <template>
        <div>
            我是子组件{{foo}}
            <grandson></grandson>
        </div>
    </template>
    
    <script>
    import grandson from '../components/grandson'
    export default {
        inject: ['foo'],
        components:{grandson}
    }
    </script>
    
    //孙子组件
    <template>
        <div>
            我是孙子组件{{foo}}
        </div>
    </template>
    
    <script>
    export default {
        inject: ['foo'],
    }
    </script>
    
    通过 provide / inject 后代组件都可以访问父组件的数据

    2. $attrs$listeners

    通过$attrs属性将父组件的数据(不作为 prop 被识别的数据)传递给子组件、孙子组件、曾孙子组件等后代组件

    $listeners属性是一个对象,里面包含了作用在这个组件上的所有监听器,我们在子组件上绑定v-on=”$listeners”, 就可以在父组件中监听孙子组件、曾孙子组件触发的事件,就能把孙子组件、曾孙子组件等后代组件发出的数据,传递给父组件。

    //父组件
    <template>
     <div>
        {{coo}}
       <child-dom
        :foo="foo"
        :coo="coo"
         @upRocket="reciveRocket">
       </child-dom>
     </div>
    </template>
    <script>
     import childDom from "../components/Father2-Child";
     export default {
       name:'demoNo',
       data() {
            return {
                foo:"Hello, world",
                coo:"Hello,rui"
            }
        },
        components:{childDom},
        methods:{
            reciveRocket(ev){
                this.coo = ev;//改变数据
                console.log(this.coo)
            }
        }
    }
    </script>
    
    //子组件
    <template>
        <div>
            <p>foo:{{foo}}</p>
            <p>attrs:{{$attrs}}</p>
            <childDomChild v-bind="$attrs" v-on="$listeners"></childDomChild>
        </div>
    </template>
    <script>
    import childDomChild from '../components/Father2-Child-Child';
    export default {
        props:["foo"],
        components:{childDomChild}
    }
    </script>
    
    //孙子组件
    <template> 
        <div>
        <p>coo:{{$attrs.coo}}</p>
        <button @click="startUpRocket">我要发射火箭</button>
        </div>
    </template>
    <script>
     export default {
        methods:{
            startUpRocket(){
                this.$emit("upRocket",'简单我的简单');
            }      
        }
     }
    </script>
    

    跨组件或者兄弟组件之间传值

    小项目少页面用eventBus

    总结:
    EventBus 又称为事件总线,在Vue中可以使用 EventBus 来作为沟通桥梁,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所有组件都可以上下平行地通知其他组件
    在需要传值的组件中用eventBus.$emit触发一个自定义事件,并传递参数
    在需要接收数据的组件中用eventBus.$on监听自定义事件,并在回调函数中处理传递过来的参数
    EventBus原理是发布订阅
    eventBus容易导致命名冲突,所以大项目要用vuex

    <template>
        <div>
            <brother1></brother1>
            <brother2></brother2>
        </div>
    </template>
    
    <script>
    import {eventBus} from '../components/eventBus.js';
    let brother1 = {
        template:'<div>{{color}} <button @click="fnEmit2">变红</button></div>',
        data(){
            return {color:'红色',old:'红色'}
        },
        created(){
            eventBus.$on('changeColor1',(val)=>{ // 绑定自定义事件
                this.color = val;
            })
        },
        methods:{
            fnEmit2(){
                eventBus.$emit('changeColor2',this.old) // 触发自定义事件
            }
        }
    }
    let brother2 = {
        template:'<div>{{color}} <button @click="fnEmit1">变绿</button></div>',
        data(){
            return {color:'绿色',old:'绿色'}
        },
        created(){
            eventBus.$on('changeColor2',(val)=>{
                this.color = val;
            })
        },
        methods:{
            fnEmit1(){
                eventBus.$emit('changeColor1',this.old)
            }
        }
    }
    
    export default {
        components:{
            brother1,brother2
        }
    }
    
    </script>
    
    //eventBus.js
    import Vue from 'vue';
    export const eventBus = new Vue();
    

    大项目多页面使用 vuex

    vuex主要借鉴了flux redux,vuex只能在vue中使用(单独为vue开发的)
    vuex为大型项目而生,主要是(状态)管理,状态指的是数据,将数据统一管理
    每一个vuex应用的核心是store,store是一个容器
    不能直接改变store中的状态,通过commit一个mutation来更改状态

    vuex计数器项目架构图
    //main.js
    import Vue from 'vue';
    import App from './App.vue';
    import store from './store'
    
    //计数器
    new Vue({
        el:'#app',
        ...App,
        store, //store被注册到了实例上 所有组件都会有this.$store这个属性
    })
    
    //store/index.js
    import Vue from 'vue';
    import Vuex from 'vuex';
    import logger from 'vuex/dist/logger'; //logger是一个日志插件
    Vue.use(Vuex);
    //容器是唯一的 不可以更改 所以用const比较好 
    const state={count:0}; 
    const getters={ //相当于computed
        val:(state)=>state.count%2 ? '奇数' : '偶数'
    };
    import mutations from './mutations';
    export default new Vuex.Store({
        state,
        mutations,
        getters, 
        plugins:[logger()],
        strict:true //严格模式 只能通过mutation(管理员)来更改状态,mutation不支持异步
    })
    
    //App.vue
    <template>
        <div>
            <Counter></Counter>
        </div>
    </template>
    
    <script>
    import Counter from './components/Counter'
    export default {
        data(){
            return {
                msg:'hello',
            }
        },
        components:{
            Counter
        }
    }
    </script>
    
    //Counter.vue
     <template>
         <div>
             计数器:<button @click="reduce">-</button>
             <br>
             当前:{{$store.state.count}}<br>
             计数器:<button @click="add">+</button><br>
             {{$store.getters.val}}
         </div>
     </template>
     
     <script>
     import * as Types from '../store/mutations-types'; 
     export default {
        data(){
            return {
                msg:'hello',
            }
        },
        methods:{
            add(){ 
                this.$store.commit(Types.INCREMENT,2); //提交add的mutation, 2是载荷payload
            },
            reduce(){
                this.$store.commit(Types.DECREMENT,1);
            }
        }
     }
     </script>
    
    // store/mutations.js
    import * as Types from './mutations-types';
    const mutations={
        [Types.INCREMENT](state,count){ //state是自动放入的,默认指的就是当前的state
            state.count+=count;
        },
        [Types.DECREMENT](state,count){
            state.count-=count;
        }
    };
    export default mutations;
    
    // store/mutations-types.js
    export const INCREMENT = 'INCREMENT';
    export const DECREMENT = 'DECREMENT';
    

    相关文章

      网友评论

          本文标题:vue 组件通讯

          本文链接:https://www.haomeiwen.com/subject/guserctx.html