微信小程序开发规范
本文档为微信小程序项目的统一开发规范,适用于人工开发与 AI 辅助开发场景。所有代码必须遵循本规范,以保证项目的可维护性、可扩展性和一致性。
目录
- [1. 代码结构规范](#1. 代码结构规范)
- [2. 分包规范](#2. 分包规范)
- [3. 页面跳转规范](#3. 页面跳转规范)
- [4. 组件封装规范](#4. 组件封装规范)
- [5. 静态资源处理规范](#5. 静态资源处理规范)
- [6. API 与网络请求规范](#6. API 与网络请求规范)
- [7. 状态管理规范](#7. 状态管理规范)
- [8. 样式规范](#8. 样式规范)
- [9. 性能优化规范](#9. 性能优化规范)
- [10. 安全规范](#10. 安全规范)
1. 代码结构规范
1.1 整体目录结构
miniprogram/
├── app.js # 小程序入口
├── app.json # 全局配置
├── app.wxss # 全局样式
├── sitemap.json # 站点地图配置
├── project.config.json # 项目配置
├── envList.js # 环境变量配置
│
├── pages/ # 主包页面(TabBar 页面 + 核心页面)
│ ├── index/ # 首页
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ └── ...
│
├── packageA/ # 分包 A(按业务模块划分)
│ └── pages/
│ └── ...
│
├── components/ # 全局公共组件
│ ├── base/ # 基础组件(按钮、输入框等)
│ ├── business/ # 业务组件(商品卡片、订单列表等)
│ └── layout/ # 布局组件(导航栏、底部栏等)
│
├── utils/ # 工具函数
│ ├── request.js # 网络请求封装
│ ├── storage.js # 本地存储封装
│ ├── format.js # 格式化工具
│ ├── validate.js # 校验工具
│ └── auth.js # 鉴权工具
│
├── api/ # 接口定义层
│ ├── modules/ # 按业务模块拆分
│ │ ├── user.js
│ │ ├── order.js
│ │ └── ...
│ └── index.js # 统一导出
│
├── store/ # 状态管理(如使用 MobX / 自实现)
│ ├── index.js
│ └── modules/
│
├── styles/ # 全局样式变量与混入
│ ├── variables.wxss # 样式变量
│ ├── mixin.wxss # 样式混入
│ └── reset.wxss # 样式重置
│
├── assets/ # 静态资源
│ ├── images/ # 图片资源
│ ├── icons/ # 图标资源
│ └── fonts/ # 字体资源
│
├── config/ # 配置文件
│ ├── routes.js # 路由配置表
│ └── constants.js # 常量定义
│
└── behaviors/ # 公共行为(代码复用)
└── ...
1.2 文件命名规范
| 类型 | 命名规则 | 示例 |
|---|---|---|
| 页面目录 | 小写 + 中划线(kebab-case) | order-detail/ |
| 页面文件 | 与目录同名 | order-detail.js |
| 组件目录 | 小写 + 中划线 | goods-card/ |
| 组件文件 | 与目录同名 | goods-card.js |
| 工具函数 | 小驼峰(camelCase) | formatDate.js |
| 常量文件 | 全大写 + 下划线 | USER_STATUS.js |
| 图片资源 | 小写 + 下划线 | icon_home.png |
1.3 JS 代码规范
1.3.1 基本规则
- 统一使用 ES6+ 语法,优先使用
const/let,禁止使用var - 字符串统一使用 单引号,模板字符串使用反引号
- 缩进使用 2 个空格
- 语句末尾 不加分号(与现代前端风格保持一致)
- 单行代码长度不超过 120 字符
1.3.2 页面/组件 JS 结构顺序
javascript
// 1. 引入依赖(按顺序:第三方 → 工具 → 组件 → 样式)
import { formatDate } from '../../utils/format'
import { getUserInfo } from '../../api/modules/user'
// 2. 常量定义
const PAGE_SIZE = 20
// 3. Page / Component 定义
Page({
// 3.1 数据
data: {
list: [],
loading: false,
hasMore: true
},
// 3.2 生命周期(按执行顺序排列)
onLoad(options) {
this.initData(options)
},
onShow() {},
onReady() {},
onHide() {},
onUnload() {},
onPullDownRefresh() {},
onReachBottom() {},
onShareAppMessage() {},
// 3.3 自定义方法(按功能分组,私有方法以下划线开头)
// --- 数据初始化 ---
async initData(options) {
// ...
},
// --- 网络请求 ---
async fetchList() {
// ...
},
// --- 事件处理 ---
handleItemTap(e) {
// ...
},
// --- 私有方法 ---
_calcTotal() {
// ...
}
})
1.3.3 函数规范
- 函数名使用 小驼峰 ,动词开头:
getUserInfo、handleTap、calcPrice - 事件处理函数统一以
handle开头:handleSubmit、handleDelete - 异步函数统一使用
async/await,配合try/catch错误处理 - 函数参数超过 3 个时,使用对象传参
javascript
// ✅ 推荐
function createOrder({ goodsId, count, addressId, couponId }) {
// ...
}
// ❌ 不推荐
function createOrder(goodsId, count, addressId, couponId) {
// ...
}
1.4 WXML 规范
- 标签名一律小写,属性值使用双引号
- 布尔值属性省略值:
<button loading />而非<button loading="{``{true}}" /> - 复杂逻辑抽取到 JS 的
computed或wxs中,WXML 只做展示 wx:for必须指定wx:key,列表项有唯一 id 时用 id,否则用*this
xml
<!-- ✅ 推荐 -->
<view class="goods-list">
<view
wx:for="{{list}}"
wx:key="id"
class="goods-item"
bindtap="handleItemTap"
data-id="{{item.id}}"
>
<image class="goods-img" src="{{item.cover}}" mode="aspectFill" />
<text class="goods-name">{{item.name}}</text>
</view>
</view>
1.5 JSON 配置规范
- 页面级
json只配置当前页面独有的属性,公共配置放app.json - 组件
json必须设置"component": true - 按需引入组件,避免全局注册过多组件
2. 分包规范
2.1 分包原则
| 原则 | 说明 |
|---|---|
| 主包精简 | 主包只放 TabBar 页面和核心公共资源,体积控制在 2MB 以内 |
| 按业务划分 | 每个分包对应一个独立业务模块,模块间低耦合 |
| 按需加载 | 非首屏页面全部放入分包,减少主包体积 |
| 公共抽离 | 多分包共用的组件/工具抽到主包或独立公共分包 |
2.2 分包划分策略
主包(main)
├── TabBar 页面(首页、分类、购物车、我的)
├── 全局公共组件
├── 工具函数
├── 全局样式
└── 基础配置
分包 A:商品模块(package-goods)
├── 商品详情
├── 商品搜索
├── 商品分类列表
└── 商品评价
分包 B:订单模块(package-order)
├── 订单列表
├── 订单详情
├── 确认订单
├── 售后申请
└── 退款进度
分包 C:用户模块(package-user)
├── 个人资料
├── 地址管理
├── 收藏夹
├── 优惠券
└── 积分中心
分包 D:营销模块(package-marketing)
├── 拼团活动
├── 秒杀活动
├── 签到
└── 邀请好友
独立分包:登录模块(package-login)
├── 登录页
├── 授权页
└── 绑定手机号
2.3 分包配置示例
json
// app.json
{
"pages": [
"pages/index/index",
"pages/category/category",
"pages/cart/cart",
"pages/user/user"
],
"subpackages": [
{
"root": "package-goods",
"name": "goods",
"pages": [
"pages/detail/detail",
"pages/search/search",
"pages/list/list"
]
},
{
"root": "package-order",
"name": "order",
"pages": [
"pages/list/list",
"pages/detail/detail",
"pages/confirm/confirm"
]
}
],
"preloadRule": {
"pages/index/index": {
"network": "wifi",
"packages": ["goods"]
},
"pages/category/category": {
"network": "all",
"packages": ["goods"]
}
},
"lazyCodeLoading": "requiredComponents"
}
2.4 分包注意事项
- TabBar 页面必须在主包,不能放入分包
- 分包之间不能互相引用 JS 文件和组件,公共资源放主包
- 主包不能引用分包的资源,分包可以引用主包资源
- 图片等静态资源尽量放入 CDN,减少包体积
- 使用 分包预下载 优化用户体验,在进入相关页面前预加载分包
- 启用
lazyCodeLoading: "requiredComponents"按需注入代码
3. 页面跳转规范
3.1 路由统一管理
所有页面路径必须在路由配置表中统一定义,禁止硬编码路径。
javascript
// config/routes.js
/**
* 路由配置表
* path: 页面路径(绝对路径)
* name: 路由名称(唯一标识)
* package: 所属分包(主包为空)
* needAuth: 是否需要登录
*/
const ROUTES = {
// 主包
HOME: { path: '/pages/index/index', name: 'home', needAuth: false },
CATEGORY: { path: '/pages/category/category', name: 'category', needAuth: false },
CART: { path: '/pages/cart/cart', name: 'cart', needAuth: false },
USER: { path: '/pages/user/user', name: 'user', needAuth: false },
// 商品分包
GOODS_DETAIL: { path: '/package-goods/pages/detail/detail', name: 'goodsDetail', package: 'goods', needAuth: false },
GOODS_SEARCH: { path: '/package-goods/pages/search/search', name: 'goodsSearch', package: 'goods', needAuth: false },
// 订单分包
ORDER_LIST: { path: '/package-order/pages/list/list', name: 'orderList', package: 'order', needAuth: true },
ORDER_DETAIL: { path: '/package-order/pages/detail/detail', name: 'orderDetail', package: 'order', needAuth: true },
ORDER_CONFIRM: { path: '/package-order/pages/confirm/confirm', name: 'orderConfirm', package: 'order', needAuth: true }
}
export default ROUTES
3.2 跳转方式选择
| 场景 | API | 说明 |
|---|---|---|
| 普通页面跳转(非 TabBar) | wx.navigateTo |
保留当前页,最多 10 层 |
| 关闭当前页跳转 | wx.redirectTo |
替换当前页,不保留 |
| 跳转到 TabBar 页面 | wx.switchTab |
只能用于 TabBar 页面 |
| 关闭所有页跳转 | wx.reLaunch |
清空页面栈,重新开始 |
| 返回上一页 | wx.navigateBack |
返回页面栈中的页面 |
3.3 统一跳转封装
javascript
// utils/navigator.js
import ROUTES from '../config/routes'
import { checkAuth } from './auth'
/**
* 统一页面跳转方法
* @param {string} routeName - 路由名称(ROUTES 中的 key)
* @param {object} params - 页面参数
* @param {object} options - 跳转选项
* @param {string} options.type - 跳转类型:navigateTo / redirectTo / switchTab / reLaunch
* @param {boolean} options.skipAuth - 是否跳过鉴权检查
*/
function navigateTo(routeName, params = {}, options = {}) {
const route = ROUTES[routeName]
if (!route) {
console.error(`[navigator] 路由不存在: ${routeName}`)
return
}
// 鉴权检查
if (route.needAuth && !options.skipAuth) {
const isLogin = checkAuth()
if (!isLogin) {
// 未登录,跳转到登录页
wx.navigateTo({
url: `${ROUTES.LOGIN.path}?redirect=${encodeURIComponent(routeName)}`
})
return
}
}
// 拼接参数
let url = route.path
const queryStr = Object.keys(params)
.filter(key => params[key] !== undefined && params[key] !== null)
.map(key => `${key}=${encodeURIComponent(params[key])}`)
.join('&')
if (queryStr) {
url += `?${queryStr}`
}
// 选择跳转方式
const type = options.type || 'navigateTo'
// TabBar 页面强制使用 switchTab
if (isTabBarPage(routeName)) {
return wx.switchTab({ url })
}
switch (type) {
case 'redirectTo':
return wx.redirectTo({ url })
case 'reLaunch':
return wx.reLaunch({ url })
case 'switchTab':
return wx.switchTab({ url })
default:
return wx.navigateTo({ url })
}
}
/**
* 返回上一页
* @param {number} delta - 返回层数
*/
function navigateBack(delta = 1) {
const pages = getCurrentPages()
if (pages.length <= delta) {
// 无法返回时,回到首页
return wx.switchTab({ url: ROUTES.HOME.path })
}
return wx.navigateBack({ delta })
}
// 判断是否为 TabBar 页面
function isTabBarPage(routeName) {
const tabBarRoutes = ['HOME', 'CATEGORY', 'CART', 'USER']
return tabBarRoutes.includes(routeName)
}
export default {
to: navigateTo,
back: navigateBack
}
3.4 使用示例
javascript
import navigator from '../../utils/navigator'
// 普通跳转
navigator.to('GOODS_DETAIL', { id: 123 })
// 带参数跳转
navigator.to('ORDER_LIST', { status: 'pending' })
// 重定向(不保留当前页)
navigator.to('LOGIN', {}, { type: 'redirectTo' })
// 返回上一页
navigator.back()
// 返回两层
navigator.back(2)
3.5 参数传递规范
- 简单参数:通过 URL query 传递(字符串、数字)
- 复杂对象 :使用
storage临时存储 + 页面 id 关联,或使用全局状态管理 - 禁止在 URL 中传递超长字符串(> 200 字符),避免 URL 截断
- 接收参数时必须做默认值处理
javascript
onLoad(options) {
const id = Number(options.id) || 0
const type = options.type || 'default'
// ...
}
4. 组件封装规范
4.1 组件分类
| 分类 | 目录 | 说明 | 示例 |
|---|---|---|---|
| 基础组件 | components/base/ |
通用 UI 组件,无业务逻辑 | base-button、base-input |
| 业务组件 | components/business/ |
包含业务逻辑的可复用组件 | goods-card、order-item |
| 布局组件 | components/layout/ |
页面布局相关 | nav-bar、bottom-bar |
| 页面级组件 | pages/xxx/components/ |
仅当前页面使用的组件 | 就近放置 |
4.2 组件设计原则
- 单一职责:一个组件只做一件事,功能明确
- 可配置 :通过
properties配置,不硬编码业务数据 - 受控组件:组件状态由父组件控制,内部不维护核心数据
- 事件冒泡:自定义事件命名规范,语义清晰
- 插槽灵活 :合理使用
slot支持内容定制
4.3 组件文件结构
components/
└── base/
└── base-button/
├── index.js # 组件逻辑
├── index.json # 组件配置
├── index.wxml # 组件模板
├── index.wxss # 组件样式
└── README.md # 组件文档(可选,复杂组件必备)
4.4 组件 JS 规范
javascript
// components/base/base-button/index.js
Component({
// 启用多 slot 支持
options: {
multipleSlots: true,
// 样式隔离:apply-shared 允许继承外部样式
styleIsolation: 'apply-shared'
},
// 组件属性
properties: {
// 按钮类型:primary / default / danger
type: {
type: String,
value: 'default'
},
// 按钮尺寸:large / medium / small
size: {
type: String,
value: 'medium'
},
// 是否禁用
disabled: {
type: Boolean,
value: false
},
// 是否加载中
loading: {
type: Boolean,
value: false
},
// 自定义样式类
customClass: {
type: String,
value: ''
}
},
// 组件内部数据
data: {
innerLoading: false
},
// 监听属性变化
observers: {
'loading': function(val) {
this.setData({ innerLoading: val })
}
},
// 组件方法
methods: {
handleTap(e) {
if (this.data.disabled || this.data.innerLoading) return
// 触发父组件事件
this.triggerEvent('tap', {
// 可携带额外数据
timestamp: Date.now()
}, {
bubbles: false,
composed: false
})
}
},
// 生命周期
lifetimes: {
attached() {
// 组件实例进入页面节点树
},
detached() {
// 组件实例被移除
}
},
// 所在页面生命周期
pageLifetimes: {
show() {},
hide() {},
resize() {}
}
})
4.5 组件通信规范
4.5.1 父 → 子:properties 传参
xml
<!-- 父组件 -->
<goods-card
goods="{{item}}"
show-price="{{true}}"
bind:add-cart="handleAddCart"
/>
javascript
// 子组件
properties: {
goods: {
type: Object,
value: {}
},
showPrice: {
type: Boolean,
value: true
}
}
4.5.2 子 → 父:triggerEvent 事件
javascript
// 子组件
this.triggerEvent('change', { value: newValue })
// 父组件
handleChange(e) {
console.log(e.detail.value)
}
4.5.3 父调用子方法:selectComponent
javascript
// 父组件
const dialog = this.selectComponent('#dialog')
dialog.show()
4.5.4 兄弟组件通信
通过父组件中转,或使用全局状态管理,禁止直接互相调用。
4.6 组件使用规范
- 在页面
json中按需引入,禁止全局注册所有组件 - 组件名使用中划线命名:
<goods-card /> - 多个属性换行排列,提升可读性
- 自定义事件统一使用
bind:前缀:bind:change="handleChange"
5. 静态资源处理规范
5.1 资源分类与目录
assets/
├── images/ # 图片资源
│ ├── common/ # 通用图片
│ ├── tabbar/ # TabBar 图标
│ ├── empty/ # 空状态图
│ └── banner/ # Banner 图
├── icons/ # 图标(建议用 iconfont 或 SVG)
└── fonts/ # 字体文件
5.2 图片规范
5.2.1 格式选择
| 场景 | 推荐格式 | 说明 |
|---|---|---|
| 照片类图片 | JPG / WebP | 体积小,适合商品图、Banner |
| 图标、Logo | PNG(透明) | 支持透明背景 |
| 简单图形 | SVG | 矢量图,缩放不失真 |
| 动画 | GIF / Lottie | 动效图标用 Lottie |
5.2.2 尺寸规范
- 图标:
@2x/@3x两套,基础尺寸 24px / 32px / 48px - 商品主图:建议 750px × 750px(正方形)
- Banner 图:建议 750px × 400px(宽高比约 1.875:1)
- 所有图片上线前必须压缩,单张图片不超过 200KB
5.2.3 命名规范
{类型}_{模块}_{描述}_{状态}.{扩展名}
示例:
icon_home_normal.png # 首页图标-正常态
icon_home_active.png # 首页图标-选中态
img_goods_default.png # 商品默认图
img_empty_cart.png # 购物车空状态图
bg_login.jpg # 登录页背景图
5.3 图标方案
优先使用 Iconfont(字体图标),原因:
- 体积小,支持任意缩放
- 可通过
color属性改变颜色 - 支持多色图标
css
/* styles/iconfont.wxss */
@font-face {
font-family: 'iconfont';
src: url('https://at.alicdn.com/t/xxx.woff2') format('woff2');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
}
使用方式:
xml
<text class="iconfont"></text>
5.4 CDN 管理
所有静态资源必须上传 CDN,禁止打包进小程序代码包:
javascript
// config/constants.js
export const CDN_BASE = 'https://cdn.example.com/miniprogram'
export const ASSETS = {
// 图片
IMG_GOODS_DEFAULT: `${CDN_BASE}/images/common/img_goods_default.png`,
IMG_EMPTY_CART: `${CDN_BASE}/images/empty/img_empty_cart.png`,
// TabBar 图标
ICON_HOME_NORMAL: `${CDN_BASE}/images/tabbar/icon_home_normal.png`,
ICON_HOME_ACTIVE: `${CDN_BASE}/images/tabbar/icon_home_active.png`
}
页面中使用:
javascript
import { ASSETS } from '../../config/constants'
Page({
data: {
defaultImg: ASSETS.IMG_GOODS_DEFAULT
}
})
xml
<image src="{{defaultImg}}" mode="aspectFill" />
5.5 图片懒加载
- 列表中的图片统一开启
lazy-load属性 - 使用
image组件的mode属性控制裁剪方式,避免图片变形 - 首屏图片优先加载,非首屏图片懒加载
xml
<image
src="{{item.cover}}"
mode="aspectFill"
lazy-load
show-menu-by-longpress="{{false}}"
/>
6. API 与网络请求规范
6.1 请求封装
javascript
// utils/request.js
import { BASE_URL, TOKEN_KEY } from '../config/constants'
import storage from './storage'
const request = (options) => {
return new Promise((resolve, reject) => {
const token = storage.get(TOKEN_KEY) || ''
wx.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data || {},
header: {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : '',
...options.header
},
timeout: 15000,
success(res) {
const { statusCode, data } = res
// HTTP 状态码处理
if (statusCode === 200) {
// 业务状态码处理
const { code, message, result } = data
if (code === 0) {
resolve(result)
} else if (code === 401) {
// 未登录,清除 token 并跳转登录
storage.remove(TOKEN_KEY)
wx.navigateTo({ url: '/pages/login/login' })
reject(new Error('未登录'))
} else {
wx.showToast({ title: message || '请求失败', icon: 'none' })
reject(new Error(message))
}
} else {
wx.showToast({ title: '网络异常', icon: 'none' })
reject(new Error(`HTTP ${statusCode}`))
}
},
fail(err) {
wx.showToast({ title: '网络连接失败', icon: 'none' })
reject(err)
}
})
})
}
export default {
get: (url, data, options) => request({ url, method: 'GET', data, ...options }),
post: (url, data, options) => request({ url, method: 'POST', data, ...options }),
put: (url, data, options) => request({ url, method: 'PUT', data, ...options }),
delete: (url, data, options) => request({ url, method: 'DELETE', data, ...options })
}
6.2 API 分层
javascript
// api/modules/user.js
import request from '../../utils/request'
// 获取用户信息
export const getUserInfo = () => {
return request.get('/api/user/info')
}
// 更新用户信息
export const updateUserInfo = (data) => {
return request.post('/api/user/update', data)
}
// 上传头像
export const uploadAvatar = (filePath) => {
return new Promise((resolve, reject) => {
wx.uploadFile({
url: BASE_URL + '/api/user/avatar',
filePath,
name: 'file',
header: { Authorization: `Bearer ${token}` },
success(res) {
const data = JSON.parse(res.data)
data.code === 0 ? resolve(data.result) : reject(data.message)
},
fail: reject
})
})
}
javascript
// api/index.js
export * from './modules/user'
export * from './modules/order'
export * from './modules/goods'
6.3 请求规范
- 所有接口调用必须经过
api/层,禁止在页面中直接wx.request - GET 请求用于查询,POST 用于新增,PUT 用于更新,DELETE 用于删除
- 接口参数必须做前端校验后再发送
- 列表接口统一分页参数:
page、pageSize - 加载状态统一管理,避免重复请求
7. 状态管理规范
7.1 方案选择
| 项目规模 | 推荐方案 | 说明 |
|---|---|---|
| 小型项目(< 20 页) | 自实现简单 store | 轻量,无依赖 |
| 中型项目 | MobX-miniprogram | 响应式,开发体验好 |
| 大型项目 | Redux + 中间件 | 严格单向数据流,可追溯 |
7.2 简单 Store 实现
javascript
// store/index.js
import { observable, autorun } from './observer'
class Store {
constructor() {
this.state = observable({
user: {
info: null,
isLogin: false
},
cart: {
count: 0,
list: []
}
})
}
// 用户相关
setUserInfo(info) {
this.state.user.info = info
this.state.user.isLogin = !!info
}
clearUser() {
this.state.user.info = null
this.state.user.isLogin = false
}
// 购物车相关
setCartCount(count) {
this.state.cart.count = count
}
}
export default new Store()
7.3 使用规范
- 全局共享数据才放入 store,页面私有数据放
data - 修改 state 必须通过 store 的方法,禁止直接修改
- 页面订阅 store 变化时,在
onUnload中取消订阅,防止内存泄漏
8. 样式规范
8.1 样式变量
css
/* styles/variables.wxss */
/* 主题色 */
--color-primary: #ff6b35;
--color-primary-light: #ff8c5a;
--color-primary-dark: #e55a2b;
/* 辅助色 */
--color-success: #07c160;
--color-warning: #faad14;
--color-danger: #f5222d;
--color-info: #1890ff;
/* 文字颜色 */
--text-primary: #1a1a1a;
--text-regular: #333333;
--text-secondary: #666666;
--text-placeholder: #999999;
--text-disabled: #cccccc;
/* 背景色 */
--bg-page: #f5f5f5;
--bg-card: #ffffff;
--bg-mask: rgba(0, 0, 0, 0.5);
/* 边框色 */
--border-color: #eeeeee;
--border-color-light: #f5f5f5;
/* 间距 */
--spacing-xs: 8rpx;
--spacing-sm: 16rpx;
--spacing-md: 24rpx;
--spacing-lg: 32rpx;
--spacing-xl: 48rpx;
/* 圆角 */
--radius-sm: 8rpx;
--radius-md: 16rpx;
--radius-lg: 24rpx;
--radius-full: 9999rpx;
/* 字体大小 */
--font-size-xs: 20rpx;
--font-size-sm: 24rpx;
--font-size-md: 28rpx;
--font-size-lg: 32rpx;
--font-size-xl: 36rpx;
8.2 样式编写规范
- 使用 rpx 作为尺寸单位(宽度相关),字体也用 rpx
- 颜色统一使用 CSS 变量,禁止硬编码颜色值
- 类名使用 BEM 命名法 :
block__element--modifier - 禁止使用 ID 选择器、标签选择器(性能差)
- 页面样式使用页面前缀隔离,避免污染全局
css
/* ✅ 推荐 */
.goods-card {
padding: var(--spacing-md);
background: var(--bg-card);
border-radius: var(--radius-md);
}
.goods-card__image {
width: 200rpx;
height: 200rpx;
border-radius: var(--radius-sm);
}
.goods-card__title {
font-size: var(--font-size-md);
color: var(--text-primary);
margin-top: var(--spacing-sm);
}
.goods-card__title--line2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
8.3 通用样式抽取
css
/* styles/mixin.wxss */
/* 单行省略 */
.ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* 多行省略 */
.ellipsis-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* flex 居中 */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
/* 安全区域底部 */
.safe-bottom {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
9. 性能优化规范
9.1 启动性能
- 主包体积控制在 2MB 以内,总包不超过 20MB
- 启用
lazyCodeLoading: "requiredComponents"按需注入 - 非首屏页面全部放入分包
- 使用分包预下载,提前加载用户可能进入的分包
app.js的onLaunch中只做必要的初始化,避免同步阻塞
9.2 渲染性能
setData合并调用,避免频繁触发渲染- 只更新需要变化的数据,不要整个对象替换
- 长列表使用 虚拟列表 或分页加载
- 页面隐藏时停止定时器和动画
- 合理使用
wx:if和hidden:频繁切换用hidden,不频繁用wx:if
javascript
// ✅ 推荐:精确更新
this.setData({
'list[0].count': newCount,
'user.avatar': newAvatar
})
// ❌ 不推荐:整个替换
this.setData({
list: newList,
user: newUser
})
9.3 内存优化
- 页面
onUnload时清除定时器、事件监听 - 及时取消未完成的请求
- 大图及时释放,避免内存泄漏
- 减少全局变量的使用
10. 安全规范
10.1 数据安全
- 敏感信息(token、用户隐私)禁止存入
wx.setStorageSync,使用加密存储 - 接口请求统一加签名,防止篡改
- 用户输入必须做 XSS 过滤,
rich-text组件使用时必须校验内容 - 禁止在前端硬编码密钥、密码等敏感信息
10.2 权限安全
- 所有需要登录的页面必须做鉴权校验
- 敏感操作(支付、删除)二次确认
- 接口权限由后端控制,前端只做展示层控制
10.3 代码安全
- 禁止使用
eval、new Function等动态执行代码 - 小程序发布前必须通过安全检测
- 第三方 SDK 必须审核来源,禁止使用来源不明的代码
附录:AI 开发 Checklist
AI 在生成代码时,必须逐项检查以下内容:
- 目录结构是否符合规范,文件命名是否正确
- JS 代码是否使用 ES6+ 语法,是否有
var - 页面/组件方法顺序是否规范
- 页面跳转是否使用统一封装的 navigator
- 是否有硬编码的页面路径
- 组件是否按分类放置,properties 是否规范
- 自定义事件命名是否语义清晰
- 图片资源是否使用 CDN 地址
- 样式是否使用 CSS 变量,类名是否符合 BEM
- 网络请求是否经过 api 层封装
- 是否有未处理的异常和错误边界
- 是否有性能问题(频繁 setData、内存泄漏)
最后更新 :2026-08-09
维护者 :前端团队
版本:v1.0.0