Vue组件间数据通信的几种方式

1 props emit

Vue组件间的响应式通信方式之一,数据通过props以单向数据流的形式从父组件流动到子组件中。
单向流动性会防止子组件意外改变父级组件的状态,避免应用的数据流向难以理解。
不能处理非父子组件通信的情况

父传子 props

通过bind指令,父组件数据单向下行流向子组件的props

子传父 $emit

子组件抛发事件a并传入数据作为形参,在父组件挂载结点的子组件标签中监听a事件.
由于父级模板里的所有内容都是在父级作用域中编译的,因此a事件的回调函数写在父组件的methods里面.
methods中拿到形参后用this设置给data数据,这样就完成子组件到父组件的传递

    <div id="app">
        <h1>{{username}}</h1>
        <myinput :value="username" @myinput="inputHandler"></myinput>
    </div>
    <script>
        const myinput = ('my-input', {
            props:['username'],
            template: `<input type='text' :value='username' @input="myemit">`,
            methods:{
                myemit(){
                    console.log(this.$el.value);
                    this.$emit("myinput",this.$el.value);
                }
            }
        });
        var vm = new Vue({
            el: "#app",
            data: {
                username: 'aaa'
            },
            methods: {
                inputHandler(val) {
                    this.username = val;
                }
            },
            components: {
                myinput
            }
        });
    </script>

2 边界条件

边界条件主要通过vue的三个实例属性实现根组件,父组件,子组件之间的数据传递

vm.$parent:当前组件的父实例.
vm.$root:组件树的根实例,如果没有父实例,vm.$root指向自己.
vm.$refs:注册过 ref 特性 的所有 DOM 元素和组件实例.

ref 与 $ref
ref用于给子元素或子组件注册引用信息
$ref是非响应式的,在组件渲染完成之后生效,只能用于父组件拿到子组件实例,不能跨级
任意设置了ref属性的子组件标签,都可以在其父组件中通过this.$refs.refvalue拿到对应的实例

<body>
    <div id="app">
        <son ref="getSon"></son>
    </div>
    <script>
        const son = {
            template: `
                <h1>
                    parent-----{{this.$parent.print}}  
                    <br>
                    root-------{{this.$root.print}}
                </h1>
            `,
            data() {
                return {
                    print: "this is son"
                }
            },
        }
        var vm = new Vue({
            el: "#app",
            data: {
                print: 'this is root'
            },
            components: {
                son
            },
            mounted() {
                console.log(this.$refs.getSon.print);
            }
        });
    </script>
</body>

3 依赖注入

作为组件数据传递的一种方式,依赖注入允许我们提供给任何后代组件的数据。
只需要在父组件中通过provide函数返回一个数据对象,就可以在后代组件中使用inject注入provide提供的数据,实现组件数据的通信。
依赖注入的特点在于,不局限于父子组件,不论多深的嵌套关系,都可以用它实现组件数据之间的通信。
还有一点值得注意,依赖注入是非响应式的。

<body>
    <div id="app">
        <son></son>
    </div>
    <script>
        const grandson = {
            inject: ['getApple'],
            template: `
                <h2>get -{{getApple}}- from grandfather</h2>
            `
        }
        const son = {
            template: `
                <h1>
                    <grandson></grandson>    
                </h1>
            `,
            components: {
                grandson
            }
        }
        var vm = new Vue({
            el: "#app",
            data: {
                apple: 'a fresh apple'
            },
            provide: function () {
                return {
                    getApple: this.apple
                }
            },
            components: {
                son
            }
        });
    </script>
</body>

4 eventbus 事件总线

使用一个全局的vue实例作为载体,通过这个载体去监听和抛发事件
将需要传递的数据放入$emit函数的参数列表,只要在某一组件中监听了此事件,就可以通过回调函数拿到此数据。
基于此,通过事件总线,可以实现非父子组件之间的数据通信,如兄弟级、跨级组件通信。

