组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用。 vue组件间的传值方式多种多样,并不局限于父子传值、事件传值这些。
文章目录
- provide / inject
- props (父传子)
- $emit(子传父)
- eventBus(全局创建Vue实例)
- vuex (状态管理)
- children / $refs (获取组件实例)
- $attrs
- $listeners
- mixin
- 路由传值 /
引用数据类型值传递实现父子间数据的共享
1.provide / inject
这一组选项需要一起使用,允许一个祖先组件向其所有的后代组件注入一个依赖,不论组件层级有多深,并在其上下游关系成立的时间里始终生效。
// inject 选项应该是:一个字符串数组,或一个对象,对象的 key 是本地的绑定名
// 父级组件提供 'foo'
var Provider = {
provide: {
foo: 'bar'
},
// ...
}
// 子组件注入 'foo' (数组形式)
var Child = {
inject: ['foo'],
created () {
console.log(this.foo) // => "bar"
}
// ...
}
或 (对象形式)
var Child = {
inject: {
foo: {
from: 'bar', // 可选
default: 'self defined content' // 默认值
}
},
created () {
console.log(this.foo) // => "bar"
}
// ...
}
需要注意的是: Vue 2.2.1 或更高版本中,inject注入的值会在 props 和 data 初始化之前得到
const Child = {
inject: ['foo'],
data () {
return {
bar: this.foo // 输出bar的值与foo相同
}
}
}
const Child = {
inject: ['foo'],
props: {
bar: {
default () {
return this.foo
}
}
}
}
-----------------------------------------------------------------------------------------------
// 注入可以通过设置默认值使其变成可选项
const Child = {
inject: {
foo: { // 注意: 此处key值必须是父组件中provide的属性的key
from: 'bar', // 属性是在可用的注入内容中搜索用的 key (字符串或 Symbol), data或者props中的可搜索值
default: 'foo' // 属性是降级情况下使用的 value, 默认值为 ‘foo’
}
}
}
// 与 prop 的默认值类似
const Child = {
inject: {
foo: {
from: 'bar',
default: () => [1, 2, 3] // 默认值为引用类型时,需要使用一个工厂方法返回对象
}
}
}
2.props (父传子)
Vue.component('props-demo-advanced', {
props: {
age: {
type: Number,
default: 0
}
}
})
// 父组件中注册和使用组件,并传值
<props-demo-advanced :age="age"> </props-demo-advanced>
3.$emit(子传父)
this.$emit('eventName', payload)
// 父组件 Parent
<Parent @evnetName="sayHi"></Parent>
4.eventBus(全局创建Vue实例)
5.children / $refs (获取组件实例)
<template>
<div class="hello">
<ipc ref="ipcRef"></ipc>
</div>
</template>
<script>\
import ipc from './ipc'
export default {
name: 'HelloWorld',
data () {
return {
parentVal: 'parent content'
}
},
mounted () {
console.log(this.$refs.ipcRef.$data.child1) // "child1 content"
console.log(this.$children[0].$data.child2) // "child2 content"
},
components: {
ipc
}
}
</script>
<template>
<div>
</div>
</template>
<script>
export default {
props: {
},
data () {
return {
child1: 'child1 content',
child2: 'child2 content'
}
},
mounted () {
console.log(this.$parent.parentVal) // "parent content"
}
}
</script>
6.$attrs
(1) 包含了父作用域中不作为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外) (2) 当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外) 可以通过v-bind="$attrs" 将所有父作用域的绑定 (class、style、 ref 除外) 传入内部组件 注: 在创建高级别的组件时非常有用 // 根组件HelloWorld.vue 中引入 ipc.vue koa=“ipcRef” name=“go” id=“id” ref=“ref” style=“border: 1px solid red;” class=“className”
// ipc.vue 中引入 ipcChild.vue
<div>
<ipcChild v-bind="$attrs" selfDefine="selfDefine"></ipcChild>
</div>
</template>
<script>
import ipcChild from './ipcChild'
export default {
components: {
ipcChild
},
mounted () {
console.log(this.$attrs) // {id: "id", name: "go", koa: "ipcRef"}
}
}
</script>
// ipcChild.vue中打印接收到的$attrs
<script>
export default {
created () {
console.log(this.$attrs) // "{"selfDefine":"selfDefine","koa":"ipcRef","name":"go","id":"id"}"
}
}
</script>
7.$listeners
包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器 通过 v-on="$listeners" 将父作用域的时间监听器传入内部组件
A、B、C三个组件依次嵌套, B嵌套在A中,C嵌套在B中。 借助 B 组件的中转,从上到下props依次传递,从下至上,attrs以及attrs以及$listeners 向上或者向下传递即可。
8. .sync
可以帮我们实现父组件向子组件传递的数据 的双向绑定,所以子组件接收到数据后可以直接修改,并且会同时修改父组件的数据
// Parent.vue
<template>
<child :page.sync="page"></child>
</template>
<script>
export default {
data(){
return {
page:1
}
}
}
// Child.vue
export default {
props:["page"],
computed(){
// 当我们在子组件里修改 currentPage 时,父组件的 page 也会随之改变
currentPage {
get(){
return this.page
},
set(newVal){
this.$emit("update:page", newVal)
}
}
}
}
</script>
\
9. .model
可以帮我们实现父组件向子组件传递的数据 的双向绑定,所以子组件接收到数据后可以直接修改,并且会同时修改父组件的数据
// Parent.vue
<template>
<child :page.sync="page"></child>
</template> <script>
export default {
data(){
return {
page:1
}
}
}
// Child.vue
export default {
props:["page"],
computed(){
// 当我们在子组件里修改 currentPage 时,父组件的 page 也会随之改变
currentPage {
get(){
return this.page
},
set(newVal){
this.$emit("update:page", newVal)
}
}
}
}
</script>
\
10. mitt
Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus
先安装 npm i mitt -S
然后像以前封装 bus 一样,封装一下
mitt.js
import mitt from 'mitt'
const mitt = mitt()
export default mitt
然后两个组件之间通信的使用
// 组件 A
<script setup>
import mitt from './mitt'
const handleClick = () => { mitt.emit('handleChange') }
</script>
//组件b
// 组件 B
<script setup>
import mitt from './mitt'
import { onUnmounted } from 'vue'
const someMethed = () => { ... }
mitt.on('handleChange',someMethed)
onUnmounted(()=>{ mitt.off('handleChange',someMethed) })
</script>
11.slot
就是把子组件的数据通过插槽的方式传给父组件使用,然后再插回来
// Child.vue
<template>
<div>
<slot :user="user"></slot>
</div>
</template>
export default{
data(){
return {
user:{ name:"沐华" }
}
}
}\
// Parent.vue
<template>
<div>
<child v-slot="slotProps">
{{ slotProps.user.name }}</child>
</div>
</template>
12.vuex
// store/index.js
import { createStore } from "vuex"
export default createStore({
state:{ count: 1 },
getters:{ getCount: state => state.count },
mutations:{ add(state){ state.count++ } }
})
// main.js
import { createApp } from "vue"
import App from "./App.vue" import store from "./store" createApp(App).use(store).mount("#app")
// Page.vue // 方法一 直接使用
<template>
<div>{{ $store.state.count }}</div>
<button @click="$store.commit('add')">按钮</button>
</template>
// 方法二 获取
<script setup>
import { useStore, computed } from "vuex"
const store = useStore()
console.log(store.state.count) // 1
const count = computed(()=>store.state.count)
// 响应式,会随着vuex数据改变而改变
console.log(count) // 1
</script>