1. 从零搭建Vue3+uniapp项目基础框架
第一次接触uniapp+Vue3的组合时,我花了两天时间才搞明白如何正确初始化项目。现在回想起来,其实只需要5分钟就能完成基础搭建。打开HBuilderX,选择"新建项目",在模板选择界面务必勾选Vue3版本。这里有个坑要注意:如果选择了Vue2模板,后期再想升级到Vue3会非常麻烦。
创建完成后,项目结构会自动生成这些核心文件:
- pages.json - 页面路由配置文件
- manifest.json - 应用配置
- App.vue - 根组件
- main.js - 应用入口
我建议立即在项目根目录下创建两个重要文件夹:
- store - 存放Pinia状态管理文件
- components - 存放公共组件
在main.js中,基础的Vue3应用初始化代码是这样的:
import { createSSRApp } from 'vue' import App from './App.vue' export function createApp() { const app = createSSRApp(App) return { app } }2. uview-plus的完整集成指南
uview-plus是目前uniapp生态中最受欢迎的UI组件库之一,但它的完整引入需要多个步骤配合。我在三个实际项目中总结出最稳定的集成方案:
首先通过HBuilderX的插件市场安装uview-plus。这里有个小技巧:安装完成后不要立即关闭插件市场,先检查是否出现了"导入示例项目"的选项。如果有,强烈建议导入,里面包含各种组件的用法示例。
接下来是关键的三个配置步骤:
2.1 主JS文件配置
在main.js中添加以下代码:
import uviewPlus from '@/uni_modules/uview-plus' app.use(uviewPlus)2.2 SCSS主题配置
在uni.scss文件中引入主题样式:
@import '@/uni_modules/uview-plus/theme.scss';2.3 基础样式引入
在App.vue的style标签中引入基础样式(必须添加lang="scss"属性):
<style lang="scss"> @import "@/uni_modules/uview-plus/index.scss"; </style>我遇到过的一个典型问题是样式不生效,后来发现是因为忘记在style标签中添加lang="scss"。uview-plus的组件使用起来非常简单,比如要使用按钮组件:
<u-button type="primary" icon="share" text="分享" @click="handleShare"> </u-button>3. Pinia状态管理深度整合
Pinia作为Vue3官方推荐的状态管理工具,在uniapp中的使用有些特殊注意事项。首先在main.js中需要进行特殊配置:
import { createPinia } from 'pinia' const pinia = createPinia() app.use(pinia) return { app, pinia // 必须返回pinia实例 }3.1 模块化Store设计
对于电商类应用,我建议按业务模块划分store。比如创建store/user.js管理用户状态:
import { defineStore } from 'pinia' export const useUserStore = defineStore('user', { state: () => ({ token: '', userInfo: null, cartCount: 0 }), actions: { async login(credentials) { // 登录逻辑 }, updateCartCount(count) { this.cartCount = count } } })3.2 跨页面状态共享
在页面中使用store时,直接导入对应store即可:
<script setup> import { useUserStore } from '@/store/user' const userStore = useUserStore() </script>Pinia的一个巨大优势是响应式系统。在任何组件中修改状态,所有依赖该状态的组件都会自动更新。比如购物车数量变化时,TabBar上的角标会自动同步。
4. 性能优化与最佳实践
经过多个项目的实战,我总结了以下几个关键优化点:
4.1 组件按需加载
虽然uview-plus支持全局导入,但对于大型项目建议按需引入:
// 在特定页面中 import { UButton, UIcon } from '@/uni_modules/uview-plus/components'4.2 Pinia持久化存储
安装pinia-plugin-persistedstate插件实现状态持久化:
import { createPersistedState } from 'pinia-plugin-persistedstate' const pinia = createPinia() pinia.use(createPersistedState({ storage: { getItem(key) { return uni.getStorageSync(key) }, setItem(key, value) { uni.setStorageSync(key, value) } } }))4.3 跨端兼容处理
在uni.scss中添加媒体查询适配不同平台:
/* 小程序特有样式 */ @media screen and (max-width: 480px) { .container { padding: 10px; } }对于API调用,使用uni的跨端API:
// 而不是window.localStorage uni.setStorageSync('token', 'xxx')5. 电商项目实战案例
以一个商品详情页为例,展示uview-plus和Pinia的协同工作:
5.1 页面布局结构
<template> <view class="container"> <u-swiper :list="product.images" /> <u-gap height="20" /> <u-cell-group> <u-cell title="价格" :value="`¥${product.price}`" /> <u-cell title="库存" :value="product.stock" /> </u-cell-group> <u-button type="error" text="加入购物车" @click="addToCart" /> </view> </template>5.2 状态管理逻辑
import { useProductStore } from '@/store/product' import { useCartStore } from '@/store/cart' const productStore = useProductStore() const cartStore = useCartStore() const addToCart = () => { cartStore.addItem({ id: productStore.currentProduct.id, quantity: 1 }) uni.showToast({ title: '添加成功' }) }5.3 全局状态响应
在TabBar组件中自动更新购物车数量:
<u-badge :value="cartStore.totalCount" :offset="[10, 10]"> <u-icon name="shopping-cart" size="28" /> </u-badge>6. 常见问题解决方案
在实际开发中,我遇到最多的问题是样式冲突。uview-plus的样式有时会被其他样式覆盖,解决方案是在App.vue中确保uview-plus的样式最后引入:
<style lang="scss"> /* 其他样式 */ @import "@/common/style/base.scss"; /* uview-plus样式最后引入 */ @import "@/uni_modules/uview-plus/index.scss"; </style>另一个常见问题是Pinia在页面刷新后状态丢失。除了使用持久化插件外,还可以在App.vue的onLaunch钩子中初始化关键状态:
onLaunch(() => { const userStore = useUserStore() userStore.loadFromStorage() })对于跨端兼容性问题,建议使用uni提供的条件编译:
// #ifdef MP-WEIXIN // 微信小程序特有逻辑 // #endif7. 项目架构优化建议
经过多个项目的实践,我总结出一套高效的目录结构:
src/ ├── components/ // 公共组件 ├── store/ // Pinia store │ ├── modules/ // 模块化store │ └── index.js // store聚合 ├── static/ // 静态资源 ├── pages/ // 页面 ├── services/ // API服务 └── utils/ // 工具函数在store/index.js中集中导出所有store模块:
export * from './modules/user' export * from './modules/product' export * from './modules/cart'这样在使用时可以统一导入:
import { useUserStore, useCartStore } from '@/store'对于大型项目,建议将uview-plus组件封装为业务组件。比如封装一个AppButton:
<template> <u-button :custom-style="customStyle" v-bind="$attrs"> <slot /> </u-button> </template> <script setup> defineProps({ customStyle: { type: Object, default: () => ({}) } }) </script>8. 调试与错误处理
在开发过程中,我强烈推荐使用Pinia的调试工具。在HBuilderX中配置:
const pinia = createPinia() pinia.use(({ store }) => { store.$onAction(({ name, after }) => { after(() => { console.log(`Action ${name} completed`) }) }) })对于uview-plus组件的问题,可以通过以下方式调试:
- 检查组件是否正确定义
- 查看控制台是否有警告
- 检查样式是否被正确加载
一个典型的错误是忘记在pages.json中注册组件:
{ "easycom": { "^u-(.*)": "@/uni_modules/uview-plus/components/u-$1/u-$1.vue" } }在处理异步操作时,建议使用async/await配合错误处理:
const fetchData = async () => { try { const data = await productApi.getList() productStore.setList(data) } catch (error) { uni.showToast({ title: '加载失败', icon: 'error' }) } }9. 构建与部署优化
在项目打包时,有几个关键配置需要注意:
在manifest.json中配置:
{ "vue3": { "compilerOptions": { "isCustomElement": tag => tag.startsWith('u-') } } }对于多端发布,建议使用环境变量管理不同配置:
// config.js const env = process.env.NODE_ENV export const BASE_URL = { development: 'https://dev.api.com', production: 'https://api.com' }[env]在HBuilderX的"运行"菜单中,可以选择"运行到小程序模拟器"进行真机调试。我习惯在开发过程中保持微信开发者工具和HBuilderX同时运行,这样可以实时查看效果。
10. 持续集成与自动化测试
虽然uniapp项目通常不需要复杂的CI/CD配置,但基本的自动化流程还是很有必要的。我通常在项目根目录下创建build.js处理构建任务:
const { execSync } = require('child_process') // 构建微信小程序 execSync('npm run build:mp-weixin', { stdio: 'inherit' }) // 处理构建后的文件 // ...对于Pinia store的单元测试,可以使用vitest:
import { setActivePinia, createPinia } from 'pinia' import { useUserStore } from '@/store/user' describe('User Store', () => { beforeEach(() => { setActivePinia(createPinia()) }) it('should login', async () => { const store = useUserStore() await store.login({ username: 'test', password: '123' }) expect(store.token).toBeTruthy() }) })在实际项目中,我发现将uview-plus组件与Pinia结合使用时,最大的优势是开发效率的提升。通过合理的架构设计,一个中等复杂度的电商应用可以在2-3周内完成核心功能开发。