VUE2-多种组件间传值的方法

141 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

父->子

1-props

  • 子组件接收到数据之后,不能直接修改父组件的数据。
<template>
    <div>
        <Child :msg="msg" :name="name"></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            msg: '父组件',
            name: 'Father'
        }
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
        <div>{{name}}</div>
    </div>
</template>
<script>
export default {
    data() {
        return {
        }
    },
    props: ['msg', 'name']
}
</script>
  • 这里的prop是只读属性,可以传递数组也可以传递对象,如果是传递对象形式的:
props: {
  msg: Number,
  name: {
      type: String, // 数据类型
      default: 'Hello', // 默认值
      required: true, // 是否必填
      validator: function (value) { // 自定义校验
          return value.length > 3
      }
  },
  obj: {
      type: Object,
      default: ()=> {}
  },
  arr: {
      type: Array,
      default: ()=> []
  }
}

2-.sync

<template>
    <div>
        <Child :msg.sync="msg"></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            msg: '父组件',
        }
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
        <button @click="changeMsg">changeMsg</button>
    </div>
</template>
<script>
export default {
    props: ['msg'],
    methods: {
        changeMsg() {
            this.$emit("update:msg", '子组件')
        }
    },
}
</script>

3-v-model

<template>
    <div>
        <Child v-model="msg"></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            msg: '父组件',
        }
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
        <button @click="changeMsg">changeMsg</button>
    </div>
</template>
<script>
export default {
    props: ['msg'],
    model:{
        prop:'msg',
        event:"updateValue"
    },
    methods: {
        changeMsg() {
            this.$emit("updateValue", '子组件')
        }
    },
}
</script>

4-ref

  • ref 如果在普通的DOM元素上,引用指向的就是该DOM元素; 如果在子组件上,引用的指向就是子组件实例;
  • 父组件可以通过 ref 主动获取子组件的属性或者调用子组件的方法
<template>
    <div>
        <button @click="transfer">向子组件传值</button>
        <hr>
        <Child ref="child"></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    methods: {
        transfer() {
            this.$refs.child.msg = '我是父组件'
            this.$refs.child.countAdd(5)
        }
    },
}
</script>
<template>
    <div>
        <div>{{count}}</div>
        <div>{{msg}}</div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            count: 0,
            msg: '我是子组件'
        }
    },
    methods: {
        countAdd(val) {
            this.count += val
        }
    },
}
</script>

子->父

5-$emit

<template>
    <div>
        <div>{{msg}}</div>
        <hr>
        <Child @getMsg="getMsg"></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            msg: '我是父组件'
        }
    },
    methods: {
        getMsg(val) {
            this.msg = val
        }
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
        <button @click="transfer">transfer</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg: '我是子组件'
        }
    },
    methods: {
        transfer() {
            this.$emit('getMsg', this.msg)
        }
    },
}
</script>

6-slot

<template>
    <div>
        <div>{{title}}</div>
        <hr>
        <Child>
            <template v-slot:test1="slotProps">
                <div>在父组件渲染子组件的值 {{slotProps.obj.msg}}</div>
            </template>
            <template v-slot:test2="slotProps">
                <div>在父组件渲染子组件的值 {{slotProps.obj.msg}}</div>
            </template>
        </Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            title: '我是父组件'
        }
    },
}
</script>
<template>
    <div>
        <slot name="test1" :obj="obj"></slot>
        <slot name="test2" :obj="obj2"></slot>
    </div>
</template>
<script>
export default {
    data() {
        return {
            obj: {
                msg: '我是子组件'
            },
            obj2: {
                msg: '我是子组件2'
            }
        }
    },
}
</script>

父->子/孙

7-$attrs / $listeners

  • $attrs获取 父传子中未在 props 定义 的值
  • 父组件的方法 可以通过 v-on="$listeners" 传入内部组件

其实attrsattrs和listeners相当于是一个中转,主要用在父亲组件上。爷组件和孙组件保持以前的使用即可!

