vue学习记录之组件间通信
在组件之间进行传递数据一般可以通过props
从父组件向子组件传递,用自定义事件
的方式从子组件向父组件传递数据。
父传子props
之前模版语法中也演示过一次props
的作用,这里再详细的介绍一下props
。
通过props
你可以传递各种类型的数据,你可以直接传递一个静态的数据,也可以传递动态获取的数据。
传递静态数据
<!-- 传递静态的字符串 -->
<comp msg="this is a static message"></comp>
<!-- 传递静态的布尔类型数据(子组件的props也要指定类型为Boolean) -->
<comp is-success></comp>
<comp :is-success="true"></comp>
<!-- 传递静态的数组类型数据(数组、对象等类型虽然数据是静态的,但为了让Vue知道是数组类型而不是字符串就必须是v-bind来告知Vue这是一个js表达式) -->
<comp :book-ids="[111,222,333]"></comp>
tips: 在html中标签的属性不区分大小写,因此使用时camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 。
传递动态数据
传递动态数据都需要使用v-bind
或者:
来动态赋值。
<!-- 传递动态的字符串 -->
<comp :msg="comp.msg"></comp>
<!-- 传递动态的布尔类型数据 -->
<comp :is-success="comp.isSuccess"></comp>
<!-- 传递动态的数组类型数据 -->
<comp :book-ids="comp.bookIds"></comp>
props的定义以及类型检查
props的定义
你可以通过字符串数据的方式定义子组件中的props
。
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
也可以像Java
等语言一样单独为一个属性定义类型,这种方式看上去会更加具有可读性。
props: {
title: String,
likes: Number,
isPublished: Boolean,
commentIds: Array,
author: Object,
callback: Function,
contactsPromise: Promise // 或任何其他构造函数
}
props的类型检查
props: {
// 基础的类型检查 (`null` 和 `undefined` 值会通过任何类型验证)
propA: Number,
// 多个可能的类型
propB: [String, Number],
// 必填的字符串
propC: {
type: String,
required: true
},
// 带有默认值的数字
propD: {
type: Number,
default: 100
},
// 带有默认值的对象
propE: {
type: Object,
// 对象或数组的默认值必须从一个工厂函数返回
default() {
return { message: 'hello' }
}
},
// 自定义验证函数
propF: {
validator(value) {
// 这个值必须与下列字符串中的其中一个相匹配
return ['success', 'warning', 'danger'].includes(value)
}
},
// 具有默认值的函数
propG: {
type: Function,
// 与对象或数组的默认值不同,这不是一个工厂函数——这是一个用作默认值的函数
default() {
return 'Default function'
}
}
}
自定义构造函数类型,此举可以让你知道这个属性是否是通过new
的方式创建出来的(一般不怎么用)。
export class Person {
constructor(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
}
// es6之前的语法
// function Person(firstName, lastName) {
// this.firstName = firstName
// this.lastName = lastName
// }
export default {
name: "Test",
props: {
customProp: {
type: Person
}
}
}
<template>
<test :custom-prop="customProp"/>
</template>
<script>
import Test from "@/components/Test";
import {Person} from "@/components/Test";
export default {
name: 'App',
components: {
Test
},
data() {
return {
customProp: new Person('abc', 'sdj')
}
}
}
</script>
数据子传父
Vue有个自定义事件的功能,我们可以通过自定义事件的方式把子组件的数据传递给父组件。
// 可以在子组件的一个事件函数中使用$emit函数传递数据
this.$emit('myEvent', 123)
// 父组件中使用自定义定义的事件(同样的驼峰转连接符)
<my-component @my-event="myEvent"></my-component>
// ... 父组件中的myEvent函数接收的参数就是子组件中传递的数据
myEvent(data) {
// data就是子组件传递过来的数据
}
更多关于自定义事件的使用,可查阅官方文档自定义事件
父子组件相互访问
Provide / Inject
针对多层组件引用,比如app->subApp->demo1->demo2->demo3想要使用app组件中的某些方法或者属性,就可以使用一对Provide/Inject
来达到目的,无论层次有多深都可以使用这一对。
// ... App组件
export default {
name: "Test"
components: {
SubApp
},
data() {
return {
user:
}
},
provide: {
user: 'Bennett'
}
}
// ... demo3组件
export default {
name: "Test",
inject: ['user'],
created() {
// this.user is Bennett
console.log(this.user);
}
}
上面的provide
中的user
属性只能给后代组件使用,本身使用不到,demo3要想访问到实例中的属性或函数就需要使用provide()
返回一个对象的形式才可以。
// ... App组件
export default {
name: "Test"
components: {
SubApp
},
data() {
return {
user:
}
},
provide() {
return {
user: this.user
}
}
}
注意⚠️inject
的数据并不会随着provide
中的数据改变而改变,要想做到响应式就需要返回的数据是通过computed
包装过的计算属性。
// ... App组件
export default {
name: "Test"
components: {
SubApp
},
data() {
return {
user:
}
},
provide() {
return {
user: Vue.computed(() => this.user);
}
}
}
使用实例属性访问
Vue为所有的组件都提供了以下几个属性:
$data
: 当前实例的数据对象$props
: 当前实例接收到的props
对象$el
: 组件实例正在使用的根 DOM 元素(不推荐直接使用$el
,推荐在使用模版引用
)
<input ref="customInput" />
之后可通过this.$refs.customInput.focus()
触发focus
事件。
$options
: 用于当前组件实例的初始化选项。当你需要在选项中包含自定义property
时会有用处。
const app = createApp({
customOption: 'foo',
created() {
console.log(this.$options.customOption) // => 'foo'
}
})
$parent
: 父实例,如果当前实例有的话。$root
: 当前组件树的根组件实例。如果当前实例没有父实例,此实例将会是其自己。$slots
: 用来以编程方式访问通过插槽分发的内容。每个具名插槽都有其相应的property
(例如:v-slot:foo
中的内容将会在this.$slots.foo()
中被找到)。$refs
: 一个对象,持有注册过ref
attribute 的所有 DOM 元素和组件实例。attrs
: 包含了父作用域中不作为组件props
或自定义事件的attribute
绑定和事件。
父组件访问子组件数据
可以用上面的this.$refs.customRefName.componentData
访问到customRefName
子组件的componentData
。这个需要依赖于$refs
和模版引用
组合使用。
<custom-component ref="customComponent"></custom-component>
<custom-component></custom-component>
export default {
name: "Test",
components: {
CustomComponent
},
methods: {
demoMethod() {
// 访问第一个CustomComponent组件中的componentData属性、componentMethod方法
console.log(this.$refs.customRefName.componentData)
console.log(this.$refs.customRefName.componentMethod)
}
}
}
子组件访问父组件数据
这个可直接使用$parent
访问到父组件实例中的内容,比如:
// 访问父组件中的demo属性、demoMethod方法
this.$parent.demo;
this.$parent.demoMethod();
// 访问父组件的父组件的demo属性、demoMethod方法(没有provide/inject好用)
this.$parent.$parent.demo;
this.$parent.$parent.demoMethod();
// 访问根组件中的属性和方法
this.$root.demo;
this.$root.demoMethod();
可见如果层级太深就比较麻烦,如果是子组件访问父组件还好,反过来就麻烦了。这就需要到vue
官方提供的vuex
组件了,这个后面再说。