全部产品
云市场
云游戏

组件

更新时间:2020-08-07 20:13:02

Component 扩充自 Vue 的组件, 提供了 Vue 组件对等的输入参数能力。在代码书写时提供类 class 的装饰器 Decorator 风格。

代码引入

  1. import { Component, Watch } from '@ali/kylin-framework';

组件声明结构

一个组件可以包含数据、JSX 渲染函数、模板、挂载元素、方法、生命周期等 Vueoptions 选项的对等配置。组件声明包括以下几部分, 分别使用 @Component@Watch 两种不同装饰器进行包装:

  • class 类声明, 使用装饰器 @Component
  • 类成员声明, 不使用装饰器。
  • 类成员方法声明, 一般不装饰器, 除非该方法需要 watch 另外一个已声明的变量。

下面是简单的组件开发演示,具体声明参数参见 组件接口 了解详情。

*.vue 单文件组件

  1. <!-- Hello.vue -->
  2. <template>
  3. <div>hello {{name}}
  4. <Child></Child>
  5. </div>
  6. </template>
  7. <style>
  8. /* put style here */
  9. </style>
  10. <component default="Child" src="./child.vue" />
  11. <script>
  12. import { Component } from '@ali/kylin-framework';
  13. @Component
  14. class Hello {
  15. data = {
  16. name: 'world'
  17. }
  18. }
  19. export default Hello;
  20. </script>

关于 <component> 标签式的组件依赖,参见 组件接口 了解详情。

组件接口

跟 vue 基本一致,组件定义写在 .vue 文件内,以下是一个简单的例子:

  1. <template>
  2. <div>
  3. <AButton @click="onClick">点击</AButton>
  4. </div>
  5. </template>
  6. <style lang="less" rel="stylesheet/less">
  7. /* less */
  8. </style>
  9. <dependency component="{ AButton }" src="@alipay/antui-vue" lazy/>
  10. <script type="text/javascript">
  11. import { Component } from '@ali/kylin-framework';
  12. @Component
  13. export default class IndexView {
  14. props = {}
  15. data = {}
  16. get comput() { return this.data.c * 2 }
  17. onClick() {}
  18. mounted() {}
  19. }
  20. </script>

上述例子中,有 4 个顶级标签,除了与 Vue 相同的 <template><style><script> 之外,还有一个 <dependency> 标签。

下文将对这 4 个标签的具体作用分别进行阐述。其中 <template><style>vue 中定义一致。

script

class 结构

定义一个 Component ,在代码结构上,使用类 class 的装饰器 Decorator 风格。其中装饰器有 @Component@Watch 2 种,通过以下方式引入。

  1. import { Component, Watch } from '@ali/kylin-framework';
  2. @Component
  3. export default class Hello {
  4. }

方法类型

组件以 class 形式声明,必须对该 class 进行装饰器修饰。在 class 内部,成员变量 是不需要被手动处理的,在构建过程中通过 babel 插件自动进行处理,而 成员函数 一般不需要装饰器挂载,除非是使用 @Watch 的场景,其中 @Component 会处理的属性如下表所示。

成员类型 名称 功能
get/set property * 用以转换成 Vuecomputed 计算属性,可以直接通过 this[varName] 调用。
method 生命周期 生命周期方法,与 Vue 对等。
method * 普通成员方法, 用以转换成 Vuemethods 方法列表。

getter/setter属性

  1. @Component
  2. export default class Hello {
  3. get computName() {
  4. // to sth
  5. }
  6. }

上述 getter 声明,等价于如下 vue 配置

  1. HelloOption = {
  2. computed: {
  3. computName: {
  4. get: computName() {
  5. // to sth
  6. }
  7. }
  8. }
  9. }

同理,setter 也会被提取,如果同时存在 gettersetter 则会被一起提取。

生命周期函数

  1. @Component
  2. export default class Hello {
  3. created() {}
  4. mounted() {}
  5. }

