uniapp小程序模板搭建
# 写在前面
2022-01-14更新:后来看到官方支持vite打包了,于是更改了模板。
之所以想折腾下uniapp小程序模板,是因为现有小程序的架子版本很老了,项目是2年前的,基于uniapp框架,使用的模板是uni-preset-vue (opens new window)模板预设,整体依赖版本都偏低。本次模板搭建是想对uniapp模板进行升级并维护,基于团队规范搭建自己的模板。
本工程基于uniapp的uni-preset-vue#vite (opens new window)模板,技术栈为vite2+vue3+vuex4+typescript
,ui框架选用uni-ui。
# 探索过程
参考官方文档进行工程搭建。
# 安装
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
# 使用sass
npm i sass -D
不需要额外配置,直接使用scss即可。
# eslint
参考vue官方文档 (opens new window)进行配置。
npm i eslint eslint-plugin-vue -D
"scripts": {
"lint": "eslint --fix --ext .js,.vue src"
}
2
3
// .eslintrc.js
module.exports = {
env: {
browser: true,
es6: true
},
extends: ['eslint:recommended', 'plugin:vue/vue3-essential'],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
uni: true,
wx: true,
require: true,
process: true,
getApp: true,
getCurrentPages: true,
gioGlobal: true,
Component: true,
requirePlugin: true,
exports: true,
define: true,
global: true
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module'
},
plugins: ['vue'],
rules: {
'linebreak-style': ['off', 'windows'],
// 单引号
quotes: ['error', 'single'],
// js部分不要分号
semi: [0, 'always'],
'no-undef': 'off',
// Vue 风格
// 指令缩写
'vue/v-bind-style': ['error', 'shorthand'],
'vue/v-on-style': ['error', 'shorthand'],
// 组件/实例的选项的顺序
'vue/order-in-components': [
'error',
{
order: [
'el',
'name',
'parent',
'functional',
['delimiters', 'comments'],
['components', 'directives', 'filters'],
'extends',
'mixins',
'inheritAttrs',
'model',
['props', 'propsData'],
'data',
'computed',
'watch',
'LIFECYCLE_HOOKS',
'onLoad',
'onReady',
'onShow',
'onHide',
'onUnload',
'methods',
['template', 'render'],
'renderError'
]
}
],
// 元素/组件特性,属性的顺序
'vue/attributes-order': [
'error',
{
order: [
'DEFINITION', // e.g. 'is', 'v-is'
'LIST_RENDERING', // e.g. 'v-for item in items'
'CONDITIONALS', // e.g. 'v-if', 'v-else-if', 'v-else', 'v-show', 'v-cloak'
'RENDER_MODIFIERS', // e.g. 'v-once', 'v-pre'
'GLOBAL', // e.g. 'id'
'UNIQUE', // e.g. 'ref', 'key'
'SLOT', // e.g. 'v-slot', 'slot'.
'TWO_WAY_BINDING', // e.g. 'v-model'
'OTHER_DIRECTIVES', // e.g. 'v-custom-directive'
'OTHER_ATTR', // e.g. 'custom-prop="foo"', 'v-bind:prop="foo"', ':prop="foo"'
'EVENTS', // e.g. '@click="functionCall"', 'v-on="event"'
'CONTENT' // e.g. 'v-text', 'v-html'
]
}
]
},
overrides: [
{
files: ['*.vue'],
rules: {
indent: 'off'
}
}
]
}
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
注意:
- extends原本设置为
plugin:vue/vue3-recommended
,但实践后发现语法跟平时开发相差太大了,于是改为plugin:vue/vue3-essential
,只保留必要的。 - 发现eslint与ts不兼容,于是新增了
@typescript-eslint/parser
。参考 (opens new window)。
其他配置均来自原有团队规范。
# vscode插件Volar
此插件是vue官方推荐的,开发vue3必备。不过它与Vetur会冲突,可以在vue3的项目中禁用Vetur。
# ts-配置@别名
import { store } from '@/store'
会报ts错误:
Cannot find module '@/http' or its corresponding type declarations.ts(2307)
需要在tsconfig.json
中新增配置。
"paths": {
"@/*": ["./src/*"]
}
2
3
# 使用vuex4
模板已安装了vuex4,也可以参考文档 (opens new window)。
和之前的使用区别不大:
// src/store/index.ts
import { createStore } from 'vuex'
import test from './modules/test'
interface State {
count: number
}
const state: State = {
count: 1
}
const mutations = {
increment(state: State, payload = 1) {
state.count += payload
}
}
const options = {
state,
mutations,
modules: {
test
}
}
export const store = createStore(options)
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
// main.ts
import { createSSRApp } from 'vue'
import App from './App.vue'
import { store } from './store'
export function createApp() {
const app = createSSRApp(App)
app.use(store)
return {
app
}
}
2
3
4
5
6
7
8
9
10
11
12
使用方式有2种
// 该方式是官方推荐的,并且对 store.state.test.count 不会有类型报错
import { useStore } from 'vuex'
const store = useStore()
// 该方式少一行代码,但会有类型报错
// import { store } from '@/store'
2
3
4
5
6
其他使用方式同vuex3,不过在setup模式下,没有辅助函数。
const count = computed(() => store.state.count)
const moduleCount = computed(() => store.state.test.count)
const addCount = (type: string) => {
console.log('type', type)
if (type === 'module') {
store.commit('test/increment', 2)
} else {
store.commit('increment', 3)
}
}
2
3
4
5
6
7
8
9
10
11
# 网络请求
axios请求库很强大,但只在浏览器中生效。根据项目经验,仍然使用flyio
(opens new window)作为请求库,幸运的是,作者已支持ts。网络请求库的封装请参考另一篇uniapp项目中使用flyio封装请求库。
# 开发代理
vite支持开发时配置代理。注意,此方式只在浏览器生效,小程序不能使用。
// vite.config.ts
server: {
proxy: {
'/customer-api': {
target: 'https://www.baidu.com',
changeOrigin: true
}
}
},
2
3
4
5
6
7
8
9
# 添加环境变量import.meta.env
参考文档 (opens new window),比如在根目录下新建.env.stage
,写入
VITE_APP_TITLE=My App
配置mode
// package.json
{
"stage:h5": "uni --mode stage",
}
2
3
4
然后在业务代码中使用
console.log(import.meta.env.VITE_APP_TITLE)
注意要设置ts类型
// env.d.ts
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
2
3
4
5
6
7
如此,上述代理转发就可以去掉了,在请求配置的baseURL使用import.meta.env.VITE_APP_HOST
即可。
# 生成环境去除console
vite内置了terser压缩 (opens new window),可以配置
// vite.config.ts
build: {
minify: 'terser',
terserOptions: {
compress: {
//生产环境时移除console
drop_console: true
}
}
},
2
3
4
5
6
7
8
9
10
也可以使用esbuild去除,注意此方式在uniapp开发微信小程序时也会去除console。
esbuild: { pure: ['console.log'], minify: true },
# 使用vuex-persistedstate
npm i vuex-persistedstate -D
// src/store/index
import createPersistedState from 'vuex-persistedstate'
const options = {
state: {
count: 1
},
plugins: [
createPersistedState({
// 本地存储数据的键名 默认vuex
key: 'uniapp_vue3',
// 指定需要存储的模块,如果是模块下具体的数据需要加上模块名称,如user.token
paths: ['count'],
// 默认存储在localStorage,改为uni storage API
storage: {
getItem: key => uni.getStorageSync(key),
setItem: (key, value) => uni.setStorageSync(key, value),
removeItem: key => uni.removeStorageSync(key)
}
})
]
}
export const store = createStore(options)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 使用@dcloudio/uni-ui
npm i @dcloudio/uni-ui -S
安装之后配置easycom方式,这样项目中可以直接使用了。
// pages.json
{
"easycom": { "autoscan": true, "custom": { "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue" } }
}
2
3
4
<uni-badge text="1"></uni-badge>
# uniapp-router-patch
在 uni-app 生成的小程序中 vue-router的兼容写法。
遇到问题1:
TS7016: Could not find a declaration file for module 'uniapp-router-patch'.
解决方案1:
/// src/shims-vue.d.ts
// 声明全局模块
declare module 'uniapp-router-patch'
2
3
遇到问题2:
小程序编译报错
TypeError: Cannot read property 'hasOwnProperty' of undefined
源头为
if (Vue.prototype.hasOwnProperty('$router')) {
return;
}
2
3
发现是uniapp-router-patch
不支持vue3的use方式引起的,因此改下该npm包对于vue3的支持。