【2023】uniapp+vue3+ts超实用模板(续)
接着上篇文章【2023】uniapp+vue3+ts超实用模板,我们继续优化模板。
6、处理unocss生成的样式在小程序报错问题
在上面模板的基础上,npm run dev:mp-weixin
会在dist/dev/mp-weixin
生成微信小程序文件,打开微信开发者工具,导入该文件夹,发现跑不起来,控制台报错,如下。
发现是生成的 app.wxss
包含了通配符*
, 微信小程序不认识,编译不通过,页面也无法显示。
解决方案:删除unocss
里面的自带预设presetUno
,改用@uni-helper/unocss-preset-uni
的专门针对uniapp
的预设presetUni
,代码如下:
// uno.config.ts
import {
defineConfig,
presetAttributify,
- presetUno,
presetIcons,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
+ import { presetUni } from '@uni-helper/unocss-preset-uni'
export default defineConfig({
presets: [
- presetUno,
+ // @ts-expect-error 类型兼容性
+ presetUni(),
// 支持css class属性化,eg: ``
presetAttributify(),
// 支持图标,需要搭配图标库,eg: @iconify-json/carbon, 使用 ``
presetIcons({
scale: 1.2,
warn: true,
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
},
}),
],
transformers: [
transformerDirectives(),
// 支持css class组合,eg: `测试 unocss`
transformerVariantGroup(),
],
})
重新 npm run dev:mp-weixin
, 会发现生成的 app.wxss
, 去掉了 *
增加了page
标签。
现在微信小程序也正常显示了,如下图
我们来处理一下生成的 app.wxss
中page
标签 不认识的问题,
在 .stylelintrc.cjs
的 rules 中增加如下代码,然后重启vscode即可。
{
rules: {
// 其他配置
+ // 处理小程序page标签不认识的问题
+ 'selector-type-no-unknown': [
+ true,
+ {
+ ignoreTypes: ['page'],
+ },
+ ],
}
}
mac电脑 cmd + shift + p
, 出现下面图,找到 重启扩展宿主
。
7、处理 v-bind() 在小程序中不生效问题
vue3 可以在css中使用v-bind(v-bind in css)来绑定变量,这是一个很不错的特性,我们来看下面的例子。
解决方法:在 manifest.json
里面增加如下设置:
{
"mp-weixin": {
+ "styleIsolation": "shared",
}
}
这样生效的前提是,style标签不能加scoped
, 否则依然报错。另外一个问题是,该文件引入的子组件的样式会被污染。
鉴于我们一般会加上scoped
,并且不想因为样式污染导致样式问题,最终还是决定用js的方式处理。(尴尬了)把上述提交revert掉即可。
另外附上小程序样式隔离文档:微信开发者文档-组件样式隔离
8、引入 vite-plugin-uni-pages
在 Vite 驱动的 uni-app 上使用基于文件的路由系统。
8-1 安装 @uni-helper/vite-plugin-uni-pages
pnpm i -D @uni-helper/vite-plugin-uni-pages
8-2 配置vite.config.ts
// vite.config.ts
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniPages from '@uni-helper/vite-plugin-uni-pages'
// It is recommended to put it in front of Uni
export default defineConfig({
plugins: [UniPages(), Uni()],
})
8-3 配置 pages.config.ts
// pages.config.ts
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
export default defineUniPages({
// 你也可以定义 pages 字段,它具有最高的优先级。
pages: [],
globalStyle: {
navigationBarTextStyle: 'black',
navigationBarTitleText: '@uni-helper',
},
})
8-4 使用 route 标签配置路由信息
通过 pnpm dev:h5
发现 pages.json
被重写了。
此时可以在页面上(仅限src/pages里面的页面)增加 route block 来配置,如:
以后每个页面”要不要导航栏,标题要怎么写,标题样式要怎样“都可以在这里设置了,再也不用来回横切了,爽歪歪!
9、vite-plugin-uni-layouts
考虑到我们可能会有多套布局,tabbar页面,非tabbar页面,通屏页面,非通屏页面。对于通屏页面,需要自己实现导航栏,同时还要刘海的情况。我们不用在每个页面都引入某个类似布局的组件,这样太浪费生产力了,我们通过 vite-plugin-uni-layouts
可以声明式地设定使用哪个布局,爽歪歪。
9-1 安装 @uni-helper/vite-plugin-uni-layouts
pnpm i -D @uni-helper/vite-plugin-uni-layouts
9-2 配置vite.config.ts
// vite.config.ts
import { defineConfig } from 'vite'
import Uni from '@dcloudio/vite-plugin-uni'
import UniPages from '@uni-helper/vite-plugin-uni-pages'
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
// It is recommended to put it in front of Uni
export default defineConfig({
plugins: [UniPages(), UniLayouts(), Uni()],
})
9-3 在 src/layouts 里面创建布局
9-4 使用layout
在页面上的route-block增加 layout 属性,设置想用的 layout,可选值是所有的 src/layouts里面的文件名,我这里是 "default"|"home".
重新运行,就可以看到效果了。
10、request 请求拦截器
10-1 先写好 store, 请求需要使用其中的token
src/store/user.ts
// src/store/user.ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { UserInfo } from '../typings'
export const useUserStore = defineStore(
'user',
() => {
const userInfo = ref()
const setUserInfo = (val: UserInfo) => {
userInfo.value = val
}
const clearUserInfo = () => {
userInfo.value = undefined
}
return {
userInfo,
setUserInfo,
clearUserInfo,
}
},
{
persist: true,
},
)
src/store/index.ts
// src/store/index.ts
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化
const store = createPinia()
store.use(
createPersistedState({
storage: {
getItem: uni.getStorageSync,
setItem: uni.setStorageSync,
},
}),
)
export default store
// 模块统一导出
export * from './user'
export * from './count'
10-2 请求拦截,支持设定返回类型
// src/utils/http.ts
/* eslint-disable no-param-reassign */
import { useUserStore } from '@/store'
import { UserInfo } from '@/typings'
const userStore = useUserStore()
type Data = {
code: number
msg: string
result: T
}
// 请求基地址
const baseURL = 'http://localhost:5565/api'
// 拦截器配置
const httpInterceptor = {
// 拦截前触发
invoke(options: UniApp.RequestOptions) {
// 1. 非 http 开头需拼接地址
if (!options.url.startsWith('http')) {
options.url = baseURL + options.url
}
// 2. 请求超时
options.timeout = 10000 // 10s
// 3. 添加小程序端请求头标识
options.header = {
platform: 'mp-weixin', // 可选值与 uniapp 定义的平台一致,告诉后台来源
...options.header,
}
// 4. 添加 token 请求头标识
const { token } = userStore.userInfo as unknown as UserInfo
if (token) {
options.header.Authorization = `Bearer ${token}`
}
},
}
// 拦截 request 请求
uni.addInterceptor('request', httpInterceptor)
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor)
export const http = (options: UniApp.RequestOptions) => {
// 1. 返回 Promise 对象
return new Promise>((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)
} else if (res.statusCode === 401) {
// 401错误 -> 清理用户信息,跳转到登录页
userStore.clearUserInfo()
uni.navigateTo({ url: '/pages/login/login' })
reject(res)
} else {
// 其他错误 -> 根据后端错误信息轻提示
uni.showToast({
icon: 'none',
title: (res.data as Data).msg || '请求错误',
})
reject(res)
}
},
// 响应失败
fail(err) {
uni.showToast({
icon: 'none',
title: '网络错误,换个网络试试',
})
reject(err)
},
})
})
}
export default http
注意:上面代码设定,如果返回401
则去/pages/login/login
也,请根据需要添加该页面或修改逻辑。
10-3 使用requst,可设定返回类型
const handleRequest = () => {
const res = http({
url: '/getUserList',
method: 'GET',
})
console.log(res)
}
11、多环境处理
11-1 编写env文件夹,里面放.env,.env.production,.env.development等文件
11-2 把http.ts文件的baseUrl 替换掉
// 请求基地址
- const baseURL = 'http://localhost:5565/api'
+ const baseURL = import.meta.env.VITE_SERVER_BASEURL
src/env-d.ts
///
///
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
// 下面都是新增的
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_SERVER_PORT: string
readonly VITE_SERVER_BASEURL: string
readonly VITE_DELETE_CONSOLE: string
// 更多环境变量...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
11-3 vite.config.ts 针对console是否清楚的处理
首先需要 pnpm i -D terser
,然后加入如下代码。
// vite.config.ts
build: {
minify: 'terser',
terserOptions: {
compress: {
drop_console: env.VITE_DELETE_CONSOLE === 'true',
drop_debugger: env.VITE_DELETE_CONSOLE === 'true',
},
},
}
12、unocss icons 的使用
12-1 安装对应的库
安装格式如下:pnpm i -D @iconify-json/[the-collection-you-want]
这里我安装carbon, 所以执行 pnpm i -D @iconify-json/carbon
,如果还要其他的还可以继续安装。
12-2 使用
所有的图标都在 https://icones.js.org/ 这个网站,要啥就安装啥(而且不用科学上网,利好国人)。
13、引入uni-ui
13-1 安装
pnpm i -S @dcloudio/uni-ui
pnpm i -D @uni-helper/uni-ui-types
13-2 tsconfig.ts 增加类型
"types": [
"@dcloudio/types",
"@types/wechat-miniprogram",
"@uni-helper/uni-app-types",
+ "@uni-helper/uni-ui-types"
]
13-3 pages.config.ts 配置 easycom
easycom: {
autoscan: true,
custom: {
// uni-ui 规则如下配置
'^uni-(.*)': '@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue',
},
},
13-4 使用
可以看到页面立马生效了。
14、自己写的常用组件,可以通过easycom引入
14-1 pages.config.ts 配置 easycom
easycom: {
autoscan: true,
custom: {
+ // 以 Fly 开头的组件,在 components 文件夹中查找引入(需要重启服务器)
+ '^Fly(.*)': '@/components/fly-$1/fly-$1.vue',
+ '^fly-(.*)': '@/components/fly-$1/fly-$1.vue',
// uni-ui 规则如下配置
'^uni-(.*)': '@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue',
},
},
虽然上面2种写法都支持,但是最好还是与主流框架统一,全部使用小写+中划线。
14-2 使用
可以看到页面立马生效了。
虽然上面2种写法都支持,但是最好还是与主流框架统一,全部使用小写+中划线。
完结
写了这么多功能,需要的基本都加上了,还有没加上的,环境评论区留言。
项目地址:github: fly-vue3-templates/vue3-uniapp-template
项目地址:gitee: fly-vue3-templates/vue3-uniapp-template
本文由mdnice多平台发布