跳转至

小程序开发规范 (Mini-App Development)

定位:uni-app x 小程序开发标准,一套代码多端发布,确保类型安全、分包优化、可维护性。


技术选型

层级 技术栈 版本要求 选型理由
框架 uni-app x ≥ 4.0 基于 Vue 3、原生渲染、一套代码多端发布
开发语言 uts (Uni Type Script) - 类型安全、编译到原生、性能接近原生
UI 组件 uni-ui / uView 最新 官方/社区组件库,跨端兼容
状态管理 Pinia ≥ 2.0 Vue 3 官方推荐、TS 支持好
网络请求 uni.request 封装 - 统一请求/响应处理
开发工具 HBuilderX 最新 官方 IDE、调试方便

发布平台

平台 说明
微信小程序 主力平台
支付宝小程序 次要平台
抖音小程序 可选
快手小程序 可选
H5 Web 应用
App 原生应用

核心优势:uni-app x 使用 uts 语言,类型安全 + 编译优化,一套代码多端运行。


项目结构

MyApp/
├── pages/                  # 主包页面
│   ├── index/              # 首页
│   │   └── index.uvue
│   └── user/               # 用户
│       └── profile.uvue
├── pagesA/                 # 分包 A
│   └── list/
│       └── list.uvue
├── pagesB/                 # 分包 B
│   └── setting/
│       └── setting.uvue
├── components/             # 公共组件
│   ├── base/               # 基础组件
│   │   └── card.uvue
│   └── business/           # 业务组件
│       └── user-card.uvue
├── composables/            # 组合式函数
│   └── useAuth.uts
├── stores/                 # Pinia 状态管理
│   └── user.uts
├── api/                    # API 接口
│   ├── request.uts         # 请求封装
│   └── modules/            # 按模块拆分
│       ├── user.uts
│       └── order.uts
├── utils/                  # 工具函数
│   ├── common.uts          # 业务工具
│   └── util.uts            # 通用工具
├── static/                 # 静态资源
│   └── images/
├── uni_modules/            # uni 插件
├── App.uvue                # 应用入口
├── main.uts                # 入口文件
├── pages.json              # 页面配置
├── manifest.json           # 应用配置
└── uni.scss                # 全局样式变量

分包策略

pages.json 配置

{
  "pages": [
    {
      "path": "pages/index/index",
      "style": { "navigationBarTitleText": "首页" }
    },
    {
      "path": "pages/user/profile",
      "style": { "navigationBarTitleText": "我的" }
    }
  ],
  "subPackages": [
    {
      "root": "pagesA",
      "name": "moduleA",
      "pages": [
        { "path": "list/list" },
        { "path": "detail/detail" }
      ]
    },
    {
      "root": "pagesB",
      "name": "moduleB",
      "pages": [
        { "path": "setting/setting" }
      ]
    }
  ],
  "preloadRule": {
    "pages/index/index": {
      "network": "all",
      "packages": ["moduleA"]
    }
  }
}

分包原则

规则 说明
主包精简 仅放 TabBar 页面和公共资源,体积 < 2MB
按功能分包 相关页面放同一分包
独立分包 不依赖主包内容的页面可设为独立分包
预加载 使用 preloadRule 提升用户体验

分包体积限制

平台 限制
微信小程序 整包 ≤ 20MB,单分包 ≤ 2MB
支付宝小程序 整包 ≤ 20MB,单分包 ≤ 2MB
抖音小程序 整包 ≤ 16MB,单分包 ≤ 2MB

命名规范

类型 规范 示例
页面目录 小写 + 连字符 user-profile/
组件文件 小写 + 连字符 user-card.uvue
变量/函数 camelCase getUserInfo
常量 UPPER_SNAKE API_BASE_URL
类型/接口 PascalCase UserInfo
组合式函数 use 前缀 useAuth

状态管理 (Pinia)

定义 Store

// stores/user.uts
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', () => {
  const token = ref<string>('')
  const userInfo = ref<UserInfo | null>(null)

  const isLoggedIn = computed(() => !!token.value)

  function setToken(newToken: string) {
    token.value = newToken
  }

  function logout() {
    token.value = ''
    userInfo.value = null
    // 清除本地存储
    uni.removeStorageSync('token')
  }

  // 初始化时从本地存储恢复
  function init() {
    const savedToken = uni.getStorageSync('token')
    if (savedToken) {
      token.value = savedToken
    }
  }

  return { token, userInfo, isLoggedIn, setToken, logout, init }
})

使用 Store

<script setup lang="uts">
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()
const { userInfo, isLoggedIn } = storeToRefs(userStore)
const { logout, init } = userStore

// 页面加载时初始化
onLoad(() => {
  init()
})
</script>

网络请求封装

请求工具

// api/request.uts
interface RequestOptions {
  url: string
  method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
  data?: any
  showLoading?: boolean
  loadingText?: string
}

interface ApiResponse<T> {
  code: number
  message: string
  data: T
}

const API_BASE_URL = 'https://api.example.com'

