最佳实践
本指南提供了一些在使用 uni-app-vue3-template
开发项目时的最佳实践建议。
页面组织
主包与分包
合理组织主包和分包可以优化应用的启动速度和运行性能。
主包 (pagesMain)
主包应包含应用的核心页面,如:
- 首页
- 个人中心
- 重要功能入口页面
json
// pages.json
{
"pages": [{
"path": "pagesMain/home",
"style": {
"navigationBarTitleText": "首页"
}
}, {
"path": "pagesMain/user",
"style": {
"navigationBarTitleText": "我的"
}
}]
}
分包 (pages)
将业务功能相关的页面组织成分包:
json
// pages.json
{
"subPackages": [{
"root": "pages",
"pages": [{
"path": "login",
"style": {
"navigationBarTitleText": "登录"
}
}, {
"path": "register",
"style": {
"navigationBarTitleText": "注册"
}
}]
}]
}
TabBar 页面
合理设计 TabBar 页面,建议:
- TabBar 页面数量控制在 3-5 个
- TabBar 页面放在主包中
- 为 TabBar 页面设计统一的图标和主题
javascript
// common/utils/index.js
export const setThemeIcon = function(theme, ms = 0) {
// 设置 TabBar 主题图标
['home', 'me'].forEach((i, index) => {
uni.setTabBarItem({
index,
selectedIconPath: `/static/tabIcon/${i}_${theme}.png`
})
})
}
页面结构
基本页面
vue
<template>
<my-container>
<view class="container">
<x-nav-bar>
<text class="navbar_title">标题</text>
</x-nav-bar>
</view>
</my-container>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { onLoad, onShow, onPullDownRefresh } from '@dcloudio/uni-app'
import { useCommon } from '@/hooks/useCommon.js'
import {} from '@/common/api/index.js'
const { gProps, userStore, globalStore } = useCommon()
const query = reactive({});
onLoad((e) => {
Object.assign(query, e)
})
onShow(() => {
})
const refresh = () => {
// getData(1)
}
onPullDownRefresh(() => {
refresh()
})
</script>
<style lang="scss" scoped>
.container {}
</style>
列表页面
vue
<template>
<my-container>
<view class="container">
<x-nav-bar>
<text class="navbar_title">标题</text>
</x-nav-bar>
</view>
</my-container>
</template>
<script setup>
import { ref, reactive } from 'vue'
import { onLoad, onShow, onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app'
import { useCommon } from '@/hooks/useCommon.js'
import { usePaging } from '@/hooks/usePaging.js'
import {} from '@/common/api/index.js'
const { gProps, userStore, globalStore } = useCommon()
const query = reactive({});
const params = reactive({})
// const { pagingInfo, getData } = usePaging(, params)
onLoad((e) => {
Object.assign(query, e)
})
onShow(() => {
})
onReachBottom(() => {
// getData()
})
const refresh = () => {
// getData(1)
}
onPullDownRefresh(() => {
refresh()
})
</script>
<style lang="scss" scoped>
.container {}
</style>
代码规范与格式化
代码风格配置
项目使用 .jsbeautifyrc
配置代码格式化规则:
json
// .jsbeautifyrc
{
"indent_size": 2,
"indent_char": " ",
"indent_with_tabs": false,
"eol": "\n",
"end_with_newline": true,
"indent_level": 0,
"preserve_newlines": true,
"max_preserve_newlines": 10,
"space_in_paren": false,
"space_in_empty_paren": false,
"jslint_happy": false,
"space_after_anon_function": false,
"brace_style": "collapse",
"unindent_chained_methods": false,
"break_chained_methods": false,
"keep_array_indentation": false,
"unescape_strings": false,
"wrap_line_length": 0,
"e4x": false,
"comma_first": false,
"operator_position": "before-newline"
}
Vue 组件规范
组件结构
vue
<template>
<view class="component-name">
<!-- 组件内容 -->
</view>
</template>
<script>
// 导入语句按功能分组
import { useGlobalStore } from '@/stores/global.js'
export default {
name: 'ComponentName',
components: {
// 组件注册
},
props: {
// 属性定义
},
data() {
return {
// 数据定义
}
},
computed: {
// 计算属性
},
watch: {
// 监听器
},
created() {
// 生命周期钩子
},
mounted() {
// 生命周期钩子
},
methods: {
// 方法定义
}
}
</script>
<style scoped>
/* 样式定义 */
.component-name {
/* 样式内容 */
}
</style>
命名规范
- 组件名使用大驼峰命名法:
UserProfile.vue
- 变量和方法使用小驼峰命名法:
getUserInfo()
- 常量使用大写字母和下划线:
const MAX_RETRY_COUNT = 3
- CSS 类名使用短横线分隔:
.user-profile
状态管理 (Pinia)
Store 设计原则
- 按功能模块划分 Store
- 保持 Store 的纯净性
- 合理使用 Getters 和 Actions
javascript
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
userInfo: null,
token: uni.getStorageSync('token') || ''
}),
getters: {
isLoggedIn: (state) => !!state.token,
userName: (state) => state.userInfo?.name || ''
},
actions: {
setToken(token) {
this.token = token
uni.setStorageSync('token', token)
},
clearToken() {
this.token = ''
uni.removeStorageSync('token')
},
async login(loginData) {
try {
const result = await http.post('/api/auth/login', loginData)
this.setToken(result.token)
this.userInfo = result.userInfo
return result
} catch (error) {
this.clearToken()
throw error
}
}
}
})
Store 使用规范
javascript
// 在组件中使用 Store
import { useUserStore } from '@/stores/user.js'
export default {
computed: {
// 使用 mapState 简化访问
...mapState(useUserStore, ['userInfo', 'isLoggedIn']),
userName() {
return useUserStore().userName
}
},
methods: {
async handleLogin() {
const userStore = useUserStore()
try {
await userStore.login(this.loginForm)
uni.showToast({
title: '登录成功',
icon: 'success'
})
uni.switchTab({
url: '/pagesMain/home'
})
} catch (error) {
uni.showToast({
title: '登录失败',
icon: 'none'
})
}
}
}
}
网络请求最佳实践
请求封装使用
- 统一使用封装的 http 实例
- 合理处理 Loading 状态
- 正确处理错误情况
javascript
// api/user.js
import { http } from '@/common/network/http.js'
export const userApi = {
// 获取用户信息
getUserInfo() {
return http.get('/api/user/info')
},
// 更新用户信息
updateUserInfo(userData) {
return http.post('/api/user/update', userData)
},
// 上传头像
uploadAvatar(filePath) {
return uploadFile.upload({
url: '/api/user/upload/avatar',
filePath: filePath,
name: 'file'
})
}
}
错误处理
javascript
// 在组件中处理请求错误
export default {
methods: {
async fetchUserInfo() {
try {
const userInfo = await userApi.getUserInfo()
this.userInfo = userInfo
} catch (error) {
// 根据错误类型进行不同处理
if (error.statusCode === 401) {
// 未授权,跳转到登录页
uni.navigateTo({
url: '/pages/login'
})
} else {
// 其他错误,显示提示
uni.showToast({
title: '获取用户信息失败',
icon: 'none'
})
}
}
}
}
}
主题与样式最佳实践
CSS 变量使用
- 统一使用主题变量定义颜色
- 避免硬编码颜色值
scss
/* 推荐 */
.button {
background-color: var(--theme-color);
color: white;
}
/* 不推荐 */
.button {
background-color: #87ceeb;
color: white;
}
响应式设计
scss
/* 使用 rpx 单位适配不同屏幕 */
.container {
padding: 20rpx;
font-size: 28rpx;
}
性能优化建议
图片优化
- 使用适当的图片格式
- 压缩图片大小
- 使用懒加载
vue
<template>
<view>
<!-- 使用 lazy-load 属性 -->
<image
src="/static/images/avatar.png"
lazy-load
mode="aspectFill"
/>
</view>
</template>
列表优化
- 使用虚拟列表处理长列表
- 合理使用 v-if 和 v-show
vue
<template>
<view>
<!-- 长列表使用虚拟滚动 -->
<uv-list>
<uv-list-item
v-for="item in visibleItems"
:key="item.id"
:title="item.title"
/>
</uv-list>
</view>
</template>
调试与测试
开发环境日志
javascript
// 在开发环境中输出详细日志
// main.js
export function createApp() {
// #ifdef H5
if (process.env.NODE_ENV == 'production')
for (let key in console) console[key] = () => {} // 生产环境清除 log
// #endif
}
网络请求调试
javascript
// common/network/http.js
const responseInterceptor = async function(res) {
// 输出请求信息, 方便 app 调试
// #ifndef APP
console.groupCollapsed(reqConf.url.replace(baseUrl, ''))
// #endif
console.log('REQ URL', reqConf.url);
console.log('REQ HEADER', reqConf.header);
console.log('REQ DATA', reqConf.data);
console.log('RES DATA', res.data);
// #ifndef APP
console.groupEnd(reqConf.url.replace(baseUrl, ''))
// #endif
}
通过遵循这些最佳实践,可以提升代码质量、开发效率和用户体验。