上述 createdmounted 生命周期函数,会被提取为数组。

  1. TestOption = {
  2. created: [function created(){}],
  3. mounted: [function mounted(){}],
  4. }

支持的生命周期方法名如下,beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroydestroyed

Watch

该装饰器的出现,只是因为 watch 需要有以下几个要素:

  • 被监听的变量名
  • 监听选项
  • 触发函数

用法

完整的 @Watch 接口如下表所示。

  1. function Watch( key: string [, option: Object = {} ] ): PropertyDecorator
参数名 类型 用途
key string 监听的参数名,来自 computeddataprops 三者之一。
option deep boolean 若监听的是复杂对象,其内层数据变更是否触发,默认为 false
immediate boolean 立即以表达式的当前值触发回调,默认为 false

示例

  • 对于 @Watch 装饰的成员函数,支持对成员函数配置多个变量的监听,如下同时对 ac 的变化进行了监听,如果任何一个发生变化,会触发 OnChangeA 成员方法。
  • 如下,OnChangeA 本质是成员方法,所以他也会和其他成员方法一起被提取到methods块中,那么必须保证没有与其他方法重名。
  • 如果对Watch有额外配置项,请按 @Watch('a', {deep: false})的方法传入。配置项请参考 watch配置项
  1. @Component
  2. class WTest {
  3. data = {
  4. a: {
  5. b: 2
  6. },
  7. c: 3
  8. }
  9. @Watch('c')
  10. @Watch('a', {deep: false})
  11. OnChangeA(newVal, oldVal) {
  12. }
  13. }

注意: 以上对 data.a 的监听,会转换成如下形式,需要注意的是,如果没开启 deep: true 选项,当 data.a.b 发生变动的时候,不会触发该 OnChangeA 监听。

属性类型

构建工具会自动对成员变量应用了 @Component.Property 装饰器,不需要用户手动填写,最终的合并策略取决于被装饰的 成员变量 的标识符名称,框架内置了以下几种。如果不在下表中,会透传至 VueComponentoptions 对象中。

成员类型 名称 功能
property props vue的props属性
property data vue的data属性,会被转换成函数形式, 支持 this,请勿直接写 data(){} 函数
property * 其他未知属性,直接复制到 Vueoptions 中的对应属性上

props

  1. @Component
  2. export default class Hello {
  3. props = {
  4. name: {
  5. type: String,
  6. default: 'haha'
  7. },
  8. num: Number
  9. }
  10. }

上述 props 成员变量定义,会被直接转换成到 options 中对应的 props

  1. HelloOption = {
  2. props: {
  3. name: {
  4. type: String,
  5. default: 'haha'
  6. },
  7. num: Number
  8. }
  9. }

具体完整定义结构请参见 Vue 文档 API-props

data

  1. @Component
  2. export default class Hello {
  3. props = {
  4. name: {
  5. type: Number,
  6. default: 1
  7. },
  8. }
  9. data = {
  10. hello: this.props.name + 2
  11. }
  12. }

上述 data 成员变量定义,会被转换成 data 函数形式,您无需手动编写 data 函数。

  1. TestOption = {
  2. props: {
  3. name: {
  4. type: Number,
  5. default: 1
  6. },
  7. },
  8. data: function data() {
  9. return {
  10. hello: this.props.name + 2
  11. }
  12. }
  13. }

dependency

上述 <script> 定义中,没有说明组件依赖的使用方式,在 .vue 文件中,推荐使用以下写法来标记组件依赖,即 <dependency> 标签,下面示例中即引用了 ./child.vue 组件。

  1. <template>
  2. <child></child>
  3. </template>
  4. <dependency component="Child" src="./child.vue" />

标签属性

default 导入

针对 ES6 Moduledefault 导出或者 commonjs Module 对象的导出,可使用如下方式引入。

属性 类型 默认值 备注
component string 必填 引入到 options.components 的标识符名。
src string 必填 组件来源,同require(src)。
lazy boolean false 是否对该组件启用懒加载(当且仅当被 Vue 使用到时再进行 require 加载模块)。
style string undefined 默认不启用,如果设置了字符串,会根据 ${src}/${style} 的路径来加载组件对应样式文件。

