组件(Component)是 Vue.js 最强大的功能之一。
组件可以扩展 HTML 元素,封装可重用的代码。
组件系统让我们可以用独立可复用的小组件来构建大型应用,几乎任意类型的应用的界面都可以抽象为一个组件树:
props
和 $emit
使用props,父组件可以使用props向子组件传递数据。
<template> <son :message="son"></son> </template> <script> import son from ‘./son.vue‘; export default { components: { son }, data () { return { message: ‘father message‘; } } } </script>
<template> <div>{{message}}</div> </template> <script> export default { /** * 得到父组件传递过来的数据 * 这里的定义最好是写成数据校验的形式,免得得到的数据是我们意料之外的 * * props: { * message: { * type: String, * default: ‘‘ * } * } * */ props:[‘message‘], } </script>
父组件向子组件传递事件方法,子组件通过$emit触发事件,回调给父组件。
<template> <son @getChildData="getChildData"></son> </template> <script> import son from ‘./son.vue‘; export default { components: { son }, methods: { // 执行子组件触发的事件 getChildData(val) { console.log(val); } } } </script>
<template> <div> <input type="text" v-model="myMessage" @input="passData(myMessage)"> </div> </template> <script> export default { props: { msg: { type: String, required: true } }, data() { return { // 这里是必要的,因为你不能直接修改 props 的值 myMessage: this.message } }, methods () { passData(val) { // 数据状态变化时触发父组件中的事件 this.$emit(‘getChildData‘, val); } } } </script>
在上面的例子中,有父组件 father 和子组件 son。
第一种方式处理父子组件之间的数据传输有一个问题:如果父组件A下面有子组件B,组件B下面有组件C,这时如果组件A想传递数据给组件C怎么办呢?
如果采用第一种方法,我们必须让组件A通过prop传递消息给组件B,组件B在通过prop传递消息给组件C;要是组件A和组件C之间有更多的组件,那采用这种方式就很复杂了。Vue 2.4开始提供了$attrs和$listeners来解决这个问题,能够让组件A之间传递消息给组件C。
$attrs
:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。
$listeners
:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
<template> <div> <p>我是父组件</p> <son :son="son" :grandSon="grandSon" @getSonData="getSonData" @getGrandSonData="getGrandSonData"></son> </div> </template> <script> import son from ‘./son.vue‘; export default { components: { son }, data() { return { son: ‘爷爷的儿子‘, grandSon: ‘爷爷的孙子‘ } }, methods: { // 来自子组件触发的事件 getSonData(val) { console.log(val) }, // 来自孙组件触发的事件 getGrandSonData(val) { console.log(val) } } } </script>
<template> <div> <p>我是子组件</p> <input type="text" v-model="myMessage" @input="passData(myMessage)"> <grandson :child="child" v-bind="$attrs" v-on="$listeners"></grandson> </div> </template> <script> import son from ‘./son.vue‘; export default { components: { grandson }, props: [‘son‘], data() { return { child: ‘爸爸的儿子‘, myMessage: ‘‘ } }, // 默认为true,如果传入的属性子组件没有prop接受,就会以字符串的形式出现为标签属性 // 设为false,在dom中就看不到这些属性,试一下就知道了 inheritAttrs: false, created () { // 在子组件中打印的$attrs就是父组件传入的值,刨去style,class,和子组件中已props的属性 console.log(this.$attrs) // 打印爷爷的孙子 }, methods:{ passData(val) { // 触发父组件中的事件 this.$emit(‘getSonData‘, val) } } } </script>
<template> <div> <p>我是孙组件</p> <input type="text" v-model="myMessage" @input="passData(myMessage)"> {{$attrs.grandSon}} {{$attrs.child}} </div> </template> <script> export default { data() { return { myMessage: ‘‘ } }, inheritAttrs: false, created () { // 打印爷爷的孙子和爸爸的儿子 console.log(this.$attrs) }, methods:{ passData(val) { // 触发父组件中的事件 this.$emit(‘getGrandSonData‘, val) } } } </script>
在上面的例子中,我们定义了 父,子,孙 三个组件,其中组件子是组件父的子组件,组件孙是组件子的子组件。
父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input‘, val)自动修改v-model绑定的值
<template> <div> <p>我是父组件</p> {{message}} <son v-model="message"></son> </div> </template> <script> import son from ‘./son.vue‘; export default { components: { son }, data() { return { message: ‘我的儿子‘ } } } </script>
<template> <div> <p>我是子组件</p> <input type="text" v-model="myMessage" @input="passData(myMessage)"> </div> </template> <script> export default { //v-model会自动传递一个字段为value的prop属性 props: [‘value‘], data() { return { myMessage: this.message } }, methods:{ passData(val) { //通过如此调用可以改变父组件上v-model绑定的值 this.$emit(‘input‘, val) } } } </script>
在上面的实例代码中,我们定义了 father 和 son 两个组件,这两个组件是父子关系,v-model 也只能实现父子组件之间的通信。
v-model
绑定了 myMessage 属性和一个 input 事件。当 input 值变化时,就会触发 input 事件,处理 father 组件通过 v-model
给 son 组件绑定的 input
事件,触发 father 组件中 message
属性值的变化,完成 child
子组件改变 father 组件的属性值。父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
<template> <div> <p>我是父组件</p> </div> </template> <script> export default { provide:{ message: ‘你是我孙子‘ } } </script>
<template> <div> <p>我是子组件</p> {{message}} //会显示中午一起吃饭 </div> </template> <script> export default { inject:[‘message‘],//得到父组件传递过来的数据 } </script>
在上面的实例中,我们定义了组件 father
和组件 son
,组件 father 和组件 son 是父子关系。
provide
属性,以对象的形式向子孙组件暴露了一些属性inject
属性注入了 father 组件提供的数据,实际这些通过 inject
注入的属性是挂载到 Vue 实例上的,所以在组件内部可以通过 this 来访问。$parent
和 $children
这里要说的这种方式就比较直观了,直接操作父子组件的实例。$parent
就是父组件的实例对象,而 $children
就是当前实例的直接子组件实例了,不过这个属性值是数组类型的,且并不保证顺序,也不是响应式的。
<template> <div> <p>我是父组件</p> <button @click="changeChildValue">test</button> <son></son> <grandson></grandson> </div> </template> <script> import son from ‘./son.vue‘ import grandson from ‘./grandSon.vue‘ export default { components: { son, grandSon }, data() { return { fatherMessage: ‘‘ } } methods: { changeChildValue(){ this.$children[0].sonMessage = ‘hello‘; } } } </script>
<template> <div> <p>我是子组件</p> <input type="text" v-model="mymessage" @change="changeValue" /> </div> </template> <script> export default { data() { return { mymessage: this.$parent.fatherMessage } }, methods: { changeValue(){ this.$parent.fatherMessage = this.mymessage;//通过如此调用可以改变父组件的值 } } } </script>
this.$children[0].sonMessage = ‘hello‘;
给 son
组件内的 sonMessage
属性赋值,而在 son 子组件中,同样也是直接通过this.$parent.fatherMessage
给 father
组件中的 fatherMessage 赋值,形成了父子组件通信。
原文:https://www.cnblogs.com/Mr-Tao/p/11515988.html