<template>
    <div>
        <div>{{title}}</div>
        <hr>
        <Child :title="title" :msg="title"  @changeTitle="changeTitle"></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            title: '我是父组件'
        }
    },
    methods: {
        changeTitle() {
            this.title = '新 标题'
        }
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
        <button @click="getFather">getFather</button>
        <Sun v-bind="$attrs" v-on="$listeners"></Sun>
    </div>
</template>
<script>
import Sun from './Sun.vue'
export default {
    components: {
        Sun
    },
    // props: ['title'], // 这里可以接收,也可以不接收(Sun.vue 同理)
    data() {
        return {
            msg: '我是子组件'
        }
    },
    methods: {
        getFather() {
            console.log(this.$attrs);  // 如果props接收了title 就是 { msg: "我是父组件" },否则就是 { title: "我是父组件", msg: "我是父组件" }
            this.msg = this.$attrs.msg
            this.$listeners.changeTitle()
        }
    },
}
</script>
<template>
    <div>
        <hr>
        <div>{{msg}}</div>
        <button @click="getFather">getFather</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg: '我是孙组件'
        }
    },
    // props: ['title'], // 这里可以接收,也可以不接收
    methods: {
        getFather() {
            console.log(this.$attrs);
            console.log(this.$listeners);
            this.$listeners.changeTitle()
        }
    },
}
</script>

8-provide / inject

  • provide / inject 是依赖注入,在一些插件或组件库里被常用
  • provide:可以让我们指定想要提供给后代组件的数据或方法
  • inject:在任何后代组件中接收想要添加在这个组件上的数据或方法,不管组件嵌套多深都可以直接拿来用
  • 要注意的是 provide 和 inject 传递的数据不是响应式的
<template>
    <div>
        <div>{{title}}</div>
        <hr>
        <Child></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            title: '我是父组件'
        }
    },
    provide() {
        return {
            title: this.title,
            changeTitle: this.changeTitle
        }
    },
    methods: {
        changeTitle() {
            this.title = '新 标题'
        },
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
        <button @click="getFather">getFather</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg: '我是子组件'
        }
    },
    inject: ["title", "changeTitle"],
    methods: {
        getFather() {
            console.log(this.title)
            this.changeTitle()
        }
    },
}
</script>

父 <-->子

9-$children / $parent

  • $children:获取到一个包含所有子组件(不包含孙子组件)的 VueComponent 对象数组,可以直接拿到子组件中所有数据和方法等
  • $parent:获取到一个父节点的 VueComponent 对象,同样包含父节点中所有数据和方法等
<template>
    <div>
        <div>{{title}}</div>
        <button @click="getChild">getChild</button>
        <hr>
        <Child :title="title"></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
export default {
    components: {
        Child
    },
    data() {
        return {
            title: '我是父组件'
        }
    },
    methods: {
        changeTitle() {
            this.title = '新 标题'
        },
        getChild() {
            this.$children[0].changeMsg() // 调用第一个子组件的方法
            this.title = this.$children[0].msg // 获取第一个子组件中的属性
        }
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
        <button @click="getFather">getFather</button>
    </div>
</template>
<script>
export default {
    data() {
        return {
            msg: '我是子组件'
        }
    },
    methods: {
        changeMsg() {
            this.msg = '新 子组件标题'
        },
        getFather() {
            this.$parent.changeTitle() // 调用父组件的方法
            this.msg = this.$parent.title // 获取父组件中的属性
        }
    },
}
</script>

others

10-EventBus

  • EventBus 是中央事件总线,不管是父子组件,兄弟组件,跨层级组件等都可以使用它完成通信操作
  • 抽离成一个单独的 js 文件 Bus.js ,然后在需要的地方引入
import Vue from "vue"
export default new Vue()
<template>
    <div>
        <div>{{title}}</div>
        <button @click="handlerClick">发消息</button>
        <hr>
        <Child></Child>
    </div>
</template>
<script>
import Child from './Child.vue'
import Bus from "../utils/Bus.js"
export default {
    components: {
        Child
    },
    data() {
        return {
            title: '我是父组件'
        }
    },
    methods: {
        handlerClick() {
            // 自定义事件名 sendMsg
            Bus.$emit("sendMsg", "父组件发消息")
        }
    },
}
</script>
<template>
    <div>
        <div>{{msg}}</div>
    </div>
</template>
<script>
import Bus from "../utils/Bus.js"
export default {
    data() {
        return {
            msg: '我是子组件'
        }
    },
    mounted() {
        // 监听事件的触发
        Bus.$on("sendMsg", data => {
            this.msg += data
        })
    },
    beforeDestroy() {
        // 取消监听
        Bus.$off("sendMsg")
    }
}
</script>

11-Vuex

Vuex

12-$root

  • 访问根组件中的属性或方法 $root只对根组件有用
  • 当前组件树的根组件实例。如果当前实例没有父组件,那么这个值就是它自己。
this.$root.xxx