vue组件传值方式有哪些

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6


Vue 作为一个轻量级的前端框架核心两大特性就是响应式编程和组件化。

本文针对组件之间传值做详细讲解。 Vue就是由一个一个的组件构成的组件化是它的精髓也是最强大的功能之一。而组件实例的作用域是相互独立的这就意味着不同组件之间的数据无法相互引用。

但在实际项目开发过程中我们需要访问其他组件的数据这样就就有了组件通信的问题。在 vue 中组件之间的关系有父子兄弟隔代。针对不同的关系怎么实现数据传递下面展开说明。

如上图所示
父子关系A与BA与CB与DC与E
兄弟关系B与C
隔代关系可能隔更多代A与DA与E
跨级关系B与ED与E等

一、父组件向子组件传值  props

<!--父组件页面-->
<template>
    <div id="app">
        <!--子组件-->
        <child-box :message="params"></child-box> //把params的值传给子组件
    </div>
</template>

<script>
// 引入子组件
import ChildBox from './childBox.vue'

export default {
    // 初始化子组件
    components: {
        ChildBox
    },
    data() {
        return {
            params: 'hello' // 如果params值为 undefined 则会使用default的值
        }
    }
}
</script>
<!--子组件页面-->
<template>
    <div class="child-box">{{ message }}</div>
</template>

<script>
    export default {
        /**
         * props功能让组件接受外部传过来的数据
         * 
         * 1.传递数据 <child-box :message="hello"></child-box>
         * 
         * 2.接收数据
         *   第一种方式(只接收)
         *   props: ['message']
         * 
         *   第二种方式(限制类型)
         *   props: { message: String }  多个 props: { message: String, name: '王新焱'}
         *   
         *   第三种方式(限制类型、限制必要性、指定默认值)  //这种方式更为严谨
         *   props: {
         *      message: {
         *          type: String,
         *          required: true,
         *          default: '王新焱'
         *      }
         *   }
         * 
         *  备注props是只读的Vue底层会监测你对props的修改如果进行了修改就会发出警告若业务需求确实需要修改那么请复制props的内容到data中一份然后去修改data中的数据
         *  
         *  如果要使用默认值 default message值设置为 undefined即可 ->示例<child-box :message="undefined"></child-box>
         * 
         */
        props: {
            message: {
               type: String, // 指定数据类型
               required: true, // 指定数据为必填项
               default: '王新焱' // 指定默认值如果message绑定的值设置为 undefined 即会使用该值
            }
        }
    }
</script>

总结

1.父组件中注册子组件,在子组件标签中添加子组件props中创建的属性,把需要传给子组件的值赋给该属性。

2.子组件在props中创建一个属性用以接收父组件传过来的值。

3.父子组件的关系可以总结为prop向下传递事件向上传递。

4.父组件通过prop给子组件下发数据子组件通过事件给父组件发送信息,这就是单向数据流的表现形式。

二、子组件向父组件传值  $emit

<!-- 父组件页面 -->
<template>
    <div id="app">
        <!-- 引入子组件定义一个v-on的方法监听子组件的状态-->
        <child-box :message="params" @childFnGetParent="parentFn"></child-box>

        <P>{{ msg }}</P>
    </div>
</template>

<script>
// 引入子组件
import ChildBox from './childBox.vue'

export default {
    // 初始化子组件
    components: {
        ChildBox
    },
    data() {
        return {
            params: 'hello',
            msg: ''
        }
    },
    methods: {
        parentFn(val) {
            // val 就是子组件传过来的值
            this.msg = val
        }
    }
}
</script>
<!-- 子组件页面 -->
<template>
    <div class="child">
        <button @click="parentFn">子组件向父组件传值</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                msg: '我是来自子组件的消息'
            }
        },
        methods: {
            parentFn() {
                // childFnGetParent 是在父组件v-on监听的方法第二个参数this.msg是需要传递的值
                this.$emit('childFnGetParent', this.msg)
            }
        }
    }
</script>

总结

1.子组件中需要以某种方式例如点击事件的方法来触发一个自定义事件

2.将需要传的值作为$emit的第二个参数该值将作为实参传给响应自定义事件的方法

3.在父组件中注册子组件并在子组件标签上绑定对自定义事件的监听

4.在通信中无论是子组件向父组件传值还是父组件向子组件传值他们都有一个共同点就是有中间介质子向父的介质是自定义事件父向子的介质是props中的属性。理解这两点对于父子通信就好理解了

子组件通过$emit()方法发布事件广播

父组件捕获到子组件向外触发的事件然后可执行相应的方法

三、非父子组件传值  EventBus

