Component 扩充自 Vue
的组件, 提供了 Vue
组件对等的输入参数能力。在代码书写时提供类 class
的装饰器 Decorator
风格。
代码引入
import { Component, Watch } from '@ali/kylin-framework';
组件声明结构
一个组件可以包含数据、JSX 渲染函数、模板、挂载元素、方法、生命周期等 Vue
的 options
选项的对等配置。组件声明包括以下几部分, 分别使用 @Component
和 @Watch
两种不同装饰器进行包装:
- class 类声明,使用装饰器
@Component
。 - 类成员声明,不使用装饰器。
- 类成员方法声明,一般不装饰器,除非该方法需要
watch
另外一个已声明的变量。
下面是简单的组件开发演示,具体声明参数参见 组件接口 了解详情。
*.vue 单文件组件
<!-- Hello.vue -->
<template>
<div>hello {{name}}
<Child></Child>
</div>
</template>
<style>
/* put style here */
</style>
<component default="Child" src="./child.vue" />
<script>
import { Component } from '@ali/kylin-framework';
@Component
class Hello {
data = {
name: 'world'
}
}
export default Hello;
</script>
关于 <component>
标签式的组件依赖,参见 组件接口 了解详情。
组件接口
跟 vue 基本一致,组件定义写在 .vue
文件内,以下是一个简单的例子:
<template>
<div>
<AButton @click="onClick">点击</AButton>
</div>
</template>
<style lang="less" rel="stylesheet/less">
/* less */
</style>
<dependency component="{ AButton }" src="@alipay/antui-vue" lazy/>
<script type="text/javascript">
import { Component } from '@ali/kylin-framework';
@Component
export default class IndexView {
props = {}
data = {}
get comput() { return this.data.c * 2 }
onClick() {}
mounted() {}
}
</script>
上述例子中,有 4 个顶级标签,除了与 Vue 相同的 <template>
、<style>
、<script>
之外,还有一个 <dependency>
标签。
下文将对这 4 个标签的具体作用分别进行阐述。其中 <template>
、 <style>
与 vue
中定义一致。
script
class 结构
定义一个 Component ,在代码结构上,使用类 class 的装饰器 Decorator 风格。其中装饰器有 @Component
和 @Watch
2 种,通过以下方式引入。
import { Component, Watch } from '@ali/kylin-framework';
@Component
export default class Hello {
}
方法类型
组件以 class 形式声明,必须对该 class 进行装饰器修饰。在 class 内部,成员变量是不需要被手动处理的,在构建过程中通过 babel
插件自动进行处理,而成员函数一般不需要装饰器挂载,除非是使用 @Watch
的场景,其中 @Component
会处理的属性如下表所示。
成员类型 | 名称 | 功能 |
---|---|---|
get/set property | * | 用以转换成 Vue 的 computed 计算属性,可以直接通过 this[varName] 调用。 |
beforeCreate | 生命周期 | 生命周期方法,与 Vue 对等。 |
created | 生命周期 | 生命周期方法,与 Vue 对等。 |
beforeMount | 生命周期 | 生命周期方法,与 Vue 对等。 |
mounted | 生命周期 | 生命周期方法,与 Vue 对等。 |
beforeUpdate | 生命周期 | 生命周期方法,与 Vue 对等。 |
updated | 生命周期 | 生命周期方法,与 Vue 对等。 |
beforeDestroy | 生命周期 | 生命周期方法,与 Vue 对等。 |
destroyed | 生命周期 | 生命周期方法,与 Vue 对等。 |
method | * | 普通成员方法, 用以转换成 Vue 的 methods 方法列表。 |
getter/setter属性
@Component
export default class Hello {
get computName() {
// to sth
}
}
上述 getter
声明,等价于如下 vue 配置
HelloOption = {
computed: {
computName: {
get: computName() {
// to sth
}
}
}
}
同理,setter
也会被提取,如果同时存在 getter
和 setter
则会被一起提取。
生命周期函数
@Component
export default class Hello {
created() {}
mounted() {}
}
上述 created
和 mounted
生命周期函数,会被提取为数组。
TestOption = {
created: [function created(){}],
mounted: [function mounted(){}],
}
支持的生命周期方法名如下,beforeCreate
、created
、beforeMount
、mounted
、beforeUpdate
、updated
、 beforeDestroy
、destroyed
。
Watch
该装饰器的出现,只是因为 watch
需要有以下几个要素:
- 被监听的变量名
- 监听选项
- 触发函数
用法
完整的 @Watch
接口如下表所示。
function Watch( key: string [, option: Object = {} ] ): PropertyDecorator
参数名 | 类型 | 用途 | |
---|---|---|---|
key | string | 监听的参数名,来自 computed 、data 、props 三者之一。 |
|
option | deep | boolean | 若监听的是复杂对象,其内层数据变更是否触发,默认为 false 。 |
immediate | boolean | 立即以表达式的当前值触发回调,默认为 false |
示例
- 对于
@Watch
装饰的成员函数,支持对成员函数配置多个变量的监听,如下同时对a
和c
的变化进行了监听,如果任何一个发生变化,会触发OnChangeA
成员方法。 - 如下,
OnChangeA
本质是成员方法,所以他也会和其他成员方法一起被提取到methods
块中,那么必须保证没有与其他方法重名。 - 如果对 Watch 有额外配置项,请按
@Watch('a', {deep: false})
的方法传入。配置项请参考 watch配置项。
@Component
class WTest {
data = {
a: {
b: 2
},
c: 3
}
@Watch('c')
@Watch('a', {deep: false})
OnChangeA(newVal, oldVal) {
}
}
注意:以上对 data.a
的监听,会转换成如下形式,需要注意的是,如果没开启 deep: true
选项,当 data.a.b
发生变动的时候,不会触发该 OnChangeA
监听。
属性类型
构建工具会自动对成员变量应用了 @Component.Property
装饰器,不需要用户手动填写,最终的合并策略取决于被装饰的 成员变量
的标识符名称,框架内置了以下几种。如果不在下表中,会透传至 VueComponent
的 options
对象中。
成员类型 | 名称 | 功能 |
---|---|---|
property | props | vue的props属性 |
property | data | vue的data属性,会被转换成函数形式, 支持 this,请勿直接写 data(){} 函数 |
property | * | 其他未知属性,直接复制到 Vue 的 options 中的对应属性上 |
props
@Component
export default class Hello {
props = {
name: {
type: String,
default: 'haha'
},
num: Number
}
}
上述 props
成员变量定义,会被直接转换成到 options
中对应的 props
。
HelloOption = {
props: {
name: {
type: String,
default: 'haha'
},
num: Number
}
}
具体完整定义结构请参见 Vue 文档 API-props。
data
@Component
export default class Hello {
props = {
name: {
type: Number,
default: 1
},
}
data = {
hello: this.props.name + 2
}
}
上述 data
成员变量定义,会被转换成 data 函数形式,您无需手动编写 data 函数。
TestOption = {
props: {
name: {
type: Number,
default: 1
},
},
data: function data() {
return {
hello: this.props.name + 2
}
}
}
dependency
上述 <script>
定义中,没有说明组件依赖的使用方式,在 .vue
文件中,推荐使用以下写法来标记组件依赖,即 <dependency>
标签,下面示例中即引用了 ./child.vue
组件。
<template>
<child></child>
</template>
<dependency component="Child" src="./child.vue" />
标签属性
default 导入
针对 ES6 Module
的 default
导出或者 commonjs Module
对象的导出,可使用如下方式引入。
属性 | 类型 | 默认值 | 备注 |
---|---|---|---|
component | string | 必填 | 引入到 options.components 的标识符名。 |
src | string | 必填 | 组件来源,同require(src )。 |
lazy | boolean | false | 是否对该组件启用懒加载(当且仅当被 Vue 使用到时再进行 require 加载模块)。 |
style | string | undefined | 默认不启用,如果设置了字符串,会根据 ${src}/${style} 的路径来加载组件对应样式文件。 |
如下示例:
<dependency component="name" src="source" lazy />
member 导入
针对 ES6 Module
的命名导出,可使用如下方式引入:
属性 | 类型 | 默认值 | 备注 |
---|---|---|---|
component | string | 必填 | 引入到 options.components 的多个命名标识符, 必须以花括号 { ,} 包括,否则会识别为 default 引入 。 |
src | string | 必填 | 组件来源,同 require(src )。 |
lazy | boolean | false | 是否对该组组件启用懒加载(当且仅当被 Vue 使用到时再进行 require 加载模块)。 |
如下示例:
<dependency component="{ List, ListItem, AButton }" src="@alipay/antui-vue" lazy />
默认对 @alipay/antui-vue
组件库支持 babel-plugin-import
按需加载。
template
模板的内容结构与 vue 一致。
<template>
<div>Hello World</div>
</template>
style
<style lang="less" rel="stylesheet/less">
/* less */
</style>
其中,可以通过添加 scoped
属性标记来使得该样式只对当前组件生效。
<style lang="less" rel="stylesheet/less" scoped>
/* less */
</style>
状态注入
推荐使用下面的 connect
机制来透传 $store
数据:
- 接口声明
- 透传数据,有以下 3 种方式:
对于 Kylin 组件, 如果需要使用到 Store
中的属性,不推荐使用计算属性把 $store
对象中的属性透传出来,如下所示:
@Component
class Hello {
// 通过计算属性来关联store中的状态
get hello() {
return this.$store.state.hello
}
}
接口声明
@Component({
mapStateToProps: Object|Array,
mapActionsToMethods: Object|Array,
mapMethods: Array|Boolean,
mapEvents: Array
})
class Hello {
}
mapStateToProps
把 state
中的特定键值映射到当前组件的 props
中,其接收参数等价于 Vuex
提供的 mapState
辅助函数。
有以下 3 种方式可实现上述功能:
函数方式
$store.state
中的名为bbb
的数据,映射到名为aaa
的props
上。
{
mapStateToProps: {
aaa: (state, getters) => state.bbb
}
}
字符串键值对方式
$store.state
中名为bbb
的数据,映射到名为aaa
的props
上。
{
mapStateToProps: {
aaa: 'bbb'
}
}
字符串数组方式
- 把
$store.state
中名为aaa
的数据,映射到名为aaa
的props
上。 - 把
$store.state
中的名为bbb
的数据,映射到名为bbb
的props
上。
{
mapStateToProps: ['aaa', 'bbb']
}
mapActionsToMethods
与 Vuex
中 mapActions
入参一致,支持使用对象方式(名称映射)、数组方式(名称)把在全局 $store
下配置的 actions
注入到当前组件的 methods
中。
@Component({
mapActionsToMethods: ['a', 'b']
})
class IndexView {
async doSomeAction() {
const ret = await this.a(123);
// 等价于调用
// const ret = await this.$store.dispatch('a', 123);
}
}
mapMethods
通过在父子组件之间加一层中间层组件的方式来具体实现 connect
机制。当父组件调用子组件中特定的 method
方法时,无法直接访问子组件(实际访问到的是中间层组件),需要通过以下配置实现访问。
@Component({
mapMethods: true
})
export default class Child {
a() {}
}
<template>
<div>
this is parent
<child ref="child"></child>
</div>
</template>
<script>
@Component
export default class Parent {
b() {
// 此处可以访问
this.$refs.child.a();
}
}
</script>