第19集:uni-app 大型应用架构

章节概览

在本章节中,我们将学习如何构建 uni-app 大型应用架构。随着应用规模的不断扩大,良好的架构设计变得越来越重要。通过本章节的学习,您将掌握状态管理、路由管理、模块化开发等核心知识点,并能够构建可维护、可扩展的大型应用架构。

核心知识点

1. 状态管理

状态管理是大型应用架构中的核心部分,用于管理应用的全局状态。uni-app 中常用的状态管理方案包括:

  • Vuex:Vue 官方推荐的状态管理库,适用于中大型应用
  • Pinia:Vue 3 官方推荐的状态管理库,提供了更简洁的 API
  • 简单状态管理:适用于小型应用的简单状态管理方案

2. 路由管理

路由管理负责应用的页面导航和页面状态管理,包括:

  • 路由配置:定义应用的页面路由结构
  • 导航守卫:控制页面导航的权限和行为
  • 路由参数:处理页面间的参数传递
  • 路由元信息:存储路由相关的额外信息

3. 模块化开发

模块化开发是大型应用架构的重要组成部分,包括:

  • 组件模块化:将 UI 拆分为可复用的组件
  • 业务逻辑模块化:将业务逻辑拆分为独立的模块
  • 工具函数模块化:将通用工具函数封装为独立的模块
  • API 模块化:将 API 请求封装为独立的模块

4. 目录结构设计

合理的目录结构设计有助于提高代码的可维护性和可读性,包括:

  • 页面目录:存放应用的页面组件
  • 组件目录:存放可复用的组件
  • store 目录:存放状态管理相关代码
  • utils 目录:存放工具函数
  • api 目录:存放 API 请求相关代码
  • config 目录:存放配置文件
  • styles 目录:存放全局样式

实用案例

构建大型电商应用架构

我们将构建一个大型电商应用的架构,包括:

  1. 状态管理设计:用户状态、商品状态、购物车状态等
  2. 路由管理设计:权限控制、页面导航等
  3. 模块化设计:组件、业务逻辑、API 等模块化
  4. 目录结构设计:合理的目录结构组织

代码示例

1. 状态管理设计

使用 Vuex 进行状态管理

// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import goods from './modules/goods'
import cart from './modules/cart'
import order from './modules/order'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // 全局状态
    loading: false,
    error: null
  },
  mutations: {
    // 设置加载状态
    setLoading(state, status) {
      state.loading = status
    },
    // 设置错误信息
    setError(state, error) {
      state.error = error
    }
  },
  actions: {
    // 重置错误信息
    resetError({ commit }) {
      commit('setError', null)
    }
  },
  getters: {
    // 获取加载状态
    isLoading: state => state.loading,
    // 获取错误信息
    error: state => state.error
  },
  modules: {
    user,
    goods,
    cart,
    order
  }
})

用户状态模块

// store/modules/user.js
export default {
  namespaced: true,
  state: {
    userInfo: null,
    token: null,
    isLoggedIn: false
  },
  mutations: {
    // 设置用户信息
    setUserInfo(state, userInfo) {
      state.userInfo = userInfo
      state.isLoggedIn = true
    },
    // 设置 token
    setToken(state, token) {
      state.token = token
    },
    // 清除用户信息
    clearUserInfo(state) {
      state.userInfo = null
      state.token = null
      state.isLoggedIn = false
    }
  },
  actions: {
    // 登录
    async login({ commit }, loginData) {
      try {
        // 调用登录 API
        const response = await uniCloud.callFunction({
          name: 'login',
          data: loginData
        })
        
        if (response.result.code === 200) {
          // 保存用户信息和 token
          commit('setUserInfo', response.result.data.user)
          commit('setToken', response.result.data.token)
          
          // 存储到本地存储
          uni.setStorageSync('userInfo', response.result.data.user)
          uni.setStorageSync('token', response.result.data.token)
          
          return true
        } else {
          throw new Error(response.result.message)
        }
      } catch (error) {
        console.error('登录失败:', error)
        throw error
      }
    },
    // 登出
    logout({ commit }) {
      // 清除用户信息
      commit('clearUserInfo')
      
      // 清除本地存储
      uni.removeStorageSync('userInfo')
      uni.removeStorageSync('token')
    },
    // 初始化用户状态
    initUserState({ commit }) {
      // 从本地存储读取用户信息
      const userInfo = uni.getStorageSync('userInfo')
      const token = uni.getStorageSync('token')
      
      if (userInfo && token) {
        commit('setUserInfo', userInfo)
        commit('setToken', token)
      }
    }
  },
  getters: {
    // 获取用户信息
    userInfo: state => state.userInfo,
    // 获取 token
    token: state => state.token,
    // 是否登录
    isLoggedIn: state => state.isLoggedIn
  }
}

