vue组件之间数据传递的几种方式

一、props $emit 父子组件之间传值

缺点:如果组件嵌套层次多的话,数据传递比较繁琐

1)父组件向子组件传值 props

<body>
    <div id="app">
        <compa :propa='username' :propb='true' :propc="[1,2,3]" :propd='{name:"dny"}' :prope='abc' :propf='["apple","banana"]' proph='默认父组件传递'></compa>
    </div>

    <script>

        const compa={
            props:{
                propa:String,
                propb:{
                    type:Boolean,
                    required:true//必传
                },
                propc:{
                    type:Array
                },
                propd:{
                    type:Object,
                    required:true
                },
                prope:{
                    type:Function,
                    required:true
                },
                propf:{
                    validator:function(value){
                        if(value.indexOf('banana')>-1){
                            return true
                        }else{
                            return false
                        }
                    },
                    required:true
                },
                //设置默认值 如果父组件没有传 就走默认值
                proph:{
                    default(){
                        return '设置默认值'
                    }
                }
            },
            template:`<h1>{{proph}}</h1>`,
            
        }
        var vm=new Vue({
            el:"#app",
            data:{
                username:'dny'
            },
            components:{
                compa
            },
            methods:{
                abc(){
                    
                }
            }
        })
    </script>
</body>

2)子组件向父组件传值 —$emit

<body>
    <div id="app">
        {{countF}}
        <gp-18 @counterchanger='handlerChange'></gp-18>
    </div>
    <script type="text/template" id="gp-18Temp">
        <div>
            <h1>gp-counter</h1>
            <button @click='decrement(1)'>-</button>
            {{count}}
            <button @click='increment(1)'>+</button>
        </div>
    </script>
    <script>
        //全局组件
        Vue.component('gp-18',{
            template:'#gp-18Temp',
            data(){
                return{
                    count:0
                }
            },
            methods:{
                increment(num){
                    this.count++
                    this.$emit('counterchanger',this.count)
                },
                decrement(num){
                    this.count--
                    this.$emit('counterchanger',this.count)
                }
            }
        })
        var vm=new Vue({
            el:'#app',
            data:{
                countF:0
            },
            methods:{
                //子组件的值将作为第一个参数
                handlerChange(num){
                    this.countF=num
                }
            }
            
        })
    </script>
</body>

二、边界 this.$root — this.$parent— this.$refs

缺点:$refs 只会在组件渲染完成之后生效,并且它们不是响应式的。这仅作为一个用于直接操作子组件的“逃生舱”——你应该避免在模板或计算属性中访问 $refs。

  • 在每个 new Vue 实例的子组件中,其根实例可以通过 $root 属性进行访问
  • r o o t root 类似, parent 属性可以用来从一个子组件访问父组件的实例
  • 尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,可以通过 ref 这个 attribute 为子组件赋予一个 ID 引用,如(< compb ref=‘refcomb’ >< /compb >),然后通过this.$refs.refcomb来访问这个子组件实例
<body>
    <div id="app">
        <input type="text" ref='myinput'>
        <compa ></compa>
    </div>

    <script>
        const compb={
            data(){
                return{
                    title:"孙子组件的数据"
                }
            },
            template:'<h2>compb-{{$root.username}}</h2>',
            mounted(){
                //获取根组件的内容
                console.log(this.$root.username)
                //获取父组件的内容
                console.log(this.$parent.title)
                console.log(this.$parent.start())
            }
        }

        const compa={
            props:['user'],
            data(){
                return{
                    title:'父组件的数据'
                }
            },
            template:`
            <h1>
                compa
                <compb ref='refcomb'></compb>
            </h1>`,
            components:{
                compb
            },
            methods:{
                start(){
                    console.log('父组件的方法');
                }
            },
            mounted(){
                console.log(this.$refs.refcomb.title);
            }
        }
        var vm=new Vue({
            el:"#app",
            data:{
                username:'dny'
            },
            components:{
                compa
            },
            mounted(){
                console.log(this.$refs.myinput);
            }
        })
    </script>
</body>

三、provide inject (依赖注入)

缺点:依赖注入将应用程序中的组件与它们当前的组织方式耦合起来,使重构变得更加困难。同时所提供的属性是非响应式的

<body>
    <div id="app">
        <input type="text" ref='myinput'>
        <compa></compa>
    </div>
<!-- 数据不是响应式的 -->
    <script>
        const compb = {
            inject: ['username'],
            template: '<h2>compb-{{username}}</h2>',
            mounted(){
            	console.log(this.foo()) // dny
            }
        }

        const compa = {
            template: `
            <h1>
            compa
            <compb ></compb>
            </h1>`,
            components: {
                compb
            },
        }
        var vm = new Vue({
            el: "#app",
            data: {
                username: 'dny'
            },
            //返回内容在子组件和孙子组件都可以访问到
            provide: function () {
                return {
                    username: this.username,
                    foo:()=>{
                    	return this.username
                    }
                }
            },
        })
    </script>
</body>

四、event-bus 事件总线

不支持响应式

<body>
    <div id="app">
        <compa></compa>
    </div>
    <script>

        var eventbus=new Vue()
        const compb = {
            template: '<h2>compb-</h2>',
            mounted(){
                eventbus.$on('message',function(msg){
                    console.log(msg);
                })
            }

        }

        const compa = {
            template: `
            <h1>
            compa
            <compb ></compb>
            </h1>`,
            components: {
                compb
            },
        }
        var vm = new Vue({
            el: "#app",
            data: {
                username: 'dny'
            },
            components: {
                compa
            },
            methods:{
                handleback(){
                    eventbus.$emit('message','hello world')
                }
            }
        })
    </script>
</body>

五、slot插槽

六、.sync修饰符

在有些情况下,我们可能需要对一个 prop 进行“双向绑定”。不幸的是,真正的双向绑定会带来维护上的问题,因为子组件可以变更父组件,且在父组件和子组件都没有明显的变更来源。这也是为什么我们推荐以 update:myPropName 的模式触发事件取而代之

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../vue.js"></script>
</head>
<body>
    <div id="app">
        <parent-component></parent-component>
    </div>
    <script>
        let ChildComponent={
            props:['title'],
            template:`
            <h1>-Child------</h1>
            `,
            mounted(){
                this.$emit('update:title',"def")
            }
        }
        let ParentComponent={
            template:`
            <div>
                <h1>parent-component-----{{title}}</h1>
                <child-component v-bind:title.sync='title'></child-component>
            </div>
            `,
            data(){
                return{
                    title:"abc"
                }
            },
            components:{
                ChildComponent
            }
        }
        
        var vm= new Vue({
            el:"#app",
            components:{
                ParentComponent
            }
        })
    </script>
</body>
</html>

七、vuex

支持响应式,但是数据的读取和修改需要按照流程来操作,不适合小型项目

<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==='increment'){
                        // this.$store.state.count++
                        //调用mutations的方法
                        // this.$store.commit('increment',{num:3})

                        store.dispatch('add',{num:3})
                    }else{
                        // this.$store.state.count--
                        this.$store.commit('decrement',{num:2})
                    }
                }
            }
        })
        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:{
                add(context,obj){
                    setTimeout(()=>{
                        context.commit('increment',obj)
                    },3000)
                    
                }
            }
        })
        var em=new Vue({
            el:"#app",
            store
        })
    </script>
</body>

猜你喜欢

转载自blog.csdn.net/weixin_44157964/article/details/105262836