组件API包括三部分: prop / 事件 / 插槽
- prop: 允许外部环境传递数据到组件
- 事件: 允许组件内部触发外部环境的方法
- 插槽: 允许外部环境将额外的内容组合在组件中
字面量语法 vs 动态语法
使用字面语法方式传递值,是字符串,而使用动态语法传递的时候,会当作JavaScript表达式计算,如下例子:
单向数据流
prop是单向绑定的,当父组件的属性变化时,将传导给子组件,但是反过来不行,这是为了防止组件无意间修改了父组件的状态,避免应用的数据流变得难以控制和理解。
但是在如下的场景中,我们常常需要进行修改,该如何(变相)实现:
- prop作为初始值传入后,子组件想把它当作局部数据来用;
- prop作为原始数据后,由子组件处理成其他数据输出。
解决方案如下:
- 定义一个局部变量,并用prop的值来初始化:
components: {
'component-1': {
props: ['initialNum'],
data() {
return {
localNum: this.initialNum
}
},
template: `
`,
methods: {
addOne: function () {
this.localNum += 1;
}
}
}
}
- 定义一个计算属性,处理prop的值并返回:
components: {
'component-1': {
props: ['initialNum'],
template: `
{{ addOne }}>
`,
computed: {
addOne: function () {
return this.initialNum + 1;
}
}
}
}
注意:JavaScript中 对象 和 数组 是引用类型,指向 同一个内存空间,如果prop是一个对象或数组,在子组件内部改变,会影响父组件的状态,此时会报错。
属性设置和校验
为组件的prop指定验证规则、默认值等信息,可以用对象的形式来定义prop,不能用字符串数组形式。下面列举几种设置组件prop的方式:
// 字符串数组形式
props: ['prop-1', 'prop-2'],
// 基础类型检测, null 指允许任何类型
props: {
'prop-1': Number
}
// 可以是多种类型
props: {
'prop-1': [Number, String]
}
// 必传,且是字符串
props: {
'prop-1': {
type: String,
required: true
}
}
// 有默认值
props: {
'prop-1': {
type: String,
default: ''
}
}
// 数组、对象的默认值应由一个工厂函数返回
props: {
'prop-1': {
type: Object,
default: function () {
return {}
}
}
}
// 自定义验证函数
props: {
'prop-1': {
validator: function (value) {
return value > 10
}
}
}
type
可以是下面原生构造器: String / Number / Boolean / Function / Object / Array / Symbol
type
也可以是一个自定义构造函数,使用 instanceof
检测
当prop验证失败,vue会抛出警告(本地开发环境下)。注意prop会在组件实例创建之前进行校验,所以在default或validator函数里,在data/methods/computed等实例属性还无法使用。
自定义事件
上面讲述了父组件如何传递数据到子组件中,那么子组件如何跟父组件之间进行通信呢?这里介绍的是自定义事件来实现这个场景需求。
使用 v-on 绑定自定义事件
每个vue实例都实现了事件接口,即:
- 使用
$on(eventName)
监听事件 - 使用
$emit(eventName)
触发事件
可以看到这里的事件系统,貌似和浏览器的 EventTarget
很相似,但是注意这里的 $on
与 $emit
和 addEventListener
与 dispatchEvent
不是别名的简称。
父组件可以在使用子组件的地方,直接用 v-on
来监听子组件触发的事件,注意这里不能使用 $on
来监听,而必须使用 v-on
来绑定(可以使用语法糖 @
来代替)。
$emit
触发事件时,也可以传递参数,形如: $emit("自定义的方法名", arg1, arg2 ...)
例子如下:
# 测试子组件传递数据到父组件
父组件的数字: {{ number }}
使用自定义事件的表单输入组件
自定义事件可以用来创建自定义的表单输入组件,使用 v-model
来进行数据双向绑定。
与以下的代码是等价的
父组件中使用时,第二个语句其实是第一个语句的缩写而已:
所以要让组件的 v-model
生效,它应该满足以下两点:
- 接受一个 value 的 prop
- 在有新的值时,触发 input 事件,并将新值作为参数
如下例子:
something:
{{ something }}
注意上面提到组件 v-model
默认是使用 value prop 和 input 事件。那么在使用单选框、复选框等情况下,就不能直接使用v-model了,下面介绍通过在组件中,使用 model
选项来避免这种冲突。
{cfg = val}"
value="some value">
完整代码如下;
父组件中的变量 cfg: >
{{ cfg }}
非父子组件的通信
有时候,非父子关系的两个组件之间也需要通信,在简单的情况下,可以使用一个空的 Vue 实例作为事件总线。
然后一个组件负责触发,另外一个组件在声明周期mounted的时候,创建一个监听事件,例子如下:
插槽slot分发内容
为了组合父组件的内容与子组件自己的模版,我们可以使用 slot
元素作为原始内容的插槽。
具体我们可以使用的有:
- 单个插槽
- 具名插槽
- 作用于插槽
下面使用代码的方式来使用插槽,例子如下:
此内容来自于父组件
但是,text内容来自于子组件: {{ props.text }}, value内容来自于子组件: {{ props.value }}
在父组件中,具有特殊特性 slot-scope
的元素,表示它是作用域插槽的模版,slot-scope
的值将被用作一个临时变量名,此变量接收从子组件传递过来的 prop
对象,例如上面的 text / value
动态组件
动态组件的功能是可以动态的切换多个组件,场景比如是tab中切换时,切换到自己想要的子组件中。
使用方法:通过
元素,并对其 is
特性进行动态绑定,你可以在同一个挂载点动态切换多个组件:
上面例子中,当切换 chose_item
值时,下面 component
中会动态切换显示指定的子组件。
上面例子中,组件切换时,都是重新渲染的,那如果想要保留组件的状态,避免重新渲染,可以使用 keep-alive
来解决这个问题,用法如下:
子组件的引用
如果想在javasc中直接访问子组件,可以使用 ref
为子组件指定一个引用 ID,例如如下:
其他
参考: vue从入门到进阶:组件Component详解(六)