商品状态模块

// store/modules/goods.js
export default {
  namespaced: true,
  state: {
    goodsList: [],
    goodsDetail: null,
    currentPage: 1,
    pageSize: 10,
    total: 0,
    loading: false
  },
  mutations: {
    // 设置商品列表
    setGoodsList(state, goodsList) {
      state.goodsList = goodsList
    },
    // 添加商品列表(用于分页加载)
    addGoodsList(state, goodsList) {
      state.goodsList = [...state.goodsList, ...goodsList]
    },
    // 设置商品详情
    setGoodsDetail(state, goodsDetail) {
      state.goodsDetail = goodsDetail
    },
    // 设置当前页码
    setCurrentPage(state, currentPage) {
      state.currentPage = currentPage
    },
    // 设置总条数
    setTotal(state, total) {
      state.total = total
    },
    // 设置加载状态
    setLoading(state, loading) {
      state.loading = loading
    }
  },
  actions: {
    // 获取商品列表
    async getGoodsList({ commit, state }, params = {}) {
      try {
        commit('setLoading', true)
        
        const response = await uniCloud.callFunction({
          name: 'getGoodsList',
          data: {
            page: params.page || state.currentPage,
            pageSize: params.pageSize || state.pageSize,
            ...params
          }
        })
        
        if (response.result.code === 200) {
          if (params.page === 1 || !params.page) {
            commit('setGoodsList', response.result.data.list)
          } else {
            commit('addGoodsList', response.result.data.list)
          }
          commit('setCurrentPage', params.page || state.currentPage)
          commit('setTotal', response.result.data.total)
          return response.result.data
        } else {
          throw new Error(response.result.message)
        }
      } catch (error) {
        console.error('获取商品列表失败:', error)
        throw error
      } finally {
        commit('setLoading', false)
      }
    },
    // 获取商品详情
    async getGoodsDetail({ commit }, goodsId) {
      try {
        commit('setLoading', true)
        
        const response = await uniCloud.callFunction({
          name: 'getGoodsDetail',
          data: { goodsId }
        })
        
        if (response.result.code === 200) {
          commit('setGoodsDetail', response.result.data)
          return response.result.data
        } else {
          throw new Error(response.result.message)
        }
      } catch (error) {
        console.error('获取商品详情失败:', error)
        throw error
      } finally {
        commit('setLoading', false)
      }
    }
  },
  getters: {
    // 获取商品列表
    goodsList: state => state.goodsList,
    // 获取商品详情
    goodsDetail: state => state.goodsDetail,
    // 是否还有更多商品
    hasMore: state => state.goodsList.length < state.total,
    // 加载状态
    loading: state => state.loading
  }
}

2. 路由管理设计

路由配置