export async function request<T>(options: RequestOptions): Promise<ApiResponse<T>> {
  const { url, method = 'GET', data, showLoading = true, loadingText = '加载中' } = options

  if (showLoading) {
    uni.showLoading({ title: loadingText, mask: true })
  }

  return new Promise((resolve, reject) => {
    uni.request({
      url: API_BASE_URL + url,
      method,
      data,
      header: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${uni.getStorageSync('token')}`
      },
      success: (res) => {
        const result = res.data as ApiResponse<T>
        if (result.code === 0) {
          resolve(result)
        } else if (result.code === 401) {
          // Token 过期,跳转登录
          uni.navigateTo({ url: '/pages/login/login' })
          reject(result)
        } else {
          uni.showToast({ title: result.message, icon: 'none' })
          reject(result)
        }
      },
      fail: (err) => {
        uni.showToast({ title: '网络错误', icon: 'none' })
        reject(err)
      },
      complete: () => {
        if (showLoading) uni.hideLoading()
      }
    })
  })
}

API 模块化

// api/modules/user.uts
import { request, type ApiResponse } from '../request'

export interface UserInfo {
  id: number
  name: string
  avatar: string
}

export const userApi = {
  getUser: (id: number): Promise<ApiResponse<UserInfo>> => request({
    url: `/user/${id}`
  }),

  updateUser: (data: Partial<UserInfo>): Promise<ApiResponse<UserInfo>> => request({
    url: '/user',
    method: 'PUT',
    data
  })
}

页面生命周期

<script setup lang="uts">
// 页面加载
onLoad((options) => {
  console.log('页面参数:', options)
})

// 页面显示
onShow(() => {
  console.log('页面显示')
})

// 页面隐藏
onHide(() => {
  console.log('页面隐藏')
})

// 页面卸载
onUnload(() => {
  console.log('页面卸载')
})

// 下拉刷新
onPullDownRefresh(() => {
  // 刷新数据
  uni.stopPullDownRefresh()
})

// 上拉加载
onReachBottom(() => {
  // 加载更多
})
</script>

样式规范

rpx 响应式单位

/* 使用 rpx 适配不同屏幕(设计稿基准 750) */
.container {
  width: 750rpx;      /* 满屏宽度 */
  padding: 32rpx;
  font-size: 28rpx;   /* 正文大小 */
}

全局样式变量 (uni.scss)

/* uni.scss */
$uni-color-primary: #07c160;    /* 微信绿 */
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;

$uni-spacing-sm: 16rpx;
$uni-spacing-md: 32rpx;
$uni-spacing-lg: 48rpx;

/* 文字颜色 */
$uni-text-color: #333;
$uni-text-color-secondary: #666;
$uni-text-color-placeholder: #999;

条件编译

/* #ifdef MP-WEIXIN */
.container { background-color: #07c160; }
/* #endif */

/* #ifdef MP-ALIPAY */
.container { background-color: #1677ff; }
/* #endif */

性能优化

策略 说明
setData 精简 只传需要更新的数据,避免传大对象
图片懒加载 使用 lazy-load 属性
分包预加载 配置 preloadRule
长列表虚拟化 使用 list + cell 组件
避免频繁 setData 合并多次更新为一次
组件按需加载 使用 easycom 自动导入

长列表优化

<template>
  <list class="list" :enable-back-to-top="true">
    <cell v-for="item in list" :key="item.id">
      <item-card :item="item" />
    </cell>
    <!-- 加载更多 -->
    <cell>
      <view class="loading" v-if="loading">加载中...</view>
      <view class="no-more" v-else-if="noMore">没有更多了</view>
    </cell>
  </list>
</template>

条件编译

平台判断

// #ifdef MP-WEIXIN
console.log('微信小程序')
// #endif

// #ifdef MP-ALIPAY
console.log('支付宝小程序')
// #endif

// #ifdef MP-TOUTIAO
console.log('抖音小程序')
// #endif

平台差异化功能

<template>
  <view>
    <!-- #ifdef MP-WEIXIN -->
    <button open-type="getPhoneNumber" @getphonenumber="onGetPhone">
      获取手机号
    </button>
    <!-- #endif -->

    <!-- #ifdef MP-ALIPAY -->
    <button open-type="getAuthorize" @getAuthorize="onGetAuth">
      授权登录
    </button>
    <!-- #endif -->
  </view>
</template>

开发环境要求

项目 要求
HBuilderX 最新正式版
微信开发者工具 最新稳定版
Node.js ≥ 18(CLI 模式需要)
内存 最低 8GB,推荐 16GB

常见陷阱

问题 原因 解法
分包体积超限 单分包 > 2MB 拆分或使用独立分包
首屏白屏 主包过大 精简主包,资源放分包
setData 卡顿 传大对象 只传需要的字段
样式不生效 使用了不支持的 CSS 查阅 uni-app x 支持的样式
图片不显示 路径错误或未配置域名 检查路径、配置合法域名
编译报错 uts 语法问题 检查类型定义

调试技巧

场景 方法
微信小程序 微信开发者工具预览
支付宝小程序 支付宝开发者工具
抖音小程序 字节开发者工具
网络请求 开发者工具 Network 面板
性能分析 开发者工具 Audits

沉淀协议

场景 行动
组件复用 ≥ 2 次 提取到 components/base/
API 调用模式重复 ≥ 3 次 封装到 api/modules/
状态逻辑重复 ≥ 2 次 提取到 stores/composables/
平台差异处理固化 更新本文档条件编译章节