1. 自定义组件复用
基本组件封装
js
// components/common-button/common-button.js
Component({
properties: {
// 定义属性
type: {
type: String,
value: 'primary' // primary/default/danger
},
text: String,
disabled: Boolean
},
methods: {
handleTap() {
if (!this.data.disabled) {
this.triggerEvent('tap')
}
}
}
})
html
<!-- components/common-button/common-button.wxml -->
<button
class="common-btn {{type}} {{disabled ? 'disabled' : ''}}"
bindtap="handleTap"
>
{{text}}
</button>
css
/* components/common-button/common-button.wxss */
.common-btn {
padding: 20rpx 40rpx;
border-radius: 8rpx;
font-size: 28rpx;
}
.common-btn.primary {
background: #07c160;
color: #fff;
}
.common-btn.disabled {
opacity: 0.5;
}
使用组件
json
// 页面.json
{
"usingComponents": {
"common-button": "/components/common-button/common-button"
}
}
html
<!-- 页面.wxml -->
<common-button
text="提交"
type="primary"
bindtap="onSubmit"
/>
<common-button
text="取消"
type="default"
bindtap="onCancel"
/>
2. Behavior(行为)复用
创建公共Behavior
js
// behaviors/loading-behavior.js
module.exports = Behavior({
data: {
isLoading: false
},
methods: {
// 显示加载
showLoading(text = '加载中...') {
wx.showLoading({ title: text })
this.setData({ isLoading: true })
},
// 隐藏加载
hideLoading() {
wx.hideLoading()
this.setData({ isLoading: false })
},
// 带加载状态的请求
async requestWithLoading(options) {
this.showLoading()
try {
const res = await this.request(options)
return res
} finally {
this.hideLoading()
}
}
}
})
在多个组件中复用
js
// components/user-card/user-card.js
const loadingBehavior = require('../../behaviors/loading-behavior')
Component({
behaviors: [loadingBehavior],
methods: {
async fetchUserInfo() {
const data = await this.requestWithLoading({
url: '/api/user/info'
})
this.setData({ userInfo: data })
}
}
})
js
// components/product-list/product-list.js
const loadingBehavior = require('../../behaviors/loading-behavior')
Component({
behaviors: [loadingBehavior],
methods: {
async loadProducts() {
const products = await this.requestWithLoading({
url: '/api/products'
})
this.setData({ products })
}
}
})
3. Mixins模式实现
虽然小程序不直接支持Mixins,但可以通过Behavior模拟:
js
// behaviors/form-behavior.js
module.exports = Behavior({
data: {
formData: {},
formErrors: {}
},
methods: {
// 表单字段变化
onFormChange(e) {
const { field } = e.currentTarget.dataset
const { value } = e.detail
this.setData({
[`formData.${field}`]: value,
[`formErrors.${field}`]: ''
})
},
// 表单验证
validateForm(rules) {
const errors = {}
let isValid = true
Object.keys(rules).forEach(field => {
const value = this.data.formData[field]
const rule = rules[field]
if (rule.required && !value) {
errors[field] = rule.message || '该字段必填'
isValid = false
}
// 更多验证规则...
})
this.setData({ formErrors: errors })
return isValid
}
}
})
4. 高阶组件模式
创建高阶组件工厂
js
// utils/with-auth.js
function withAuth(Component) {
return function(options) {
const originalOnLoad = options.methods.onLoad || function() {}
options.methods.onLoad = function(params) {
// 检查登录状态
if (!this.checkAuth()) {
wx.redirectTo({ url: '/pages/login/login' })
return
}
originalOnLoad.call(this, params)
}
// 添加登录检查方法
options.methods.checkAuth = function() {
const token = wx.getStorageSync('token')
return !!token
}
return Component(options)
}
}
使用高阶组件
js
// pages/user/user.js
import withAuth from '../../utils/with-auth'
const originalOptions = {
data: { userInfo: {} },
methods: {
onLoad() {
this.loadUserInfo()
},
loadUserInfo() {
// 加载用户信息
}
}
}
// 包装原始配置
withAuth(Component)(originalOptions)
5. 工具函数复用
按功能分类的工具函数
js
// utils/date-util.js
// 日期格式化
export function formatDate(date, format = 'YYYY-MM-DD') {
if (!date) return ''
const d = new Date(date)
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
}
// 相对时间
export function relativeTime(date) {
const now = new Date()
const target = new Date(date)
const diff = now - target
const minutes = Math.floor(diff / 60000)
if (minutes < 60) return `${minutes}分钟前`
const hours = Math.floor(minutes / 60)
if (hours < 24) return `${hours}小时前`
const days = Math.floor(hours / 24)
if (days < 30) return `${days}天前`
return formatDate(date)
}
js
// utils/request-util.js
// 统一的网络请求
export function request(options) {
const token = wx.getStorageSync('token')
const header = {
'Content-Type': 'application/json',
...options.header
}
if (token) {
header['Authorization'] = `Bearer ${token}`
}
return new Promise((resolve, reject) => {
wx.request({
url: `https://api.example.com${options.url}`,
method: options.method || 'GET',
data: options.data,
header,
success: (result) => {
if (result.statusCode === 200) {
resolve(result.data)
} else {
reject(result.data)
}
},
fail: reject
})
})
}
6. 组合式复用
复杂组件的组合
js
// components/smart-form/smart-form.js
const formBehavior = require('../../behaviors/form-behavior')
const validationBehavior = require('../../behaviors/validation-behavior')
Component({
behaviors: [formBehavior, validationBehavior],
methods: {
// 继承自formBehavior的onFormChange
// 继承自validationBehavior的validateForm
async onSubmit() {
if (!this.validateForm(this.data.rules)) {
return
}
// 提交表单
const result = await this.submitData(this.data.formData)
if (result.success) {
this.triggerEvent('success', result.data)
}
}
}
})
7. 模板复用
WXML模板
html
<!-- templates/empty-state.wxml -->
<template name="emptyState">
<view class="empty-container">
<image src="{{icon}}" class="empty-icon" />
<text class="empty-text">{{text}}</text>
<button wx:if="{{showButton}}" bindtap="onButtonTap">
{{buttonText}}
</button>
</view>
</template>
<template name="loading">
<view class="loading-container">
<image src="/images/loading.gif" class="loading-icon" />
<text>{{text || '加载中...'}}</text>
</view>
</template>
使用模板
html
<!-- 页面.wxml -->
<import src="/templates/empty-state.wxml" />
<import src="/templates/loading.wxml" />
<view wx:if="{{isLoading}}">
<template is="loading" data="{{text: '拼命加载中...'}}" />
</view>
<view wx:elif="{{isEmpty}}">
<template
is="emptyState"
data="{{{
icon: '/images/empty.png',
text: '暂无数据',
showButton: true,
buttonText: '刷新试试'
}}}"
/>
</view>
8. 样式复用策略
创建样式变量
css
/* styles/variables.wxss */
:root {
--primary-color: #07c160;
--danger-color: #fa5151;
--text-color: #333;
--border-color: #eee;
--font-size-sm: 24rpx;
--font-size-md: 28rpx;
--font-size-lg: 32rpx;
--border-radius: 8rpx;
}
复用样式类
css
/* styles/common.wxss */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.text-ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.hairline {
position: relative;
}
.hairline::after {
content: '';
position: absolute;
left: 0;
right: 0;
bottom: 0;
border-bottom: 1rpx solid var(--border-color);
transform: scaleY(0.5);
}
在页面中引入
css
/* 页面.wxss */
@import "/styles/variables.wxss";
@import "/styles/common.wxss";
.user-card {
composes: flex-center;
padding: 20rpx;
border-radius: var(--border-radius);
background: #fff;
}
9. 数据状态复用
创建全局状态管理器
js
// store/user-store.js
class UserStore {
constructor() {
this._listeners = []
this._userInfo = null
}
// 获取用户信息
getUserInfo() {
if (!this._userInfo) {
this._userInfo = wx.getStorageSync('userInfo')
}
return this._userInfo
}
// 更新用户信息
setUserInfo(userInfo) {
this._userInfo = userInfo
wx.setStorageSync('userInfo', userInfo)
this._notifyListeners()
}
// 订阅变化
subscribe(listener) {
this._listeners.push(listener)
return () => {
const index = this._listeners.indexOf(listener)
if (index > -1) this._listeners.splice(index, 1)
}
}
_notifyListeners() {
this._listeners.forEach(listener => listener(this._userInfo))
}
}
// 导出单例
export default new UserStore()
在多个页面中使用
js
// pages/profile/profile.js
import userStore from '../../store/user-store'
Page({
onLoad() {
// 获取用户信息
this.setData({
userInfo: userStore.getUserInfo()
})
// 订阅更新
this.unsubscribe = userStore.subscribe((userInfo) => {
this.setData({ userInfo })
})
},
onUnload() {
// 取消订阅
this.unsubscribe && this.unsubscribe()
}
})
10. 复用最佳实践
1. 按职责分层
bash
utils/
├── date-util.js # 日期处理
├── request-util.js # 网络请求
├── validate-util.js # 验证工具
└── storage-util.js # 存储工具
behaviors/
├── form-behavior.js
├── loading-behavior.js
└── auth-behavior.js
components/
├── common/ # 通用组件
├── business/ # 业务组件
└── layout/ # 布局组件
mixins/ # 混合功能
templates/ # 模板文件
styles/ # 样式文件
2. 创建配置中心
arduino
// config/app-config.js
export default {
// API配置
api: {
baseURL: 'https://api.example.com',
timeout: 10000
},
// 页面路径
pages: {
login: '/pages/login/login',
home: '/pages/home/home'
},
// 存储键名
storageKeys: {
token: 'user_token',
userInfo: 'user_info'
}
}
3. 文档和示例
为每个复用组件创建示例页面:
css
components/common-button/
├── common-button.js
├── common-button.wxml
├── common-button.wxss
└── example/
├── example.js # 使用示例
└── example.wxml
复用带来的好处
- 减少代码量:避免重复编写相同逻辑
- 便于维护:修改只需在一处进行
- 一致性:保证功能和行为统一
- 团队协作:提供标准化的开发模式
- 包体积优化:显著减少重复代码
通过合理的代码复用,不仅能减小包体积,还能提升开发效率和代码质量。建议在项目初期就规划好复用策略,建立代码规范。