一、前言
随着微信小程序开发复杂度的提升,官方提供的基础组件已无法满足日益增长的业务需求。为了提高代码的复用性、可维护性与开发效率,自定义组件 成为了微信小程序开发中不可或缺的重要技能。
本文将带你从零开始,全面掌握 微信小程序自定义组件的开发流程,包括:
✅ 自定义组件的基本结构
✅ 如何创建与使用组件
✅ 组件与页面之间的通信
✅ 组件间传值(props)
✅ 组件事件绑定与触发(自定义事件)
✅ 实战案例:封装一个可复用的“评分组件”
✅ 常见问题与优化建议
并通过完整代码示例帮助你掌握如何在实际项目中高效使用自定义组件。
二、什么是微信小程序自定义组件?
微信小程序的 自定义组件 是开发者基于官方组件封装的、具有独立结构和功能的 UI 模块,支持在多个页面中复用。它与页面类似,但不能独立显示,必须被嵌套在页面或其他组件中使用。
✅ 自定义组件的优势:
优势 | 描述 |
---|---|
提高复用性 | 一次编写,多处使用 |
提高可维护性 | 修改一处,全局生效 |
提高开发效率 | 减少重复代码 |
逻辑与视图分离 | 更清晰的组件化结构 |
三、自定义组件的基本结构
一个自定义组件通常由以下 4 个文件组成:
components/
└── custom-component/
├── component.js
├── component.json
├── component.wxml
└── component.wxss
✅ component.json
(组件配置)
{
"component": true
}
必须设置
"component": true
,表示这是一个组件。
✅ component.js
(组件逻辑)
Component({
options: {
multipleSlots: true // 允许使用多个插槽
},
properties: {
// 用于接收父组件传入的值
title: {
type: String,
value: '默认标题'
}
},
data: {
// 组件内部状态
},
methods: {
// 组件内部方法
}
});
✅ component.wxml
(组件结构)
<view class="component-container">
<text>{{title}}</text>
</view>
✅ component.wxss
(组件样式)
.component-container {
padding: 20rpx;
background-color: #f0f0f0;
border-radius: 8rpx;
}
四、如何在页面中使用自定义组件
✅ 1. 引入组件
在使用组件的页面的 json
文件中引入组件:
{
"usingComponents": {
"custom-component": "/components/custom-component/component"
}
}
✅ 2. 在 WXML 中使用组件
<custom-component title="这是一个自定义组件" />
五、组件通信:传值与事件
✅ 1. 父组件 → 子组件:通过 properties
传值
// 子组件 component.js
properties: {
name: {
type: String,
value: '默认名称'
}
}
<!-- 父页面使用 -->
<custom-component name="ethan" />
✅ 2. 子组件 → 父组件:通过 triggerEvent
触发事件
// 子组件 component.js
methods: {
onClick() {
this.triggerEvent('click', { detail: '按钮被点击了' });
}
}
<!-- 父页面监听 -->
<custom-component bind:click="handleClick" />
// 父页面 index.js
Page({
handleClick(e) {
console.log(e.detail); // 输出:按钮被点击了
}
});
六、插槽(Slots):组件内容插入
✅ 默认插槽
<!-- 父页面 -->
<custom-component>
<view>插入的内容</view>
</custom-component>
<!-- 组件 component.wxml -->
<view class="component-container">
<slot></slot>
</view>
✅ 多插槽
// component.json
{
"component": true,
"usingComponents": {}
}
<!-- 组件 component.wxml -->
<view class="component-container">
<slot name="header"></slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</view>
<!-- 父页面使用 -->
<custom-component>
<view slot="header">头部内容</view>
<view slot="body">主体内容</view>
<view slot="footer">底部内容</view>
</custom-component>
七、实战案例:封装一个“评分组件”
✅ 功能描述:
- 显示星星评分(1~5)
- 支持用户点击评分
- 可设置只读模式
- 支持分数回传给父组件
✅ 目录结构:
components/
└── rating/
├── rating.js
├── rating.json
├── rating.wxml
└── rating.wxss
✅ rating.js
Component({
properties: {
score: {
type: Number,
value: 0
},
readonly: {
type: Boolean,
value: false
}
},
data: {
stars: []
},
observers: {
'score': function(score) {
this.setData({
stars: this.generateStars(score)
});
}
},
methods: {
generateStars(score) {
const full = Math.floor(score);
const half = score % 1 >= 0.5 ? 1 : 0;
const empty = 5 - full - half;
return [...Array(full).fill('full'), ...Array(half).fill('half'), ...Array(empty).fill('empty')];
},
onStarClick(e) {
if (this.data.readonly) return;
const index = e.currentTarget.dataset.index;
const newScore = index + 1;
this.setData({ score: newScore });
this.triggerEvent('change', { score: newScore });
}
}
});
✅ rating.wxml
<view class="rating-container">
<block wx:for="{{stars}}" wx:key="index">
<image
data-index="{{index}}"
src="/images/{{item === 'full' ? 'star-full.png' : item === 'half' ? 'star-half.png' : 'star-empty.png'}}"
mode="aspectFit"
bindtap="onStarClick"
/>
</block>
</view>
✅ rating.wxss
.rating-container image {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
✅ 页面使用组件
{
"usingComponents": {
"rating-component": "/components/rating/rating"
}
}
<rating-component score="{{3.5}}" bind:change="onRatingChange" />
Page({
onRatingChange(e) {
console.log('评分变化:', e.detail.score);
}
});
八、常见问题与解决方案
问题 | 原因 | 解决方案 |
---|---|---|
组件不显示 | 未正确引入或路径错误 | 检查 usingComponents 配置 |
数据未更新 | 未使用 setData | 组件中使用 setData 更新数据 |
事件未触发 | 未绑定或拼写错误 | 检查事件名是否一致 |
插槽内容不显示 | 插槽名不匹配 | 检查 slot 名称是否一致 |
图片路径错误 | 路径相对位置不对 | 使用绝对路径或检查层级关系 |
九、总结对比表:组件开发关键点一览
类型 | 内容 |
---|---|
组件配置 | component.json 中设置 "component": true |
组件传值 | properties 接收父组件数据 |
组件事件 | triggerEvent 向父组件发送事件 |
插槽使用 | <slot> 或具名插槽 <slot name="xxx"> |
数据更新 | 使用 setData 更新组件内部状态 |
组件复用 | 可在多个页面中引入并使用 |
组件通信 | 支持父子双向通信 |
十、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!