非父子组件最常用的是EventBus方案进行数据传递定义方式有三种

/**
 * 方法一 抽离成一个单独的 js 文件 EventBus.js 然后在需要的地方引入
 * EventBus.js
 */
import Vue from "vue"
export default new Vue()

/**
 * 方法二 直接挂载到全局
 * main.js
 */
import Vue from "vue"
Vue.prototype.$bus = new Vue()

/**
 * 方法三 注入到 Vue 根对象上
 * main.js
 */
import Vue from "vue"
new Vue({
    el:"#app",
    data:{
        Bus: new Vue()
    }
})

本文案例以第一种方法 新建 EventBus 展开讲述

1.新建公共文件 EventBus.js

/**
 * EventBus.js
 * 
 * eventBus 又称为事件总线在vue中可以使用它来作为沟通桥梁的概念, 就像是所有组件共用相同的事件中心
 * 
 * 不管是父子组件兄弟组件跨层级组件等都可以使用它完成通信操作
 */

import Vue from 'vue'

// 创建一个 EventBus.js 文件暴露一个 vue 实例
export default new Vue()

2.新建一个A组件

<!--组件A-->
<template>
    <div class="child-A">
        <span>年龄{{ value }}</span>
        <button @click="getValue">修改年龄</button>
    </div>
</template>

<script>
import Bus from './EventBus.js'
export default {
    data() {
        return {
            value: 30
        }
    },
    methods: {
        getValue() {
            // 在A组件中通过$emit向外部发送自定义事件 (事件广播)
            Bus.$emit('getVal', this.value)
        }
    }
}
</script>

3.新建一个B组件

<!--组件B-->
<template>
    <div class="child-B">
        <span>年龄{{ age }}</span>
        <button @click="getData">点击触发</button>
    </div>
</template>

<script>
import Bus from './EventBus.js'
export default {
    data() {
        return {
            age: 18
        }
    },
    mounted() {
        // B组件 用$on事件来接收外部事件
        Bus.$on('getVal', (data)=> {
            this.age = data // data为广播过来值
        })
    },
    methods: {
        getData() {
            this.age++
        }
    }
}
</script>

使用 EventBus 有一个弊端就是事件广播这种方式不会自动销毁所以避免回调函数重复执行需要在destroyed生命周期中销毁广播事件

destroyed() {
    Bus.$off('eventName')  // 对Bus取消事件监听后 内存得到了释放
}

总结

$off() 会取消所有的事件订阅

$off('事件名') 会取消指定事件名的

$off('事件名', 回调) 会取消指定事件名的指定回调

四、多层父子组件通信 (依赖注入)  provide / inject

有时需要实现通信的两个组件不是直接的父子组件而是祖父和孙子或者是跨越了更多层级的父子组件这种时候就不可能由子组件一级一级的向上传递参数特别是在组件层级比较深嵌套比较多的情况下需要传递的事件和属性较多会导致代码很混乱。

这时就需要用到 vue 提供的更高阶的方法provide/inject

provide/inject简单来说就是在父组件中通过provider来提供变量然后在子组件中通过inject来注入变量不管组件层级有多深只要调用了inject 那么就可以注入provide中的数据。

<!--父组件页面-->
<template>
    <div class="box">
        <!-- 引入子组件A-->
        <child-a></child-a>

        <!-- 引入子组件B-->
        <child-b></child-b>
    </div>
</template>

<script>
export default {
    provide: {
        name: '张三丰', // 将变量name提供给它的所有子组件及后代组件
        reload: this.reload // 也可以是一个函数
    },
    data() {
        return {
            isShow: false
        }
    },
    methods: {
        reload() {
            this.isShow = true
        }
    }
}
</script>
<!--组件A-->
<template>
    <div class="child-A">
        <div>这是组件A</div>

        <!-- 输出张三丰 -->
        <div>{{ name }}</div>
    </div>
</template>

<script>

export default {
    inject: ['name'], // 注入了从父组件中提供的name变量

    mounted() {
        console.log(this.name) // 张三丰
    }
}
</script>
<!--组件B-->
<template>
    <div class="child-B">
        <div>这是组件B</div>
        
        <!-- 输出张三丰 -->
        <div>{{ name }}</div>
    </div>
</template>

<script>
export default {
    inject: ['name'], // 注入了从父组件中提供的name变量

    mounted() {
        console.log(this.name) // 张三丰
    }
}
</script>

注provide 和 inject 绑定并不是可响应的。即父组件的name变化后子组件不会跟着变。

五、通过访问组件实例的方式  ref

父组件通过 ref获取子组件的实例 可以直接访问子组件里面的方法和属性

