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:`

{{proph}}

`
, } 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 类似, rootparent 属性可以用来从一个子组件访问父组件的实例
  • 尽管存在 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:'

compb-{{$root.username}}

'
, mounted(){ //获取根组件的内容 console.log(this.$root.username) //获取父组件的内容 console.log(this.$parent.title) console.log(this.$parent.start()) } } const compa={ props:['user'], data(){ return{ title:'父组件的数据' } }, template:`

compa

`
, 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: '

compb-{{username}}

'
, mounted(){ console.log(this.foo()) // dny } } const compa = { template: `

compa

`
, 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: '

compb-

'
, mounted(){ eventbus.$on('message',function(msg){ console.log(msg); }) } } const compa = { template: `

compa

`
, 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:`
            

-Child------

`
, mounted(){ this.$emit('update:title',"def") } } let ParentComponent={ template:`

parent-component-----{{title}}

`
, 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:``,
            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:`{{$store.state.count}}`
        })
        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>

你可能感兴趣的:(vue)