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