UniApp 项目从 0 到 1 搭建工程化架构的最佳实践

UniApp 作为跨端开发框架,凭借“一次编写,多端运行”的能力,成为众多团队构建小程序、H5、App 的首选方案。然而,当项目从 Demo 走向生产,缺乏工程化约束的代码会迅速腐化:目录混乱、状态管理失控、请求层重复、构建流程低效。本文将从 0 到 1 梳理一套可落地的 UniApp 工程化架构,覆盖目录设计、状态管理、请求封装、组件规范、构建优化与代码质量六个维度。

一、目录结构:按职责分层,而非按文件类型

许多开发者习惯将文件按类型堆放——所有页面在 pages,所有组件在 components,所有 API 在 api。项目变大后,修改一个功能需要跨五个目录跳转。更合理的做法是按业务模块划分,内部再按职责分层

src/
├── pages/                # 主包页面(仅保留 TabBar 和核心页面)
├── subPackages/          # 分包目录,按业务模块拆分
│   ├── order/
│   ├── user/
│   └── activity/
├── components/           # 全局公共组件
├── hooks/                # 组合式函数
├── store/                # 状态管理
├── api/                  # 接口定义层
├── utils/                # 工具函数
├── static/               # 静态资源
├── styles/               # 全局样式与变量
└── config/               # 环境配置与常量

分包策略是 UniApp 工程化的关键。微信小程序对主包体积有 2MB 限制,建议将非核心业务全部放入 subPackages,并在 pages.json 中配置 preloadRule 实现分包预下载,兼顾体积与体验。

二、状态管理:Pinia 替代 Vuex

UniApp 从 Vue2 到 Vue3 的迁移中,状态管理推荐使用 Pinia。相比 Vuex,Pinia 去掉了 Mutation,TypeScript 支持更友好,且天然支持组合式 API。

// store/modules/user.js
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    token: uni.getStorageSync('token') || '',
    userInfo: null
  }),
  getters: {
    isLogin: (state) => !!state.token
  },
  actions: {
    async login(credentials) {
      const { data } = await api.login(credentials)
      this.token = data.token
      this.userInfo = data.userInfo
      uni.setStorageSync('token', data.token)
    },
    logout() {
      this.token = ''
      this.userInfo = null
      uni.removeStorageSync('token')
    }
  }
})

关键实践:按业务域拆分 Store 文件,避免单一巨型 Store;在 main.js 中全局注册 Pinia 实例;对于需要持久化的状态(如 token),在 action 中同步写入 Storage,而非依赖插件自动持久化,以便精确控制写入时机。

三、请求层:统一拦截与类型安全

直接调用 uni.request 会导致每个页面重复处理 loading、错误提示和 token 刷新。建议封装一个请求类,统一处理:

// utils/request.js
class Request {
  constructor(baseURL) {
    this.baseURL = baseURL
    this.interceptors = { request: [], response: [] }
  }

  async request(options) {
    // 请求拦截
    for (const fn of this.interceptors.request) {
      options = await fn(options)
    }

    return new Promise((resolve, reject) => {
      uni.request({
        url: this.baseURL + options.url,
        method: options.method || 'GET',
        data: options.data,
        header: { 'Content-Type': 'application/json', ...options.header },
        success: async (res) => {
          // 响应拦截
          for (const fn of this.interceptors.response) {
            res = await fn(res)
          }
          if (res.statusCode === 200) {
            resolve(res.data)
          } else {
            uni.showToast({ title: res.data.message || '请求失败', icon: 'none' })
            reject(res)
          }
        },
        fail: (err) => {
          uni.showToast({ title: '网络异常', icon: 'none' })
          reject(err)
        }
      })
    })
  }
}

配合 api/ 目录按模块定义接口函数,实现请求逻辑与业务逻辑解耦。例如 api/user.js 导出 logingetProfile 等函数,页面只关心调用与数据消费。

四、组件规范:EasyCom 与原子化设计

UniApp 的 EasyCom 机制允许组件自动按需引入,无需手动 import。在 pages.json 中配置:

{
  "easycom": {
    "autoscan": true,
    "custom": {
      "^u-(.*)": "@/components/u-$1/u-$1.vue",
      "^my-(.*)": "@/components/my-$1/my-$1.vue"
    }
  }
}

建议将组件分为三层:

  • 基础组件:按钮、输入框、弹窗等,与业务无关,可跨项目复用。
  • 业务组件:如订单卡片、用户头像组,包含特定业务逻辑。
  • 页面组件:仅在某页面使用的局部组件,放在页面同级 components 目录。

命名约定:基础组件以 base- 或 UI 库前缀命名,业务组件以模块名开头(如 order-card),避免命名冲突。

五、构建优化:条件编译与体积控制

UniApp 的条件编译是跨端适配的利器,但滥用会导致代码可读性下降。建议将平台差异收敛到配置文件或工具函数中,而非散落在业务代码里:

// utils/platform.js
export const isMP = process.env.UNI_PLATFORM === 'mp-weixin'
export const isH5 = process.env.UNI_PLATFORM === 'h5'

export function setNavigationBar(title) {
  // #ifdef MP-WEIXIN
  uni.setNavigationBarTitle({ title })
  // #endif
  // #ifdef H5
  document.title = title
  // #endif
}

体积控制方面:开启 uni-app--minimize 压缩;使用 webpack-bundle-analyzer 分析依赖;将大图上传 CDN 而非打包进 static;对非首屏页面使用分包异步化。

六、代码质量:Lint 与 Git Hooks

工程化离不开自动化约束。推荐配置:

  • ESLint + Prettier:统一代码风格,@vue/eslint-config-standard 配合 UniApp 全局变量声明。
  • Stylelint:约束 SCSS/CSS 书写规范。
  • Husky + lint-staged:在 pre-commit 阶段对暂存文件执行 lint 和格式化,防止脏代码进入仓库。
  • Commitlint:规范提交信息,便于生成 CHANGELOG。
// package.json
{
  "lint-staged": {
    "*.{js,vue}": ["eslint --fix", "prettier --write"],
    "*.{scss,css}": ["stylelint --fix"]
  }
}

总结

UniApp 工程化不是一次性配置,而是持续迭代的约束体系。从目录分层明确代码归属,用 Pinia 管理共享状态,以请求封装隔离网络细节,借 EasyCom 提升组件复用,靠条件编译收敛处理平台差异,最后用 Lint 工具链守住质量底线。这套架构在多个中大型项目中验证可行,能将协作效率提升 40% 以上。建议团队根据自身规模裁剪,先落地请求层与目录规范,再逐步引入其余模块。

未经允许不得转载:任鹏个人博客 » UniApp 项目从 0 到 1 搭建工程化架构的最佳实践

赞 (0) 打赏

评论 0

取消
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