0. 前言
- 最近在把一个uni-app项目从原来的vue2重构到vue3上,趁这次机会记录一下这个项目打搭建方便自己后面再次用到就可以直接拉代码了。
- 这篇文章很多实现和思路都是参考其他大佬的,如果有哪里不对欢迎大家指出。
1. 项目初始化
1.1 通过vue-cli命令创建
- 使用Vue3/Vite版
- 创建以 typescript 开发的工程(如命令行创建失败,请直接访问 gitee 下载模板)
1
| npx degit dcloudio/uni-preset-vue
|
tips:
- Vue3/Vite版要求 node 版本^14.18.0 || >=16.0.0
1.2 ESLint
配置参考文章
vs-code安装和配置ESLint
1.3 prettier
1
| npm i prettier eslint-config-prettier eslint-plugin-prettier -D
|
1 2 3 4 5 6 7 8 9 10 11 12
| module.exports = { printWidth: 100, tabWidth: 2, useTabs: false, singleQuote: true, semi: true, arrowParens: 'always', endOfLine: 'auto', vueIndentScriptAndStyle: true, htmlWhitespaceSensitivity: 'strict', };
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
module.exports = { root: true, env: { browser: true, es2021: true, node: true, }, extends: [ 'eslint:recommended', 'plugin:vue/vue3-essential', 'plugin:prettier/recommended', 'prettier', ], parser: 'vue-eslint-parser', parserOptions: { ecmaVersion: 'latest', parser: '@typescript-eslint/parser', sourceType: 'module', }, plugins: ['vue', 'prettier'], rules: { 'vue/multi-word-component-names': 'off' }, };
|
1 2 3 4
| # package.json
# 可以运行'npm run lint'检查代码 "lint": "eslint --ext .js,.vue,.ts src --fix"
|
1.4 保存文件自动格式化
1 2 3 4 5 6 7 8 9 10
|
{ "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.formatOnSave": true }
|
2. 环境变量
vite官方文档:
环境变量和模式
- 创建环境变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 1. 根目录创建.env.[mode]文件
# .env.development
# 开发环境 NODE_ENV = development VITE_APP_API_BASE_URL = 'http://10.204.xx.xx:9091' # 是否在打包时生成 sourcemap VITE_BUILD_SOURCEMAP = true # 是否在打包时删除 console 代码 VITE_BUILD_DROP_CONSOLE = false
# .env.test # .env.production
|
.env.[mode]文件中的mode可自定义,如.env.development
对应package.json脚本中的--mode development
只有以 VITE_ 为前缀的变量才会暴露给经过 vite 处理的代码
1 2 3 4 5
|
"dev:h5": "uni -p h5 --mode development", "build:test": "uni build --mode test", "build:pro": "uni build -p h5 --mode production"
|
- 使用环境变量
- js,vue 文件中可使用
import.meta.env
获取环境变量,比如:
1 2 3
| let baseUrl = import.meta.env.VITE_APP_API_BASE_URL;
let isProd = import.meta.env.MODE === 'production';
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
import { defineConfig, loadEnv } from 'vite';
export default ({ command, mode }) => { const env = loadEnv(mode, process.cwd()); return defineConfig({ plugins: [ uni(), resolve: { alias: { '@': path.resolve(__dirname, 'src'), '@img': path.resolve(__dirname, 'src/static/images'), }, }, build: { sourcemap: env.VITE_BUILD_SOURCEMAP === 'true', minify: 'terser', terserOptions: { compress: { drop_console: env.VITE_BUILD_DROP_CONSOLE === 'true', }, }, chunkSizeWarningLimit: 1500, }, }); };
|
3. Css预处理器
1 2 3 4 5
| // 1、 安装sass npm i sass -D 或 yarn add sass -D
//安装 sass-loader npm i sass-loader@10.1.1 -D 或 yarn add sass-loader@10.1.1 -D
|
- 全局使用自定义变量
- 根目录新建样式文件夹styles
- index.scss - 自定义变量
1 2 3 4 5 6 7 8
| css: { preprocessorOptions: { scss: { additionalData: `@import "@/styles/vars.scss";`, }, }, }
|
vue文件使用
1 2 3
| .title { color: $font-color }
|
4. uni-ui
uni-ui官方文档
- 安装uni-ui
1 2
| //安装 uni-ui npm i @dcloudio/uni-ui 或 yarn add @dcloudio/uni-ui
|
- 配置easycom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 配置easycom 使用 npm 安装好 uni-ui 之后,需要配置 easycom 规则,让 npm 安装的组件支持 easycom 打开项目根目录下的 pages.json 并添加 easycom 节点
{ "easycom": { "autoscan": true, "custom": { "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" } }, pages:[ ] }
|
5. 自动导入API
unplugin-auto-import
1
| npm i unplugin-auto-import -D
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // vite.config.js import AutoImport from 'unplugin-auto-import/vite'
plugins: [ AutoImport({ imports: ['vue', 'uni-app'], // 可以选择auto-import.d.ts生成的位置,使用ts建议设置为'src/auto-import.d.ts' // dts: 'src/auto-import.d.ts' // 自动生成'eslintrc-auto-import.json'文件,在'.eslintrc.cjs'的'extends'中引入解决报错 eslintrc: { enabled: true, }, }) ]
|
- 原理: 安装的时候会自动生成auto-imports.d文件(默认是在根目录)
- 其他插件 vue-router, vue-i18n, @vueuse/head, @vueuse/core等自动引入的自动引入请查看文档
1 2 3 4 5
| extends: [ './.eslintrc-auto-import.json', ],
|
接下来就可以全局使用 vue 相关 api,不用一个个手动导入了。哪些 api 可用请参考生成的 src/auto-import.d.ts
类型声明文件。
6. Pinia
pinia官方文档
- 安装
- 创建store
1 2 3 4 5 6 7 8
|
import { createPinia } from 'pinia';
const pinia = createPinia();
export default pinia; export * from './modules/user';
|
- 挂载store
1 2 3 4 5 6 7 8 9 10 11 12
| import { createSSRApp } from 'vue'; import store from './store'; import App from './App.vue';
export function createApp() { const app = createSSRApp(App); app.use(store); return { app, }; }
|
- 创建useUserStore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
import { defineStore } from 'pinia';
export const useUserStore = defineStore('user', { state: () => { return { name: '张三', }; }, getters: { nameLength: (state) => state.name.length, }, actions: { updateName(name) { this.name = name; }, }, });
|
- 使用useUserStore
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <template> <div class="pinia"> <div class="name">用户名:{{ userStore.name }}</div> <div class="length">长度:{{ userStore.nameLength }}</div> <van-button type="primary" @click="updateName(true)">action修改store中的name</van-button> <van-button @click="updateName(false)">patch修改store中的name</van-button> </div> </template>
<script setup> import { useUserStore } from '@/store';
const userStore = useUserStore();
const updateName = (isAction) => { if (isAction) { userStore.updateName('userStore.updateName方式'); } else { userStore.$patch({ name: 'userStore.$patch方式', }); } }; </script>
|
- ==注意点==
在使用 pinia 中的变量时如果使用解构赋值,需要使用 storeToRefs
这个方法包裹一下,否则全局变量会失去响应式,变量更新时并不会重新渲染组件。
1 2 3 4 5 6 7
| import { useUserStore } from '@/store';
const {name} = useUserStore()
const (user) = storeToRefs(useUserStore())
|
7. 请求封装
封装请求的方式多种多样,根据自己喜欢的方式实现就好,还可以根据需求增加重试或者取消请求等方法。
- request请求统一封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
import { getToken } from '@/utils/auth'; import { useUserStore } from '@/store';
let baseUrl = import.meta.env.VITE_APP_API_BASE_URL; const request = ({ url = '', data = {}, method = 'POST', header = { token: getToken() }, hideLoading=false, hideMessage, }) => { const userStore = useUserStore(); return new Promise((resolve, reject) => { uni.request({ timeout: 60000, method, url: baseUrl + url, data, header, success(response) { let res = response.data; if (res.resultCode !== 0) { if (hideMessage) { reject(res || 'Error'); } else { if (res.resultCode === 3 || res.resultCode === -5) { uni.showToast({ title: res.resultMessage, icon: 'none', duration: 3000, }); } else if (res.resultCode === -4) { } else { if (res.resultCode === -1) { uni.showModal({ title: '提示', content: '登录失效,请重新登录!', confirmColor: '#0087FF', cancelColor: '#0087FF', success: function (res) { if (res.confirm) { userStore.resetToken().then(() => { uni.navigateTo({ url: '/subPackagesA/personal/chooseLoginType', }); }); } }, }); } else { uni.showToast({ title: `操作异常,请联系管理员(${res.resultCode})!`, icon: 'none', duration: 3000, }); } } reject(res || 'Error'); } } else { resolve(res); } }, fail(err) { reject(err); }, }); }); }; export default request;
|
- 接口api管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
import request from '@/utils/request'; import { requestPort } from '@/utils/requestPort';
export default { login(data) { return request({ url: `${requestPort.users}/user/login`, method: 'post', data, }); }, logout() { return request({ url: `${requestPort.users}/user/logout/3`, method: 'post', }); }, logoff(data) { return request({ url: `${requestPort.users}/user/logoff`, method: 'post', data, }); } };
|
- 使用接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // vue文件内 <template> <view> <button type="default" class="logout" @click="logout">注销</button> </view> </template>> <script setup> import UserService from '@/api/UserService';
const logout = async () => { try { const { resultData } = await UserService.logout(); console.log(resultData, 'resultData'); } catch (e) { console.log(e, 'error'); } }; <script>
|
8. 配置开发环境代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
import { defineConfig, loadEnv } from 'vite';
const path = require('path')
export default ({ command, mode }) => { const env = loadEnv(mode, process.cwd()) return defineConfig({ plugins: [uni()], base: './', server: { "port": 8080, proxy: { '/apis': { target: 'https://www.xxx.com', changeOrigin: true, secure: false, rewrite: path => { return path.replace(/^\/apis/, '/') } } } } }) }
|
- 以上就是环境搭建的大致流程了,缺少的部分后面想到了再更新。