<!--父组件页面-->
<template>
    <div class="box">
        <!-- 引入子组件A-->
        <child-a ref="childA"></child-a>

        <button @click="getChildData">获取子组件的属性和方法</button>
    </div>
</template>

<script>
// 引入子组件
import ChildA from './childBox.vue'

export default {
    // 初始化子组件
    components: {
        ChildA
    },
    methods: {
        getChildData() {
            // 获取子组件的属性
            console.log(this.$refs.childA.txt) //我是子组件A

            // 获取子组件的方法
            console.log(this.$refs.childA.getName()) //张三丰
        }
    }
}
</script>
<!--组件A-->
<template>
    <div class="child-A">
        <div>这是组件A</div>
    </div>
</template>

<script>

export default {
    data() {
        return {
            txt: '我是子组件A',
            name: '张三丰'
        }
    },
    methods: {
        getName () {
            console.log(this.name)
        } 
    }
}
</script>

注这种方式的组件通信不能跨级

六、$children / $parent

$children获取到一个组件实例包含所有子组件(不包含孙子组件)的 VueComponent 对象数组可以直接拿到子组件中所有数据和方法等

$parent获取到一个组件实例包含父节点的 VueComponent 对象同样包含父节点中所有数据和方法

$children 方法讲解

<!--父组件页面-->
<template>
    <div class="box">
        <!-- 引入子组件A-->
        <child-a ref="childA"></child-a>

        <!-- 引入子组件A-->
        <child-b></child-b>

        <button @click="getChildData">获取子组件的属性和方法</button>
    </div>
</template>

<script>
// 引入子组件
import ChildA from './childBox.vue'
import ChildB from './childBox2.vue'

export default {
    // 初始化子组件
    components: {
        ChildA,
        ChildB
    },
    methods: {
        getChildData() {
            // 获取第一个组件的name
            console.log(this.$children[0].name) //张三丰
            
            // 获取第一个组件getTitle方法
            console.log(this.$children[0].getTitle()) //我是子组件A
            
            // 获取第二个组件的name
            console.log(this.$children[1].name) //张无忌
        }
    }
}
</script>
<!--组件A-->
<template>
    <div class="child-A">
        <div>这是组件A</div>
    </div>
</template>

<script>

export default {
    data() {
        return {
            txt: '我是子组件A',
            name: '张三丰'
        }
    },
    methods: {
        getTitle () {
            console.log(this.txt)
        } 
    }
}
</script>
<template>
    <div class="child-B">
        <div>这是组件B</div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            txt: '我是子组件B',
            name: '张无忌'
        }
    }
}
</script>

$parent 方法讲解

<!--父组件页面-->
<template>
    <div class="box">
        <!-- 引入子组件A-->
        <child-a ref="childA"></child-a>
    </div>
</template>

<script>
// 引入子组件
import ChildA from './childBox.vue'

export default {
    // 初始化子组件
    components: {
        ChildA
    },
    data() {
        return {
            title: '这是父页面',
            name: '父组件',
            age: 20
        }
    },
    methods: {
        getAge() {
            console.log(this.age)
        }
    }
}
</script>
<!--组件A-->
<template>
    <div class="child-A">
        <div>这是组件A</div>

        <button @click="getParentData">A组件获取父组件的属性和方法</button>
    </div>
</template>

<script>

export default {
    methods: {
        getParentData() {
            console.log(this.$parent.title) //获取父页面title属性
            console.log(this.$parent.getAge()) //执行父页面getAge方法
        }
    }
}
</script>

七、slot 插槽传值

子组件的数据通过插槽的方式传给父组件使用要显示内容由父组件决定

<!--父组件页面-->
<template>
    <div class="box">
        <!--引入子组件A-->
        <child-a ref="childA" v-slot="slotProps">
            <!--父组件使用slot插槽方式获取子组件数据-->  
            {{ slotProps.user.name }}
            
            {{ name }}
        </child-a>
    </div>
</template>

<script>
// 引入子组件
import ChildA from './childBox.vue'

export default {
    // 初始化子组件
    components: {
        ChildA
    },
    data() {
        return {
            name: '张三丰'
        }
    }
}
</script>
<!--组件A-->
<template>
    <div class="child-A">
        <!-- <div>这是组件A</div> -->
        <slot :user="user"></slot>
    </div>
</template>

<script>

export default {
    data() {
        return {
            user: {
                name: '张无忌'
            }
        }
    }
}
</script>

以上几种也是vue组件传值主流的方式了根据业务的不同场景选择不同的方式此外vuex也是组件传值最常用的方式我计划把这个知识点单独列出来进行讲解后续会附上链接。

友情赠送一张小图

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: vue