如下示例:

  1. <dependency component="name" src="source" lazy />

member 导入

针对 ES6 Module 的命名导出,可使用如下方式引入:

属性 类型 默认值 备注
component string 必填 引入到 options.components 的多个命名标识符, 必须以花括号 {} 包括,否则会识别为 default 引入
src string 必填 组件来源,同 require(src)。
lazy boolean false 是否对该组组件启用懒加载(当且仅当被 Vue 使用到时再进行 require 加载模块)。

如下示例:

  1. <dependency component="{ List, ListItem, AButton }" src="@alipay/antui-vue" lazy />

默认对 @alipay/antui-vue 组件库支持 babel-plugin-import 按需加载。

template

模板的内容结构与 vue 一致。

  1. <template>
  2. <div>Hello World</div>
  3. </template>

style

  1. <style lang="less" rel="stylesheet/less">
  2. /* less */
  3. </style>

其中,可以通过添加 scoped 属性标记来使得该样式只对当前组件生效。

  1. <style lang="less" rel="stylesheet/less" scoped>
  2. /* less */
  3. </style>

状态注入

对于 Kylin 组件, 如果需要使用到 Store 中的属性,使用计算属性把 $store 对象中的属性透传出来是一种不推荐的写法,如下所示:

  1. @Component
  2. class Hello {
  3. // 通过计算属性来关联store中的状态
  4. get hello() {
  5. return this.$store.state.hello
  6. }
  7. }

推荐使用下面的 connect 机制来透传 $store 数据:

接口声明

  1. @Component({
  2. mapStateToProps: Object|Array,
  3. mapActionsToMethods: Object|Array,
  4. mapMethods: Array|Boolean,
  5. mapEvents: Array
  6. })
  7. class Hello {
  8. }

mapStateToProps

state 中的特定键值映射到当前组件的 props 中,其接收参数等价于 Vuex 提供的 mapState 辅助函数

有以下 3 种方式可实现上述功能:

函数方式

说明:把 $store.state 中的名为 bbb 的数据,映射到名为 aaaprops 上。
  1. {
  2. mapStateToProps: {
  3. aaa: (state, getters) => state.bbb
  4. }
  5. }

字符串键值对方式

说明:把 $store.state 中名为 bbb 的数据,映射到名为 aaaprops 上。
  1. {
  2. mapStateToProps: {
  3. aaa: 'bbb'
  4. }
  5. }

字符串数组方式

说明
  • $store.state 中名为 aaa 的数据,映射到名为 aaaprops 上。
  • $store.state 中的名为 bbb 的数据,映射到名为 bbbprops 上。
  1. {
  2. mapStateToProps: ['aaa', 'bbb']
  3. }

mapActionsToMethods

VuexmapActions 入参一致,支持使用对象方式(名称映射)、数组方式(名称)把在全局 $store 下配置的 actions 注入到当前组件的 methods 中。

  1. @Component({
  2. mapActionsToMethods: ['a', 'b']
  3. })
  4. class IndexView {
  5. async doSomeAction() {
  6. const ret = await this.a(123);
  7. // 等价于调用
  8. // const ret = await this.$store.dispatch('a', 123);
  9. }
  10. }

mapMethods

通过在父子组件之间加一层中间层组件的方式来具体实现 connect 机制。当父组件调用子组件中特定的 method 方法时,无法直接访问子组件(实际访问到的是中间层组件),需要通过以下配置实现访问。

  1. @Component({
  2. mapMethods: true
  3. })
  4. export default class Child {
  5. a() {}
  6. }
  1. <template>
  2. <div>
  3. this is parent
  4. <child ref="child"></child>
  5. </div>
  6. </template>
  7. <script>
  8. @Component
  9. export default class Parent {
  10. b() {
  11. // 此处可以访问
  12. this.$refs.child.a();
  13. }
  14. }
  15. </script>