// pages.json
{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页"
      }
    },
    {
      "path": "pages/login/login",
      "style": {
        "navigationBarTitleText": "登录"
      }
    },
    {
      "path": "pages/register/register",
      "style": {
        "navigationBarTitleText": "注册"
      }
    },
    {
      "path": "pages/goods/list",
      "style": {
        "navigationBarTitleText": "商品列表"
      }
    },
    {
      "path": "pages/goods/detail",
      "style": {
        "navigationBarTitleText": "商品详情"
      }
    },
    {
      "path": "pages/cart/index",
      "style": {
        "navigationBarTitleText": "购物车"
      }
    },
    {
      "path": "pages/order/list",
      "style": {
        "navigationBarTitleText": "订单列表"
      }
    },
    {
      "path": "pages/order/detail",
      "style": {
        "navigationBarTitleText": "订单详情"
      }
    },
    {
      "path": "pages/user/index",
      "style": {
        "navigationBarTitleText": "个人中心"
      }
    }
  ],
  "subPackages": [
    {
      "root": "pages/admin",
      "pages": [
        {
          "path": "index/index",
          "style": {
            "navigationBarTitleText": "管理后台"
          }
        },
        {
          "path": "goods/manage",
          "style": {
            "navigationBarTitleText": "商品管理"
          }
        },
        {
          "path": "order/manage",
          "style": {
            "navigationBarTitleText": "订单管理"
          }
        }
      ]
    }
  ],
  "tabBar": {
    "color": "#999",
    "selectedColor": "#007aff",
    "borderStyle": "black",
    "backgroundColor": "#fff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "static/tabbar/home.png",
        "selectedIconPath": "static/tabbar/home-active.png"
      },
      {
        "pagePath": "pages/goods/list",
        "text": "商品",
        "iconPath": "static/tabbar/goods.png",
        "selectedIconPath": "static/tabbar/goods-active.png"
      },
      {
        "pagePath": "pages/cart/index",
        "text": "购物车",
        "iconPath": "static/tabbar/cart.png",
        "selectedIconPath": "static/tabbar/cart-active.png"
      },
      {
        "pagePath": "pages/user/index",
        "text": "我的",
        "iconPath": "static/tabbar/user.png",
        "selectedIconPath": "static/tabbar/user-active.png"
      }
    ]
  },
  "globalStyle": {
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "uni-app 电商",
    "navigationBarBackgroundColor": "#fff",
    "backgroundColor": "#f5f5f5"
  }
}

导航守卫

// utils/routeGuard.js
import store from '@/store'

// 白名单,无需登录即可访问的页面
const whiteList = ['/pages/login/login', '/pages/register/register', '/pages/index/index', '/pages/goods/list', '/pages/goods/detail']

// 路由守卫
export function routeGuard() {
  // 监听页面加载
  uni.addInterceptor('navigateTo', {
    invoke: (args) => {
      // 检查是否需要登录
      if (!whiteList.includes(args.url) && !store.state.user.isLoggedIn) {
        uni.navigateTo({
          url: '/pages/login/login'
        })
        return false
      }
      return true
    }
  })
  
  // 监听页面重定向
  uni.addInterceptor('redirectTo', {
    invoke: (args) => {
      // 检查是否需要登录
      if (!whiteList.includes(args.url) && !store.state.user.isLoggedIn) {
        uni.navigateTo({
          url: '/pages/login/login'
        })
        return false
      }
      return true
    }
  })
  
  // 监听页面切换
  uni.addInterceptor('switchTab', {
    invoke: (args) => {
      // 检查是否需要登录(这里可以根据实际需求调整)
      return true
    }
  })
}

3. 模块化开发

API 模块化

// api/index.js
import request from './request'

export const userApi = {
  // 登录
  login: (data) => request('login', data),
  // 注册
  register: (data) => request('register', data),
  // 获取用户信息
  getUserInfo: (data) => request('getUserInfo', data),
  // 更新用户信息
  updateUserInfo: (data) => request('updateUserInfo', data)
}

export const goodsApi = {
  // 获取商品列表
  getGoodsList: (data) => request('getGoodsList', data),
  // 获取商品详情
  getGoodsDetail: (data) => request('getGoodsDetail', data),
  // 搜索商品
  searchGoods: (data) => request('searchGoods', data)
}

export const cartApi = {
  // 获取购物车列表
  getCartList: (data) => request('getCartList', data),
  // 添加到购物车
  addToCart: (data) => request('addToCart', data),
  // 更新购物车商品
  updateCartItem: (data) => request('updateCartItem', data),
  // 删除购物车商品
  deleteCartItem: (data) => request('deleteCartItem', data)
}

export const orderApi = {
  // 创建订单
  createOrder: (data) => request('createOrder', data),
  // 获取订单列表
  getOrderList: (data) => request('getOrderList', data),
  // 获取订单详情
  getOrderDetail: (data) => request('getOrderDetail', data),
  // 更新订单状态
  updateOrderStatus: (data) => request('updateOrderStatus', data)
}

请求封装

// api/request.js
import store from '@/store'

