为什么要抽自定义组件
小程序页面写多了,会发现同样的商品卡片、同样的弹窗、同样的列表项在每个页面复制粘贴。改一处要改五遍,很容易漏。自定义组件就是把这段重复的界面连同它的逻辑封装成一个独立单元,页面里像用内置标签一样用它。
一次封装,到处使用;改组件一处,所有页面跟着变。
- 复用同一段 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,商品卡片长什么样、点起来什么反应,只改这一个组件。页面代码清爽很多。
组件库的组织与复用
组件多了,就该有个统一的目录(比如 components/)按类别分子文件夹,并写一份 README 说明每个组件的 properties 与事件。跨项目复用时,把 components 目录连同样式变量一起拷过去即可。
更进一步可以做成「独立分包」或通过 npm 引入的自定义组件库。但对多数团队,先做到「单项目内组件统一维护」就已经收益巨大,不必一上来追求发布成库。
小结与延伸阅读
自定义组件的核心是「properties 进、事件出」,配合 slot 与 externalClasses 就能覆盖绝大多数复用场景。把重复 UI 抽成组件,是小程序工程化的第一步。
- 小程序怎么起,读《小程序入门与首个 Demo》。
- 包体积与分包治理,看《小程序安全与性能治理实战》。
- 服务端云能力,看《云开发入门与实战》。