为什么要抽自定义组件

小程序页面写多了,会发现同样的商品卡片、同样的弹窗、同样的列表项在每个页面复制粘贴。改一处要改五遍,很容易漏。自定义组件就是把这段重复的界面连同它的逻辑封装成一个独立单元,页面里像用内置标签一样用它。

一次封装,到处使用;改组件一处,所有页面跟着变。
组件化的收益
  • 复用同一段 UI 不再复制粘贴。
  • 一致视觉与交互由组件统一约束。
  • 解耦页面只管排版,细节交给组件。
自定义组件配图:多个可拼接的圆角组件块相互咬合拼装成完整面板,接缝处发光连线
组件像积木一样拼起来,组成页面这块完整的面板。

Component 构造器四件套

一个自定义组件由四个文件组成:wxml 结构、wxss 样式、js 逻辑、json 声明。json 里要写明 component: true,小程序才认它是组件。

// card.json
{ "component": true }

// card.js
Component({
  properties: {
    title: { type: String, value: '' }
  },
  data: { count: 0 },
  methods: {
    onTap() { this.setData({ count: this.data.count + 1 }); }
  }
})

页面要用组件时,先在页面 json 的 usingComponents 里登记标签名与路径,再在 wxml 里直接写标签即可。

properties 与事件通信

组件与外部的通信分两个方向。父页面给组件传数据,走 properties——声明类型与默认值。组件内部要通知父页面,走 triggerEvent 发自定义事件。

// 组件内:被点击时通知页面
this.triggerEvent('itemtap', { id: this.data.id });

// 页面 wxml 中监听
// <my-card bind:itemtap="onCardTap" />

注意一个原则:组件不要直接改父页面传进来的 properties,只读;要改就发事件让父页面决定。这样数据流才单向、可追踪。

插槽与外部样式

组件的 wxml 里可以放 slot 节点,父页面在组件标签之间塞进来的内容就会插到这里。这让组件既能定骨架、又允许页面填自定义内容。

<!-- 组件 wxml -->
<view class="box">
  <slot></slot>
</view>

<!-- 页面使用 -->
<my-card>这里是页面塞进来的内容</my-card>

样式默认是隔离的(组件 wxss 不影响页面,页面也改不了组件内部)。需要外部干预时,用 externalClasses 声明几个外部样式类,由页面传入。

封装一个业务卡片组件

把商品列表项抽成 my-card:接收标题、价格、图片三个 properties,点击时把 id 通过事件抛给页面。

Component({
  properties: {
    title: String,
    price: Number,
    img: String,
    id: Number
  },
  methods: {
    tap() { this.triggerEvent('tap', { id: this.data.id }); }
  }
})

之后所有列表页都用同一个 my-card,商品卡片长什么样、点起来什么反应,只改这一个组件。页面代码清爽很多。