// 请求封装
export default async function request(name, data = {}) {
  try {
    // 显示加载状态
    store.commit('setLoading', true)
    
    // 调用云函数
    const response = await uniCloud.callFunction({
      name,
      data: {
        // 添加 token 等公共参数
        token: store.state.user.token,
        ...data
      }
    })
    
    // 处理响应
    if (response.result.code === 200) {
      return response.result.data
    } else if (response.result.code === 401) {
      // 未登录或 token 过期
      store.dispatch('user/logout')
      uni.navigateTo({
        url: '/pages/login/login'
      })
      throw new Error('请重新登录')
    } else {
      throw new Error(response.result.message || '请求失败')
    }
  } catch (error) {
    console.error('请求失败:', error)
    throw error
  } finally {
    // 隐藏加载状态
    store.commit('setLoading', false)
  }
}

工具函数模块化

// utils/index.js
export * from './storage'
export * from './validate'
export * from './format'
export * from './request'
export * from './routeGuard'
// utils/storage.js
// 存储工具函数

export const storage = {
  // 设置存储
  set(key, value) {
    uni.setStorageSync(key, value)
  },
  // 获取存储
  get(key, defaultValue = null) {
    try {
      const value = uni.getStorageSync(key)
      return value !== undefined ? value : defaultValue
    } catch (error) {
      console.error('获取存储失败:', error)
      return defaultValue
    }
  },
  // 删除存储
  remove(key) {
    uni.removeStorageSync(key)
  },
  // 清空存储
  clear() {
    uni.clearStorageSync()
  }
}

4. 目录结构设计

src/
├── api/             # API 模块化
│   ├── index.js     # API 导出
│   └── request.js   # 请求封装
├── components/      # 公共组件
│   ├── goods-card/  # 商品卡片组件
│   ├── form-item/   # 表单项目组件
│   └── modal/       # 模态框组件
├── pages/           # 页面
│   ├── index/       # 首页
│   ├── login/       # 登录页
│   ├── register/    # 注册页
│   ├── goods/       # 商品相关页面
│   ├── cart/        # 购物车页面
│   ├── order/       # 订单相关页面
│   └── user/        # 用户中心页面
├── store/           # 状态管理
│   ├── index.js     # 状态管理入口
│   └── modules/     # 状态管理模块
├── styles/          # 样式
│   ├── common.css   # 公共样式
│   └── variables.css # 样式变量
├── utils/           # 工具函数
│   ├── index.js     # 工具函数导出
│   ├── storage.js   # 存储工具
│   ├── validate.js  # 验证工具
│   └── format.js    # 格式化工具
├── App.vue          # 应用入口组件
├── main.js          # 应用入口文件
└── pages.json       # 页面配置文件

常见问题与解决方案

1. 状态管理复杂度

问题:应用状态管理变得越来越复杂,难以维护

解决方案

  • 合理划分状态管理模块,每个模块负责特定的业务领域
  • 使用命名空间(namespaced)避免状态冲突
  • 遵循单向数据流原则,保持状态管理的清晰性
  • 定期清理不需要的状态,避免状态冗余

2. 路由管理混乱

问题:应用路由管理混乱,权限控制复杂

解决方案

  • 合理设计路由结构,使用子包(subPackages)优化打包体积
  • 使用导航守卫统一处理权限控制
  • 定义路由常量,避免硬编码路由路径
  • 合理使用路由元信息,存储路由相关的额外信息

3. 模块化开发不规范

问题:模块化开发不规范,代码重复度高

解决方案

  • 制定模块化开发规范,统一代码风格
  • 合理划分模块边界,避免模块间过度耦合
  • 使用工具函数封装通用逻辑,减少代码重复
  • 定期重构代码,优化模块结构

学习总结

通过本章节的学习,您已经掌握了以下内容:

  1. 状态管理:学会了使用 Vuex 进行状态管理,包括模块划分、 mutations、actions 和 getters 的使用
  2. 路由管理:掌握了路由配置、导航守卫、路由参数和路由元信息的使用
  3. 模块化开发:学会了 API 模块化、工具函数模块化等模块化开发方法
  4. 目录结构设计:掌握了合理的目录结构设计方法,提高代码的可维护性
  5. 实战应用:构建了一个大型电商应用的架构设计

良好的架构设计是大型应用成功的关键。通过合理的状态管理、路由管理和模块化开发,您可以构建出可维护、可扩展的大型应用架构。在实际项目中,您应该根据项目的具体需求和规模,选择合适的架构方案,并不断优化和调整。

在后续的学习中,我们将继续探讨 uni-app 的最佳实践,帮助您进一步提升应用开发能力。

« 上一篇 uni-app 云开发 下一篇 » uni-app 最佳实践