1.基础准备
uni-app官网uni-app,uniCloud,serverless,环境安装,创建uni-app,自定义模板,国内特殊情况,更新依赖到指定版本,运行、发布uni-app,运行并发布快应用,运行并发布快应用(webview),运行并发布快应用(webview)-华为,cli创建项目和HBuilderX可视化界面创https://uniapp.dcloud.net.cn/quickstart-cli.html 安装node、hbuilder
安装pnpm,相比于npm他的优点是:显著提升了包安装的速度和磁盘空间利用率,同时避免了依赖冲突和重复安装的问题;
npm install -g pnpm
创建uniapp vue3+ts项目
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
2.引入uni-ui 组件库
安装 uni-ui 组件库
pnpm i @dcloudio/uni-ui
src/pages.json
// pages.json
{
// 组件自动导入
"easycom": {
"autoscan": true,
"custom": {
// uni-ui 规则如下配置 // [!code ++]
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" // [!code ++]
}
},
"pages": [
// …省略
]
}
安装类型声明文件
pnpm i -D @uni-helper/uni-ui-types
配置类型声明文件
// tsconfig.json
{
"compilerOptions": {
// ...
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types", // uni-app 组件类型
"@uni-helper/uni-ui-types" // uni-ui 组件类型 // [!code ++]
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
"nativeTags": ["block", "component", "template", "slot"]
}
}
3.小程序端 Pinia 持久化
安装持久化存储插件,pinia-plugin-persistedstate
pnpm i pinia-plugin-persistedstate
src/stores/modules/member.ts
import type { LoginResult } from '@/types/member'
import { defineStore } from 'pinia'
import { ref } from 'vue'
// 定义 Store
export const useMemberStore = defineStore(
'member',
() => {
// 会员信息
const profile = ref<LoginResult>()
// 保存会员信息,登录时使用
const setProfile = (val: LoginResult) => {
profile.value = val
}
// 清理会员信息,退出时使用
const clearProfile = () => {
profile.value = undefined
}
// 记得 return
return {
profile,
setProfile,
clearProfile,
}
},
{
// 网页端配置
// persist: true,
// 小程序端配置
persist: {
storage: {
getItem(key) {
return uni.getStorageSync(key)
},
setItem(key, value) {
uni.setStorageSync(key, value)
},
},
},
},
)
src/stores/index.ts
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建 pinia 实例
const pinia = createPinia()
// 使用持久化存储插件
pinia.use(persist)
// 默认导出,给 main.ts 使用
export default pinia
// 模块统一导出
export * from './modules/member'
src/main.ts
import { createSSRApp } from 'vue'
import App from './App.vue'
// 导入 pinia 实例
import pinia from './stores'
export function createApp() {
// 创建 vue 实例
const app = createSSRApp(App)
// 使用 pinia
app.use(pinia)
return {
app,
}
}
4.uni.request 请求封装
创建拦截器,src/utils/http.ts
/**
* 添加拦截器:
* 拦截 request 请求
* 拦截 uploadFile 文件上传
*
* TODO:
* 1. 非 http 开头需拼接地址
* 2. 请求超时
* 3. 添加小程序端请求头标识
* 4. 添加 token 请求头标识
*/
import { useMemberStore } from '@/stores'
const baseURL = 'https://pcapi-xiaotuxian-front-devtest.itheima.net'
// 添加拦截器
const httpInterceptor = {
// 拦截前触发
invoke(options: UniApp.RequestOptions) {
// 1. 非 http 开头需拼接地址
if (!options.url.startsWith('http')) {
options.url = baseURL + options.url
}
// 2. 请求超时, 默认 60s
options.timeout = 10000
// 3. 添加小程序端请求头标识
options.header = {
...options.header,
'source-client': 'miniapp',
}
// 4. 添加 token 请求头标识
const memberStore = useMemberStore()
const token = memberStore.profile?.token
if (token) {
options.header.Authorization = token
}
},
}
uni.addInterceptor('request', httpInterceptor)
uni.addInterceptor('uploadFile', httpInterceptor)
/**
* 请求函数
* @param UniApp.RequestOptions
* @returns Promise
* 1. 返回 Promise 对象
* 2. 获取数据成功
* 2.1 提取核心数据 res.data
* 2.2 添加类型,支持泛型
* 3. 获取数据失败
* 3.1 401错误 -> 清理用户信息,跳转到登录页
* 3.2 其他错误 -> 根据后端错误信息轻提示
* 3.3 网络错误 -> 提示用户换网络
*/
type Data<T> = {
code: string
errorMessage: string
data: T
}
// 2.2 添加类型,支持泛型
export const http = <T>(options: UniApp.RequestOptions) => {
// 1. 返回 Promise 对象
return new Promise<Data<T>>((resolve, reject) => {
uni.request({
...options,
// 响应成功
success(res) {
// 状态码 2xx, axios 就是这样设计的
if (res.statusCode >= 200 && res.statusCode < 300) {
// 2.1 提取核心数据 res.data
resolve(res.data as Data<T>)
} else if (res.statusCode === 401) {
// 401错误 -> 清理用户信息,跳转到登录页
const memberStore = useMemberStore()
memberStore.clearProfile()
uni.navigateTo({ url: '/user/login' })
reject(res)
} else {
// 其他错误 -> 根据后端错误信息轻提示
uni.showToast({
icon: 'none',
title: (res.data as Data<T>).errorMessage || '请求错误',
})
reject(res)
}
},
// 响应失败
fail(err) {
uni.showToast({
icon: 'none',
title: '网络错误,换个网络试试',
})
reject(err)
},
})
})
}
6.统一代码风格
安装 eslint+prettier
pnpm i -D eslint prettier eslint-plugin-vue @vue/eslint-config-prettier @vue/eslint-config-typescript @rushstack/eslint-patch @vue/tsconfig
新建文件.eslintrc.cjs ,添加以下 eslint 配置
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier',
],
// 小程序全局变量
globals: {
uni: true,
wx: true,
WechatMiniprogram: true,
getCurrentPages: true,
UniApp: true,
UniHelper: true,
},
parserOptions: {
ecmaVersion: 'latest',
},
rules: {
'prettier/prettier': [
'warn',
{
singleQuote: true,
semi: false,
printWidth: 100,
trailingComma: 'all',
endOfLine: 'auto',
},
],
'vue/multi-word-component-names': ['off'],
'vue/no-setup-props-destructure': ['off'],
'vue/no-deprecated-html-element-is': ['off'],
'@typescript-eslint/no-unused-vars': ['off'],
},
}
配置package.json
{
"script": {
// ... 省略 ...
"lint": "eslint . --ext .vue,.js,.ts --fix --ignore-path .gitignore"
}
}
pnpm lint
7.Git 工作流规范
安装并初始化 husky
pnpm dlx husky-init
npx husky-init
安装 lint-staged
pnpm i -D lint-staged
配置package.json
{
"script": {
// ... 省略 ...
},
"lint-staged": {
"*.{vue,ts,js}": ["eslint --fix"]
}
}
修改 .husky/pre-commit
文件
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
pnpm lint-staged