自定义组件

自定义组件实现是基于原生小程序提供的自定义组件支持。

  • 创建自定义组件,假设自定义组件文件为:src/components/Hello.okm

  • 在需要使用自定义组件的 Page 或者 自定义组件里通过类似 Vuecomponents 配置方式声明引入

经过 okam 构建工具,Hello.okm 最终会被构建生成四个文件,在 components 目录下:Hello.js、Hello.json、Hello.swan、Hello.css

组件定义

自定义组件也是单文件组件开发方式,主要由三部分:<template>, <script>, <style> 组成

  • <template>

  • <style>

    • 样式定义跟原生小程序保持一致
    • 可以根据框架提供的语言扩展,使用 stylus 或者 less 等各种预处理语言,通过lang="stylus"来区分使用的预处理语言
    • 更多进阶处理,参见:样式相关处理
  • <script>

    • 不需要原生 Component({}) 方式进行包裹,只需要导出对应的入口脚本定义即可
    • Component 上下文依旧是原生小程序上下文,因此定义同原生小程序
    • properties 简写为 props 字段
    • Component 实例扩展 Api 支持 详见-扩展 API
    • 主要属性支持
属性 说明
config Component 的配置对象, 可以不用设置,定义同原生小程序自定义组件的 json 配置文件。详见-页面配置
components Component 的子组件配置对象,声明 Component 可使用的子组件列表,将自动生成 .json 中组件配置定义。详见-页面配置
props Component 外部渲染数据对象,用于模板绑定外部渲染数据, 同原生的 properties 对象
data Component 内部渲染数据对象,用于模板绑定内部渲染数据。详见-数据操作
computed Component 计算数据对象,声明组件中一个数据项的值需要由其他数据项计算得来的计算数据。详见-计算属性
生命周期 生命周期函数,包含 created、mounted、destroyed 等类 Vue 的一系列生命周期支持。详见-生命周期
methods 组件的方法,包括事件响应函数和任意的自定义方法详见-事件处理及自定义函数
  1. <template>
  2. <view class="hello-wrap">
  3. <text class="title">Hello Title</text>
  4. <slot name="top"></slot>
  5. <slot></slot>
  6. <button class="btn" @click="handleClick">{{source}}-{{num}}</button>
  7. <slot name="bottom"></slot>
  8. </view>
  9. </template>
  10. <script>
  11. export default {
  12. config: {},
  13. components: {},
  14. props: {
  15. source: {
  16. type: String,
  17. default: 'Baidu'
  18. },
  19. num: {
  20. type: Number
  21. }
  22. },
  23. options: {
  24. // 在组件定义时的选项中启用多slot支持
  25. multipleSlots: true
  26. },
  27. data: {
  28. title: 'xxx'
  29. },
  30. // 生命周期
  31. beforeCreate() {},
  32. created() {},
  33. beforeMount() {,
  34. mounted() {},
  35. beforeDestroy() {},
  36. destroyed() {},
  37. methods: {
  38. handleClick(...args) {
  39. console.log("click in Hello", args);
  40. this.sayHi();
  41. this.$emit('hi', {name: 'Jack'});
  42. },
  43. sayHi() {
  44. console.log('hi in', this.title);
  45. }
  46. }
  47. }
  48. </script>
  49. <style lang="stylus">
  50. </style>

!> 对于小程序平台,自定义组件要想通过外部控制样式,需要使用原生小程序提供的 externalClasses 来实现,目前 百度小程序 在实现上可能会存在样式抖动现象,使用时候如果有存在这种现象,只能暂且换种方式实现,待官方优化。

组件配置

原生小程序 需在 .json 中设置 component: true,okam 无需在额外设置,也不需要声明依赖的自定义组件,即不需要配置 usingComponents

!> 不在 src/app.js 入口文件 config.pages 声明的页面组件都会被当成自定义组件处理

生命周期

okam 保留了小程序组件生命周期, 同时提供了一套 vue 生命周期风格的写法

生命周期 说明
beforeCreate 在实例初始化之后,立即同步调用 created 前期
created 实例已经创建完成之后被调用 created 后期
beforeMount 在挂载开始之前被调用 attached 前期
mounted 在实例挂载之后调用 ready 后期
beforeUpdate 暂无 -
updated 暂无 -
activated 暂无 -
deactivated 暂无 -
beforeDestroy 实例销毁之前调用 detached 前期
destroyed 实例销毁后调用 detached 后期

原生小程序自定义组件生命周期

!> Component 脚本执行的上下文还是原生小程序 Component 上下文,因此生命周期钩子,依旧可以使用原生提供的,但建议保持一致,要么都使用原生小程序钩子,要么都使用类 Vue 生命周期钩子,这样避免造成混乱。

组件 Props

props 定义参考 Vue 语法跟原生的有一定区别:

  • properties 变为 props,更加简洁,支持数组形式

  • value 变为 default,语义更清晰,支持 function执行上下文非组件

  • type 支持同原生小程序:String, Number, Boolean, Object, Array, null

  • observer 不建议使用,默认支持父组件到子组件数据的单向同步

  1. export default {
  2. props: [
  3. 'postTitle',
  4. 'postSource'
  5. ],
  6. props: {
  7. arr: {
  8. type: Array,
  9. default() {
  10. return [1, 2, 3];
  11. }
  12. }
  13. }
  14. // 等价于
  15. properties: {
  16. postTitle: {
  17. type: null
  18. },
  19. postSource: {
  20. type: null
  21. }
  22. },
  23. properties: {
  24. arr: {
  25. type: Array,
  26. value: [1, 2, 3]
  27. }
  28. }
  29. }

父子组件的数据传递

  • 传递静态值

    1. <template>
    2. <child str="abc"></child>
    3. </template>
    1. <template>
    2. <view>{{src}}</view>
    3. </template>
    4. <script>
    5. export default {
    6. props: {
    7. str: String
    8. }
    9. }
    10. </script>
  • 传递动态值,使用数据绑定语法 : 来传递动态值:

    1. <template>
    2. <child :childTitle="title"></child>
    3. </template>
    4. <script>
    5. export default {
    6. data: {
    7. title: 'str'
    8. }
    9. }
    10. </script>
    1. <template>
    2. <view>{{childTitle}}</view>
    3. </template>
    4. <script>
    5. export default {
    6. props: {
    7. childTitle: String
    8. }
    9. }
    10. </script>
  • 单向数据流

    父组件数据通过 props 传递给子组件,一旦父组件数据发生变更,会自动把变更数据应用到子组件上,子组件视图也会自动更新。

    原则上,不建议子组件改变 props 数据,一旦修改,这些 props 数据不会反过来自动应用到父组件上,如果需要将子组件数据同步给父组件,建议使用事件方式来同步该数据状态:

    1. <template>
    2. <child :num="num" @numChange="onNumChange"></child>
    3. </template>
    4. <script>
    5. export default {
    6. data: {
    7. num: 1
    8. },
    9. methods: {
    10. onNumChange(e) {
    11. this.num = e.detail.value;
    12. }
    13. }
    14. }
    15. </script>
    1. <template>
    2. <view>{{num}}</view>
    3. <button @click="changeNum">change number</button>
    4. </template>
    5. <script>
    6. export default {
    7. props: {
    8. num: Number
    9. },
    10. methods: {
    11. changeNum() {
    12. this.$emit('numChange', 10);
    13. }
    14. }
    15. }
    16. </script>

    一般,有如下两个场景可能需要修改 props 数据:

    • 一种可能需要将传入的数据重新格式化展现或者其他目的,这种情况下,可以使用 computed

      1. export default {
      2. props: {
      3. myTitle: String
      4. },
      5. computed: {
      6. normalizeTitle() {
      7. return this.myTitle.toLowerCase();
      8. }
      9. }
      10. }
    • 另外一种,可能需要将传入的属性数据作为子组件自身状态的一个初始值,后续子组件会自己维护该状态数据变更

      1. export default {
      2. props: {
      3. initCounter: Number
      4. },
      5. data: {
      6. counter: 0
      7. },
      8. created() {
      9. this.counter = this.initCounter;
      10. }
      11. }

模板 slot

原生小程序 slot

扩展 API

  • $api: 挂载所有原生小程序 API,Page 上下文访问:this.$api.xxx() 等价于 swan.xxx(),建议小程序都摒弃特定小程序命名空间 API 访问方式,方便以后同一套代码转成其它平台端的小程序;
  • $global: 小程序提供的全局对象;
  • $app: app 实例
  • $http: 封装了 swan.request 接口的 HTTP 请求接口;详见-HTTP 请求
  • App 入口文件声明 Promise 化,Page 能直接使用;详见-Promise化 API
  • data 操作语法类似于 Vue,支持 computed$nextTick详见-数据操作
  • 可通过 $broadcast, $emit 进行组件通信,详见下面章节介绍

组件通信

  • 触发事件

子组件触发事件,父组件进行事件接收,原生小程序 使用 this.triggerEvent('eventName', eventData);okam 框架使用 $emit 来进行事件触发。

使用:

  1. this.$emit('eventName', eventData);

示例:

  1. // 子组件
  2. this.$emit('hi', {name: 'Jack'});
  3. // 父组件
  4. <template>
  5. <hello @hi="handleHi">
  6. </hello>
  7. </template>
  8. <script>
  9. {
  10. methods: {
  11. handleHi(...args) {
  12. console.log('hi trigger...', args);
  13. }
  14. }
  15. }
  16. </script>