<body>
    <div id="app">
        <son ></son>
        <button @click="giveFoodHandler">抛发事件</button>
    </div>
    <script>
        var eventbus = new Vue();
        const grandson = {
            template: `
                <h2 >get -{{getApple}}- from grandfather</h2>
            `,
            data() {
                return {
                    getApple: ""
                }
            },
            created(){
                eventbus.$on("getFood",(apple)=>{
                    this.getApple = apple;
                })
            }
        }
        const son = {
            template: `
                <h1>
                    <grandson></grandson>    
                </h1>
            `,
            components: {
                grandson
            }
        }
        var vm = new Vue({
            el: "#app",
            data: {
                apple: 'a fresh apple'
            },
            components: {
                son
            },
            methods: {
                giveFoodHandler() {
                    eventbus.$emit("getFood", this.apple);
                }
            }
        });
    </script>
</body>

5 vuex 状态管理

vuex是vue的状态管理模式,它的核心是store仓库对象,它主要用于存放应用的状态.
在Vuex中,更改状态的唯一方式是mutation,mutation是同步的,
异步逻辑需要放在action中,但它最后也需要commit到mutation中实现状态的更改,

使用store的commit与dispatch来进行组件之间的响应式数据状态管理.

<body>
    <div id="app">
        <counter-btn type="decrement"></counter-btn>
        <counter-span></counter-span>
        <counter-btn type="increment"></counter-btn>
    </div>

    <script>
        Vue.component('counter-btn', {
            props: ['type'],
            template: `
            <button @click='handleClick' >{{btntext}}</button>
            `,
            computed: {
                btntext() {
                    return this.type === 'decrement' ? '-' : '+';
                }
            },
            methods: {
                handleClick() {
                    if (this.type === 'decrement') {
                        store.commit('decrement', { num: 1 });
                        return;
                    }
                    this.$store.dispatch('increment', { num: 1 });
                }
            }
        });

        Vue.component('counter-span', {
            template: `<span>{{$store.state.count}}</span>`,
        });

        var store = new Vuex.Store({
            state: {
                count: 0
            },
            //同步
            mutations: {
                increment(state, obj) {
                    state.count += obj.num;
                },
                decrement(state, obj) {
                    state.count -= obj.num;
                }
            },
            // 异步
            actions: {
                increment(state, obj) {
                    setTimeout(() => store.commit('increment', obj), 0);
                }
            }
        });

        var vm = new Vue({
            el: "#app",
            store
        });
    </script>
</body>

总结

pros emit:

优点:
响应式
单向数据流防止子组件意外改变父级组件的状态,常用于父子组件通信

缺点:
但是单行向下的bind+props在父子嵌套过多的场景下显得过于繁琐。
无法运用在非父子组件通信的场景中

边界条件

优点:
使用起来较为方便,在子组件标签上增加ref属性即可使用

缺点:
在组件渲染完成之后生效
非响应式

依赖注入

优点:
不像props emit用起来那么繁琐
祖先组件不需要知道那些后代组件使用它提供的属性,后代组件也不需要知道被注入的属性来自哪里。
不局限于父子组件,不论多深的嵌套关系,都可以用它实现组件数据之间的通信。

缺点:
但这样的方式也有一个明显的缺陷,那就是不支持响应式

事件总线

优点:
eventbus是全局的,意味着可以在任意两个组件中通过事件的抛发和监听来传递数据。

缺陷:
不支持响应式

vuex

优点:
通过store状态管理,可以灵活的在父子/非父子/兄弟组件之间进行响应式数据传递

缺点
刷新浏览器会使得vuex中的state置为初始状态,考虑结合本地存储如localstorage进行使用
如果数据不存在跨组件共享,那么也不建议使用vuex
vuex不适合小型项目

posted @ 2020-04-06 22:01  IslandZzzz  阅读(914)  评论(0编辑  收藏  举报