first commit

This commit is contained in:
2026-04-14 15:06:26 +08:00
commit 9ee0c6c597
582 changed files with 61051 additions and 0 deletions

208
docs/base/1-introduction.md Normal file
View File

@@ -0,0 +1,208 @@
# 简介
<div class="md-center" style="margin-top: 20px;">
[![GitHub Repo stars](https://img.shields.io/github/stars/codercup/unibest?style=flat&logo=github)](https://github.com/codercup/unibest)
[![GitHub Repo stars](https://img.shields.io/github/stars/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest)
[![star](https://gitee.com/codercup/unibest/badge/star.svg?theme=dark)](https://gitee.com/codercup/unibest)
![node version](https://img.shields.io/badge/node-%3E%3D18-green)
![pnpm version](https://img.shields.io/badge/pnpm-%3E%3D7.30-green)
![GitHub License](https://img.shields.io/github/license/codercup/unibest)
</div>
> 上面前 2 个 `star` 分别是旧仓库 `codercup` 和新仓库 `feige996` 的 `star` 数。
`unibest` 是最好的 `uniapp` 开发框架,由 `uniapp` + `Vue3` + `Ts` + `Vite5` + `UnoCss` + `VSCode`(可选 `webstorm`) + `uni插件`+ `wot-ui`(可选其他 UI 库)构建,集成了多种工具和技术,使用了最新的前端技术栈,无需依靠 `HBuilderX`,通过命令行方式即可运行 `web``小程序``App`。(注:`App` 还是需要 `HBuilderX`
`unibest` 内置了 `约定式路由``layout布局``请求封装``请求拦截``登录拦截``UnoCSS``i18n多语言` 等基础功能,提供了 `代码提示``自动格式化``统一配置``代码片段` 等辅助功能,让你编写 `uniapp` 拥有 `best` 体验 `unibest 的由来`)。
> `unibest` 目前支持 `H5`、`小程序` 和 `App`。
::: tip ⭐⭐⭐⭐⭐
如果 `unibest` 对您有帮助,希望你可以去 **Github** 或者 **Gitee(码云)** 帮我点个 ⭐ ,这将是对我极大的鼓励。
<!-- - github - feige996/unibest -->
[![star](https://img.shields.io/github/stars/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest)
<!-- - gitee - feige996/unibest -->
[![star](https://gitee.com/feige996/unibest/badge/star.svg?theme=dark)](https://gitee.com/feige996/unibest)
:::
::: tip 🌟🌟🌟🌟🌟
旧的文档地址 [codercup/unibest](https://codercup.github.io/unibest-docs/)不再维护,尽量使用新地址[unibest.tech](https://unibest.tech)。
:::
## ⭐ Star History
同类模板对比实时地址:[https://www.star-history.com/#Ares-Chang/uni-vitesse&uni-helper/vitesse-uni-app&codercup/unibest&feige996/unibest&DaMaiCoding/uni-plus&Date](https://www.star-history.com/#Ares-Chang/uni-vitesse&uni-helper/vitesse-uni-app&codercup/unibest&feige996/unibest&DaMaiCoding/uni-plus&Date)
如图所示,两个高高的都是 `unibest`,分别是新旧仓库。
黄色的是旧的 `codercup`,秘钥丢失,进不去了。粉色的新的仓库(`feige996`),目前正在积极维护。
[![Star History Chart](https://api.star-history.com/svg?repos=Ares-Chang/uni-vitesse,uni-helper/vitesse-uni-app,codercup/unibest,feige996/unibest,DaMaiCoding/uni-plus&type=Date)](https://www.star-history.com/#Ares-Chang/uni-vitesse&uni-helper/vitesse-uni-app&codercup/unibest&feige996/unibest&DaMaiCoding/uni-plus&Date)
## 🗂 生成过程
`unibest` 由最初始的官方 cli 脚手架模板生成,执行的命令如下:
```sh
npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project
```
`uniapp` 官方链接:[点击跳转 - quickstart-cli](https://uniapp.dcloud.net.cn/quickstart-cli.html)
在官方生成的项目基础上,增加了如下内容:
- 前端基础配置六件套
- prettier
- eslint
- stylelint
- husky
- lint-staged
- commitlint
- UnoCSS
- UnoCSS Icons
- Uni 插件
- vite-plugin-uni-pages
- vite-plugin-uni-layouts
- vite-plugin-uni-manifest
- vite-plugin-uni-platform
- UI 库(默认 `wot-ui`,支持替换其他 `UI库`)
- pinia + pinia-plugin-persistedstate
- 通用功能
- 请求封装
- 请求拦截
- 路由拦截
## ✨ 特性
- ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite](https://github.com/vitejs/vite), [pnpm](https://pnpm.io/), [esbuild](https://github.com/evanw/esbuild) - 就是快!
- 🔥 最新语法 - `<script lang="ts" setup>` 语法
- 🎨 [UnoCSS](https://unocss.dev/) - 高性能且极具灵活性的即时原子化 CSS 引擎
- 😃 [UnoCSS Icons](https://unocss.dev/presets/icons) & [icones](https://icones.js.org/) - 海量图标供你选择
- 🍍 [pinia](https://pinia.vuejs.org/) & [pinia-plugin-persistedstate](https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/) - 全端适配的全局数据管理
- 🗂 `uni.request` 请求封装 - 一键引入,快捷使用
- 📦 `路由拦截` 基础封装,支持扩展,快捷使用,拒绝黑盒
- 📥 [API 自动加载](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 无需引入
- 🎉 `v3` Code Snippets 加快你的页面生成
- 🦾 `Pritter` & `ESLint` & `Stylelint` & `husky` & `lint-staged` + `commitlint` - 保证代码质量
- 🌈 `TypeScript` 加持,同时又兼容 `js` ,同时满足不同人群
- 💡 `ES6 import` 自动排序,`css 属性` 自动排序,增强编码一致性
- 🖥 `多环境` 配置分开,想则怎么配置就怎么配置
## 📦 目录结构
通过 `tree -I node_modules -I dist -I .git -a > tree.md` 命令生成。
```txt
.
├── .editorconfig
├── .eslintignore
├── .eslintrc-auto-import.json
├── .eslintrc.cjs
├── .gitignore
├── .husky
├── .npmrc
├── .prettierignore
├── .prettierrc.cjs
├── .stylelintignore
├── .stylelintrc.cjs
├── .vscode
├── LICENSE
├── README.md
├── commitlint.config.cjs
├── env
│   ├── .env
│   ├── .env.development
│   ├── .env.production
│   └── .env.test
├── favicon.ico
├── index.html
├── manifest.config.ts
├── package.json
├── pages.config.ts
├── src
│   ├── App.vue
│   ├── components
│   │   └── .gitkeep
│   ├── env.d.ts
│   ├── hooks
│   │   ├── .gitkeep
│   │   ├── useRequest.ts
│   │   └── useUpload.ts
│   ├── interceptors
│   │   ├── index.ts
│   │   ├── prototype.ts
│   │   ├── request.ts
│   │   └── route.ts
│   ├── layouts
│   │   ├── default.vue
│   │   └── demo.vue
│   ├── main.ts
│   ├── manifest.json
│   ├── pages
│   │   ├── about
│   │   │   ├── about.vue
│   │   │   └── components
│   │   │   ├── request.vue
│   │   │   └── upload.vue
│   │   └── index
│   │   └── index.vue
│   ├── pages-sub
│   │   └── demo
│   │   └── index.vue
│   ├── pages.json
│   ├── service
│   │   └── index
│   │   └── foo.ts
│   ├── static
│   │   ├── images
│   │   │   └── .gitkeep
│   │   ├── logo.svg
│   │   └── tabbar
│   │   ├── example.png
│   │   ├── exampleHL.png
│   │   ├── home.png
│   │   ├── homeHL.png
│   │   ├── personal.png
│   │   └── personalHL.png
│   ├── store
│   │   ├── index.ts
│   │   └── user.ts
│   ├── style
│   │   └── index.scss
│   ├── types
│   │   ├── auto-import.d.ts
│   │   ├── global.d.ts
│   │   ├── shims-uni.d.ts
│   │   └── uni-pages.d.ts
│   ├── typings.ts
│   ├── uni.scss
│   ├── uni_modules
│   │   └── .gitkeep
│   └── utils
│   ├── http.ts
│   ├── index.ts
│   └── platform.ts
├── tsconfig.json
├── uni-pages.d.ts
├── uno.config.ts
└── vite.config.ts
```

163
docs/base/10-i18n.md Normal file
View File

@@ -0,0 +1,163 @@
# 多语言篇
`多语言` 是一个常见的需求, `unibest` 专门开发了一个 `i18n`模板,可以直接生成 `多语言模板项目`
```sh
pnpm create unibest my-project -t i18n
```
`vue组件` 里面使用方式如下:
```html
<view class="m-4">{{ $t('app.name') }}</view>
```
`非vue组件` 里面怎么使用呢?比如 `ts` 文件。
这时需要用到作者编写的 `translate` 函数,使用方式如下:
```ts
import { translate as t } from '@/locale/index'
/** 非vue 文件使用 i18n */
export const testI18n = () => {
console.log(t('app.name'))
// 下面同样生效
uni.showModal({
title: 'i18n 测试',
content: t('app.name'),
})
}
```
上面基本的使用都是没问题的,但是传递参数时,只有 `H5端` 生效,`其他端` 是不生效的,代码如下:
```html
<view class="m-4">{{ $t('weight', { heavy: 100 }) }}</view>
```
`H5端` 效果如下,正常显示:
![alt text](./assets/10-1.png)
`非H5端` 效果如下,异常显示:
![alt text](./assets/10-2.png)
下面我们就来处理这个问题。
## 多语言传参
上面提到 `vue-i18n``非H5端` 传参时显示异常,那我们就来处理一下,主要方式就是通过 `正则` 替换 `多语言字符串`
编写一个函数 `formatI18n`,如下:
```ts
/**
* formatI18n('我是{name},身高{detail.height},体重{detail.weight}',{name:'张三',detail:{height:178,weight:'75kg'}})
* 暂不支持数组
* @param template 多语言模板字符串eg: `我是{name}`
* @param obj 需要传递的对象里面的key与多语言字符串对应eg: `{name:'菲鸽'}`
* @returns
*/
export function formatI18n(template, data) {
const match = /\{(.*?)\}/g.exec(template)
if (match) {
const variableList = match[0].replace('{', '').replace('}', '').split('.')
let result = data
for (let i = 0; i < variableList.length; i++) {
result = result[variableList[i]] || ''
}
return formatStr(template.replace(match[0], result), data)
} else {
return template
}
}
```
`vue组件` 里面使用方式如下:
```html
<view class="m-4">{{ formatI18n(translate('introduction'), user) }}</view>
```
用到的函数引入如下:
```js
import { formatI18n, translate } from '@/locale/index'
```
对应的 en.json 文件如下:
```json
{ "introduction": "I am {name},height:{detail.height},weight:{detail.weight}" }
```
`user` 对象如下:
```js
{name:'张三',detail:{height:178,weight:'75kg'}}
```
这样,在 `H5端``非H5端` 都能正常显示,如下:
![alt text](./assets/10-3.png)
very good !
## 导航栏标题
目前发现 `导航栏标题``小程序端` 不会跟随多语言切换而切换,比如说刚开始是中文,切换成英文后,页面内容都变成英文了,标题栏还是中文。
> `App端` 说明:`App模拟器`,以我的 `mac电脑` `ios模拟器` 来说,是正常的,可以直接切换,多语言也是生效的。
>
> 但是 `安卓真机` 会出现`切换多语言后,自动重启,然后界面多语言是生效的`。
>
> 既然 `App 正常`,这里主要说 `小程序端` 不正常的处理。
`小程序端` 需要使用 `uni.setNavigationBarTitle` 来手动处理,`API` 使用如下:
```js
uni.setNavigationBarTitle({
title: '新的标题',
})
```
结合 `translate` 函数,则:
```js
uni.setNavigationBarTitle({
title: translate('app.name'),
})
```
可以满足大部分场景。
## tabbar 标题
`导航栏标题`。使用 `uni.setTabBarItem` 来手动处理。
## App 端视频
这里给出 `2``App端` 的视频,加深开发者的认识和印象。
:::details
### `ios模拟器` 多语言直接就是生效的
<video src="./assets/10-ios.mp4" controls="controls" width="300px" height="100%"></video>
### `安卓真机` 会自动重启,重启后界面多语言是生效的
<video src="./assets/10-android.mp4" controls="controls" width="300px" height="100%"></video>
:::
## 总结
本文介绍了 `unibest` 里面使用 `多语言` 的基本方式,还处理了 `3` 个多端异常的问题:
- `多语言传参` 不生效 BUG
- `导航栏标题` 切换多语言不生效 BUG
- `tabbar标题` 切换多语言不生效 BUG
全文完~

119
docs/base/11-build.md Normal file
View File

@@ -0,0 +1,119 @@
# 运行发布
## 运行
- `h5 平台` `pnpm dev:h5` 或者简单点 `pnpm dev` ),然后浏览器打开 `http://localhost:9000/`
- `wx 小程序``pnpm dev:mp-weixin`,然后打开微信开发者工具,导入本地文件夹,选择本项目的 `dist/dev/mp-weixin` 文件。
- ![alt text](./assets/11-1.png)
- `APP 平台``pnpm dev:app`,然后打开 `HBuilderX`,导入刚刚生成的 `dist/dev/app` 文件夹,选择运行到 `模拟器`( `开发时优先使用` ),或者 `运行到 ios 基座` (真机调试时使用) 。
> **如果不是 `iOS` 端 ,请查看 [App 专题章节 - 3.app 热更新](/base/18-app#_3-app-热更新)**
>
> **如果不是 `iOS` 端 ,请查看 [App 专题章节 - 3.app 热更新](/base/18-app#_3-app-热更新)**
>
> **如果不是 `iOS` 端 ,请查看 [App 专题章节 - 3.app 热更新](/base/18-app#_3-app-热更新)**
>
> 重要的事情说三遍!
![alt text](./assets/11-2.png)
![alt text](./assets/11-3.png)
![alt text](./assets/11-4.png)
> 如果需要配置其他模拟器,可以参考:[安装模拟器](https://uniapp.dcloud.net.cn/tutorial/run/installSimulator.html)
> 这样操作的话,开发时都会有热更新,开发体验很爽!
## 发布
- `h5 平台` `pnpm build:h5`,打包后的文件在 `dist/build/h5`,可以放到 web 服务器,如 nginx 运行。如果最终不是放在根目录,可以在 `manifest.config.ts` 文件的 `h5.router.base` 属性进行修改。
- `wx 小程序``pnpm build:mp-weixin`,打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
- `APP 平台``pnpm build:app`,然后打开 `HBuilderX`,导入刚刚生成的 `dist/build/app` 文件夹,选择 `发行` - `原生APP-云打包`
![alt text](./assets/11-13.png)
![alt text](./assets/11-5.png)
![alt text](./assets/11-6.png)
> 熟悉原生 APP 开发的开发者也可以使用 `原生APP-本地打包`。
## APP 打包注意事项(上)
很多开发者发现打包失败,或者打包白屏,这里简单说明一下。
- 1. 重新获取自己的 `AppId`
![alt text](./assets/11-7.png)
- 2. 根据上面获取到的 `AppId` 修改 `env/.env` 文件的 `VITE_UNI_APPID` 字段
![alt text](./assets/11-8.png)
- 3. (可选)云打包如果有出现解析时出问题的,把 `minSdkVersion` 版本改低一点就好了,比如 `21`。(最低 `21`,不能低于 `21`;我模板里面设置的是 `30`)。
![alt text](./assets/11-9.png)
## APP 打包注意事项 (下)
### `uni-app SDK` 版本
> 特别备注:`2024-05-03`,新的 `base` 模板的 `uni-app SDK` 版本已经升级到 `4.14` 了。
>
> ![alt text](./assets/11-100.png)
`2024-04-14`,新的 `base` 模板的 `uni-app SDK` 版本已经升级到 `4.08` ,记得更新您的 `HBuilderx` 版本。
`"@dcloudio/uni-app": "3.0.0-4000820240401001"` 表示 `uni-app``3.0.0` 版本,对应的 `HBuilderx` 版本为 `4.08`,后面的 `20240401001` 是发布日期。
> `40008` 第一个数字 `4` 表示主要版本,后面每 `2` 位数为一组,所以代表 `4.0.8`。
>
> 类似的,`30812` 代表 `3.8.12` 版本,`30909` 代表 `3.9.9` 版本。
>
> 另外,从 `3.99` 开始,后面 2 个小版本合并书写,于是 `3.9.9` 变成 `3.99``4.0.8` 变成 `4.08`。
`unibest` 历史用过的 `@dcloudio/uni-app` 版本:
```text
"@dcloudio/uni-app": "3.0.0-3081220230817001", => 3.8.12
"@dcloudio/uni-app": "3.0.0-3090920231225001", => 3.99
"@dcloudio/uni-app": "3.0.0-4000820240401001", => 4.08
"@dcloudio/uni-app": "3.0.0-4010420240430001", => 4.14
```
![alt text](./assets/11-10.png)
### `uni-app SDK` 版本匹配 `HBuilderX`
> 温馨提示:下面的部分是使用 `uni-app` 版本为 `3.8.12` 时写的文档,适当参考~
本模板使用的是 `3.8.12` 的库版本(`"@dcloudio/uni-app": "3.0.0-3081220230817001",`),所以尽量使用 `3.8.12` 版本的 `HBuilderX` 来打包,否则可能有未知的风险,出现情况如下图。
> 原来的图不见了,重新补了一张。
![alt text](./assets/11-11.png)
上图表示您的 `HBuilderX` 版本是 `4.08`,但是代码 `uni-app SDK` (即 `"@dcloudio/uni-app": "3.0.0-4010420240430001"` ) 是 `4.14`,版本不匹配。
- 点击 `ignore`(忽略) 后若可以正常使用,那就不用管。(可选添加如下配置)
```json
"app-plus" : { "compatible": { "ignoreVersion": true } }
```
- 如果出现白屏啥的,请更新您的 `HBuilderX``uni-app SDK` 相同版本(这里是 `4.14` )。
### 多个 `HBuilderX` 版本安装
> 温馨提示:下面的部分是使用 `uni-app` 版本为 `3.8.12` 时写的文档,适当参考~
`MAC` 可以安装多个版本的软件,如下图我安装了 `3.8.12` (3.8.12.20230817) 和最新的 `3.99` (3.99.2023122611) 两个版本,平时的项目使用 `3.99`, 打包 `unibest` 的时候使用 `3.8.12`
![alt text](./assets/11-12.png)
> `window` 系统也可以同时安装多个 `HBuilderX` 版本,安装时选择安装到不同目录下即可。
## 总结
本文描述了多端的运行和发布,希望对您有帮助。
全文完~

89
docs/base/12-env.md Normal file
View File

@@ -0,0 +1,89 @@
# 环境变量
主要介绍 `vite` 环境变量和 `uni` 环境变量。
## `vite` 环境变量
`Vite` 在一个特殊的 `import.meta.env` 对象上暴露环境变量。环境变量定义在 `.env` 文件里。
### .env 文件
创建环境文件:
```yml
.env # 所有情况下都会加载
.env.local # 所有情况下都会加载,但会被 git 忽略
.env.[mode] # 只在指定模式下加载
.env.[mode].local # 只在指定模式下加载,但会被 git 忽略
# 注意 .env.local 无法覆盖 .env.[mode]
```
环境文件只包含环境变量的 `键值对`
```yml
VITE_SOME_KEY=123
DB_PASSWORD=foobar
```
> 为了防止意外地将一些环境变量泄漏到客户端,只有以 `VITE_` 为前缀的变量才会暴露给经过 `vite` 处理的代码。
如上配置,只有 `VITE_SOME_KEY` 会被暴露为 `import.meta.env.VITE_SOME_KEY` 提供给客户端源码,而 `DB_PASSWORD` 则不会。
```js
console.log(import.meta.env.VITE_SOME_KEY) // "123"
console.log(import.meta.env.DB_PASSWORD) // undefined
```
### mode 模式
Vite 允许你为不同的构建环境指定不同的模式。通常在 `npm scripts` 里面指定 `mode` 参数:
```sh
"scripts": {
"dev": "uni",
"dev-dev": "uni --mode development",
"dev-test": "uni --mode test",
"dev-prod": "uni --mode production",
}
```
运行不同模式的脚本时,`Vite` 会自动加载对应的 `.env.[mode]` 文件,就能获取到不同的环境变量。
`Vite` 运行 `dev` 时默认会加载 `.env.development` 文件(若有)。
`Vite` 运行 `build` 时默认会加载 `.env.production` 文件(若有)。
故,如上配置 `pnpm dev``pnpm dev-dev` 是一个效果。
## `uni` 环境变量
`uni` 环境变量这里指运行 `uni` 的平台变量,通过 `vite``define` 配置可以暴露出来。
```
define: {
__UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
},
```
代码里面使用:
```js
export const platform = __UNI_PLATFORM__
export const isH5 = __UNI_PLATFORM__ === 'h5'
export const isApp = __UNI_PLATFORM__ === 'app'
export const isMp = __UNI_PLATFORM__.startsWith('mp-')
const PLATFORM = {
platform,
isH5,
isApp,
isMp,
}
export default PLATFORM
```
## 总结
本文描写了 `vite` 环境变量和 `uni` 环境变量如何配置和使用。
全文完~

74
docs/base/13-hbx.md Normal file
View File

@@ -0,0 +1,74 @@
# hbx 模板
为了方便使用 `HBuilderX` 的开发者,`unibest` 也提供 `hbx` 模板。
`hbx 模板` 适用于 `2 类用户`
- ~~使用 `uniCloud` 云开发的用户,必须使用 `hbx 版本`,因为 `uniCloud``HBuilderX` 是绑定的。~~
- 开发 `App` 的用户,可选使用 `hbx 版本`
> 现在 `base` 模板已经完全可以替代 `hbx` 模板了,所以 `hbx` 模板不再维护。
>
> 1. `base` 模板一样可以使用 `uniCloud` 云开发。
> 2. `base` 模板支持 `App` 开发,并且也可以热更新,详情请见 [APP 专区](./18-app)。
## 仓库地址
> `hbx` 目前由 `青谷` 大佬维护,微信号:`qingguxixi`[青谷 github 地址](https://github.com/Xiphin) 。
- gitee: [unibest-hbx](https://github.com/uni-run/unibest-hbx)
- github: [unibest-hbx](https://github.com/uni-run/unibest-hbx)
没有梯子的用户优先推荐使用 `gitee` 仓库,速度更快。(两个仓库会实时同步,无差别。)
## 导入项目
有 2 种方式导入项目:
-`Git` 导入...
- 从本地目录导入...
## 运行项目
此时运行菜单会提示 `本项目类型无法运行`,如下图
![alt text](./assets/13-1.png)
![alt text](./assets/13-2.png)
需要执行如下 2 步:
- 项目下执行 `pnpm i`
- 右键项目,选择 `重新识别项目类型`
![alt text](./assets/13-3.png)
## 运行效果
经过上面的操作后,就可以正常运行了。
- ios 模拟器运行效果如下:
![alt text](./assets/13-4.png)
![alt text](./assets/13-5.png)
![alt text](./assets/13-6.png)
- 微信小程序运行效果如下:
![alt text](./assets/13-7.png)
> 目前微信小程序静态资源还有点问题,如下图 `logo 不见了`,后续会修复。
![alt text](./assets/13-8.png)
> 另外还发现 `UnoCSS Icon` 不生效,原因未知。
## 总结
本文描述了 `hbx` 模板的由来,使用方式。
有需要的可以试试,但是不太建议使用。另外精力有限,该模板不再维护。
全文完~

128
docs/base/14-faq.md Normal file
View File

@@ -0,0 +1,128 @@
# 常见问题
本篇介绍一些常见的问题,会持续更新。
## 1. 如何设置/修改首页?
`vue` 文件的 `route-block` 块里面设置 `type="home"` 即可,请确保项目里面 `只有一个页面` 是这个配置。
> 注意:如果有多个,会按照字母顺序排列,第一个是首页。(可能不是您的想要的效果。)
## 2. 修改 `pages.json`、`manifest.json` 被覆盖问题
- `pages.json`
本项目引入了 `@uni-helper/vite-plugin-uni-pages``pages.json` 文件将会自动生成,手动修改 `pages.json` 将会被覆盖。
全局的东西请在 `pages.config.ts` 里面配置,页面的东西请在 `vue` 文件的 `route-block` 配置。
- `manifest.json`
与上面类似。本项目引入了 `@uni-helper/vite-plugin-uni-manifest``manifest.json` 文件将会自动生成,手动修改 `manifest.json` 将会被覆盖。
如需修改,请在 `manifest.config.ts` 里面修改。
## 3. 怎么分包?
`vite.config.ts` 里面有一个配置,如下:(其中 `subPackages` 就是用来分包的)
```ts [vite.config.ts]{3}
UniPages({
exclude: ['**/components/**/**.*'],
subPackages: ['src/pages-sub'], // 是个数组,可以配置多个
}),
```
## 4. 首次运行 `pnpm:mp` 时报错。
首次运行 `pnpm:mp` 时报错,报错如下:
```text
Error: ENOENT: no such file or directory, open '/Users/burtlai/unibest-projects/unibest/src/manifest.json'
```
首次运行 `非h5端` 时都可能出现上面的问题,需要先执行一下 `pnpm i` 以生成 `src/manifest.json` 文件,后面就不会报错了。
## 5. `git commit` 报错。
请看 `commitlint.config.ts` 里面的配置,需要满足对应的设定。根据自己的需要,可以修改 `commitlint.config.ts` 里面的配置。
如果是一次的(比如引入了某个第三方库),可以通过 `--no-verify` 参数跳过校验:
```sh
git commit -m "feat: xxx" --no-verify
```
第三方库还有另外一种处理方式,放到特定的文件夹,然后在 `.eslintignore` 和 `.styleintignore` 里面加上该文件夹。
## 6. 不想要严格的 `git` 提交检测,怎么办?
直接把 `.husky` 这个文件删掉即可。(或者不删除,只把里面的文件内容注释掉。)
## 7. `uni-app` 无法使用 `process.env` 变量,怎么办?
使用 `import.meta.env` 替代!
## 8. 如何跟随 `uni-app` 官方升级?
项目下,执行 `npx @dcloudio/uvm@latest` 即可更新。
![alt text](./assets/14-1.png)
> 注意,上面的命令会自动安装 `vue-i18n`,可以手动删除(`pnpm un vue-i18n`),也可以不理它(没多大影响)。
## 9. 如何把已经加入 `git` 管理的文件移出 `git` 管理?
- 第一步,先把文件移出`git` 管理,操作如下:
```text
# git rm -r --cached file1 file2 ## 针对某些文件
# git rm -r --cached dir1 dir2 ## 针对某些文件夹
# git rm -r --cached . ## 针对所有文件
```
- 第二步,提交 `commit` 以正式删除的文件
> 总结:`git rm -r --cached .` + `git commit` 即可。
## 10. 支付宝小程序运行报错。
- 默认运行是会报错的,如下图
![alt text](./assets/14-2.png)
- 只需要勾上 `本地开发跳过 ES5 转译` 即可正常运行,如下图
![alt text](./assets/14-3.png)
> 总结:勾上 `本地开发跳过 ES5 转译` 即可。
## 11. 支持 `uni-app x` 吗?
不支持。但我们一直保持关注。[uni-app x 传送门](https://doc.dcloud.net.cn/uni-app-x/)
目前 `unibest` 已经有 `hbx` 模板,后续接入 `uni-app x` 会很容易,坐等官方发布。
## 12. 为啥 `package.json` 中 `vue` 已经 `3.4+` 了,还不支持 `defineModel` ?
`uni-app` 官方虽然已经把 `vue` 升级到 `3.4+` 了,但是目前只有 `H5端` 支持 `defineModel`,其他端目前运行报错,详情请看 `uni-app` 官网的发布日志:
[HBuilder X - Release Notes](https://3085868976.hiecheimaetu.com:22443/qn-GO8xCsKgpKDZWIBAkVCUkI1EnGmQUMT4.update.dcloud.net.cn/hbuilderx/changelog/4.14.2024043013.html)
关键截图如下:(仅支持 `H5端`
![alt text](./assets/14-4.png)
真实运行报错截图如下:(分别是 `小程序` 和 `APP` 都会报错
![alt text](./assets/14-5.png)
![alt text](./assets/14-6.png)
## 13. `base` 模板如何接 `uniCloud` ?
- 1. 操作方案:直接在原始项目目录上右键,重新识别项目类型,就可以关联 `uniCloud` 了,然后用原始项目直接运行就可以了,不需要再 `pnpm dev:app` 后导入 `dist/dev/app` 再运行了。
- 2. 问:其他模板可以吗?答:其他模板也可以,操作同上。
- 3. 我写的文章链接:[【unibest】可以去掉 hbx 模版了base 模板一统天下](https://mp.weixin.qq.com/s?__biz=MzUxMzAwNzMwNw==&mid=2247484792&idx=1&sn=b6116198f265384e5a51bd2bd95bea90&chksm=f95a8edcce2d07caba60782e17e48d766612c0ad85c019379fd5ac37890e31b6ca7049e670f7&scene=178&cur_album_id=3438500614009782275#rd)
全文完~

284
docs/base/15-faq.md Normal file
View File

@@ -0,0 +1,284 @@
# 常见问题 2
## 1. `wot-ui` 的 `toast` + `message-box` 不生效。
- 1. `layout` 引入 `wot-ui``toast` + `message-box`
```vue [src/layouts/default.vue]
<!-- src/layouts/default.vue -->
<template>
<view>
<slot />
<wd-toast />
<wd-message-box />
</view>
</template>
```
> `unibest@2.1.0` 开始已经默认引入。
- 2.页面使用
```ts
import { useMessage } from 'wot-design-uni'
const message = useMessage()
const handleClick = () => {
// 顺便测试 message 的使用
message.show('显示隐藏切换')
}
```
## 2. `uni-app` 插件市场的插件如何使用?
`hbx` 模板可以直接引入,不在讨论范围内,下面描述的是 `普通模板`。
> 如果该插件支持 `npm` 安装,则直接安装即可,推荐统一使用 `pnpm` 安装。接着根据该插件的文档使用即可。
下面描写的是不支持 `npm` 安装的插件。
这里以 `sp-editor` 富文本插件为例,[插件地址](https://ext.dcloud.net.cn/plugin?id=14726)
- 1. 下载 `uni-app` 插件市场的代码。(居然要登录+看广告)
![alt text](./assets/15-1.png)
- 2. 解压并拷贝到 `unibest` 项目的 `uni_modules` 目录下。
![alt text](./assets/15-2.png)
- 3. 整理插件文件夹名称,把 `sp-editor_1.3.7` 改为 `sp-editor`。
> 不改会报错,因为内部代码都是用 `sp-editor` 不带版本号的。会导致查找文件失败。
![alt text](./assets/15-3.png)
- 4. 代码直接使用,无需引入组件。( `uni-app插件` 有一套规范,`uni-app` 会自动查找,跟 `easycom` 类似。)
```html
<template>
<view class="home">
<view class="editor-box">
<sp-editor
:toolbar-config="{
excludeKeys: ['direction', 'date', 'lineHeight', 'letterSpacing', 'listCheck'],
iconSize: '18px',
}"
@init="initEditor"
@input="inputOver"
@upinImage="upinImage"
@overMax="overMax"
@addLink="addLink"
@exportHtml="exportHtml"
></sp-editor>
</view>
</view>
</template>
```
完整版见下:
:::details
```vue
<route lang="json5">
{
layout: 'demo',
style: { navigationBarTitleText: '富文本' },
}
</route>
<template>
<view class="home">
<view class="editor-box">
<sp-editor
:toolbar-config="{
excludeKeys: ['direction', 'date', 'lineHeight', 'letterSpacing', 'listCheck'],
iconSize: '18px',
}"
@init="initEditor"
@input="inputOver"
@upinImage="upinImage"
@overMax="overMax"
@addLink="addLink"
@exportHtml="exportHtml"
></sp-editor>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
const editorIns = ref(null)
/**
* 获取输入内容
* @param {Object} e {html,text} 内容的html文本和text文本
*/
function inputOver(e) {
// 可以在此处获取到编辑器已编辑的内容
console.log('==== inputOver :', e)
}
/**
* 超出最大内容限制
* @param {Object} e {html,text} 内容的html文本和text文本
*/
function overMax(e) {
// 若设置了最大字数限制,可在此处触发超出限制的回调
console.log('==== overMax :', e)
}
/**
* 编辑器就绪
* @param {Object} editor 编辑器实例你可以自定义调用editor实例的方法
* @tutorial editor组件 https://uniapp.dcloud.net.cn/component/editor.html#editor-%E7%BB%84%E4%BB%B6
* @tutorial 相关api https://uniapp.dcloud.net.cn/api/media/editor-context.html
*/
function initEditor(editor) {
editorIns.value = editor // 保存编辑器实例
// 保存编辑器实例后,可以在此处获取后端数据,并赋值给编辑器初始化内容
preRender()
}
function preRender() {
setTimeout(() => {
// 异步获取后端数据后,初始化编辑器内容
editorIns.value.setContents({
html: `<div>&nbsp;&nbsp;猫猫<img src="https://img.yzcdn.cn/vant/cat.jpeg"/></div>`,
})
}, 1000)
}
/**
* 直接运行示例工程插入图片无法正常显示的看这里
* 因为插件默认采用云端存储图片的方式
* 以$emit('upinImage', tempFiles, this.editorCtx)的方式回调
* @param {Object} tempFiles
* @param {Object} editorCtx
*/
function upinImage(tempFiles, editorCtx) {
/**
* 本地临时插入图片预览
* 注意:这里仅是示例本地图片预览,因为需要将图片先上传到云端,再将图片插入到编辑器中
* 正式开发时,还请将此处注释,并解开下面 使用 uniCloud.uploadFile 上传图片的示例方法 的注释
* @tutorial https://uniapp.dcloud.net.cn/api/media/editor-context.html#editorcontext-insertimage
*/
// #ifdef MP-WEIXIN
// 注意微信小程序的图片路径是在tempFilePath字段中
editorCtx.insertImage({
src: tempFiles[0].tempFilePath,
width: '80%', // 默认不建议铺满宽度100%,预留一点空隙以便用户编辑
success: function () {},
})
// #endif
// #ifndef MP-WEIXIN
editorCtx.insertImage({
src: tempFiles[0].path,
width: '80%', // 默认不建议铺满宽度100%,预留一点空隙以便用户编辑
success: function () {},
})
// #endif
/**
* 使用 uniCloud.uploadFile 上传图片的示例方法(可适用多选上传)
* 正式开发环境中,请将上面 本地临时插入图片预览 注释后,模仿以下写法
*/
// tempFiles.forEach(async (item) => {
// uni.showLoading({
// title: '上传中请稍后',
// mask: true
// })
// let upfile = await uniCloud.uploadFile({
// filePath: item.path,
// // 同名会导致报错 policy_does_not_allow_file_overwrite
// // cloudPath可由 想要存储的文件夹/文件名 拼接若不拼文件夹名则默认存储在cloudstorage文件夹中
// cloudPath: `cloudstorage/${item.name}`,
// cloudPathAsRealPath: true
// })
// editorCtx.insertImage({
// src: upfile.fileID,
// width: '80%', // 默认不建议铺满宽度100%,预留一点空隙以便用户编辑
// success: function () {
// uni.hideLoading()
// }
// })
// })
}
/**
* 导出 - toolbar需要开启export工具
* @param {string} e 导出的html内容
*/
function exportHtml(e) {
uni.navigateTo({
url: '/pages/out/out',
success(res) {
// 传至导出页面解析即可
res.eventChannel.emit('e-transmit-html', {
data: e,
})
},
})
}
/**
* 添加超链接
* @param {Object} e { text: '链接描述', href: '链接地址' }
*/
function addLink(e) {
console.log('==== addLink :', e)
}
</script>
```
:::
## 3. Vue-Official ` vue.volar` 使用哪个版本?
`2025-05-22` 更新 :经测试,最新可用版本为 `v2.2.8` `v2.2.10` 会报错。(可以关闭 vue.volar 的自动更新)
![alt text](./assets/15-4.png)
## 4. 为啥不用 `vant-ui`
`vant-ui` 是 `WEB` 端 `UI 库`,不适用于 `uni-app`。
`uni-app` 没有 `window`, `document` 等 `WEB API`,所以凡是使用 `WEB API` 的 `框架`、`UI 库` 等都不适用于 `uni-app`。
## 4. 控制台报错 `[plugin:uni:mp-using-component] Unexpected token S in JSON at position 208`。
控制台报错如下:
![alt text](./assets/15-6.png)
原因是 `uni-pages` 这个插件最新版本 `0.2.22` 有问题,需要回退到 `0.2.20`。
![alt text](./assets/15-5.png)
执行如下命令即可:
```
pnpm add @uni-helper/vite-plugin-uni-pages@0.2.20
```
> 因为 `unibest` 在 `2.3.0(含)` 之前没有把 `pnpm-lock.yaml` 加入到版本管理,导致小版还是有细微差别。
>
> 在 `2.4.0` 开始已经加入,不会再出现这个问题。
## 5.不会 TypeScript 怎么办
不管个人还是团队、产品或者项目,从长远考虑我们都建议你学习 TypeScript因为它是未来的趋势而且大部分框架、库、插件都是用 TypeScript 开发的,足以证明它是构建一款成熟稳健产品的基石。
但考虑到实际情况,会各种客观原因存在,如果必须要用传统 JavaScript 进行开发,你可以在 `tsconfig.json` 里将 `allowJs` 设置为 `true` 即可,框架原有的 TypeScript 代码不会受到影响,并且你也可以在项目中使用 JavaScript 编写代码。
## 6.微信小程序 `INVALID_LOGIN`
微信小程序开发进入登录页时,可能导致如下问题:
```text
{errMsg: "navigateTo:fail Error: INVALID_LOGIN,
access_token expired [20250103 17:08:03][touristappid]"}
```
> 解答:游客模式会出现该错误,微信扫码登录一下就可以了。

View File

@@ -0,0 +1,17 @@
# 小程序的标识
目前有以下 `9` 种小程序标识,对应小程序平台类型如下:
| 类型 | 标识 |
| ------------ | ----------- |
| 微信小程序 | mp-weixin |
| 支付宝小程序 | mp-alipay |
| 抖音小程序 | mp-toutiao |
| 飞书小程序 | mp-lark |
| QQ小程序 | mp-qq |
| 京东小程序 | mp-jd |
| 小红书小程序 | mp-xhs |
| 百度小程序 | mp-baidu |
| 快手小程序 | mp-kuaishou |
> 注意: `mp-toutiao` 就是抖音小程序,其他的都很好辨别。

135
docs/base/17-generate.md Normal file
View File

@@ -0,0 +1,135 @@
# 自动生成代码
![alt text](17-good.png)
集成 [openapi-ts-request](https://github.com/openapi-ui/openapi-ts-request) 插件,可以根据接口文档自动生成 js,ts,uni.request,vue-query 代码。
> 好用的话记得点赞,`star` 支持下。
支持 apifox/swagger/opeanpi/yapi 等接口文档,更多配置详情请查看 [openapi-ts-request](https://github.com/openapi-ui/openapi-ts-request) 插件。
## 如何使用
你只需要将接口文档对应的接口配置url复制到根目录的 `openapi-ts-request.config.ts` 插件的配置文件中的 `schemaPath` 字段中,然后运行 `npm run openapi-ts-request` 命令,就可以生成代码。
支持同时配置多个接口文档url生成的代码默认会放在 `src/service/app` 目录下,你可以自己调整生成代码的目录。
配置如下:
```ts
import type { GenerateServiceProps } from 'openapi-ts-request'
export default [
{
schemaPath: 'http://petstore.swagger.io/v2/swagger.json',
serversPath: './src/service/app',
requestLibPath: `import request from '@/utils/request';\n import { CustomRequestOptions } from '@/interceptors/request';`,
requestOptionsType: 'CustomRequestOptions',
isGenReactQuery: true,
reactQueryMode: 'vue',
isGenJavaScript: false,
},
] as GenerateServiceProps[]
```
## 生成 ts 代码
ts 的 type 类型会默认生成在 `src/service/app/types.ts` 文件,你可以通过引入它们进行使用。
```ts
import { type Category } from '@/service/app'
const category: Category = {
id: 1,
name: '张三',
}
```
## 生成 uni.request 代码
ts 的 uni.request 客户端会默认生成在 `src/service/app` 目录下,以模块名进行分类,你可以通过引入它们进行使用。
```ts
import { getPetById } from '@/service/app'
onShow(() => {
const res = await getPetById({ id: 1 })
console.log('res: ', res)
})
```
## 生成 vue-query 代码
vue-query 的代码会默认生成在 `src/service/app` 目录下,以模块名进行分类,后缀为 `moduleName.vuequery.ts`,你可以通过引入它们进行使用。
```ts
import { useQuery } from '@tanstack/vue-query'
import { findPetsByStatusQueryOptions, usePlaceOrderMutation } from '@/service/app'
// get请求使用findPetsByStatusQueryOptions 方法为自动生成 react-query 函数
export function findPetsByStatusQueryOptions(options: {
// 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
params: API.findPetsByStatusParams;
options?: CustomRequestOptions;
}) {
return queryOptions({
queryFn: async ({ queryKey }) => {
return apis.findPetsByStatus(queryKey[1] as typeof options);
},
queryKey: ['findPetsByStatus', options],
});
}
// vue-query useQuery 默认使用
const {
data,
error
isLoading,
refetch,
} = useQuery(findPetsByStatusQueryOptions({ params: { status: ['available'] } }))
// vue-query useQuery 额外配置
const {
data,
error
isLoading,
refetch,
} = useQuery({
...findPetsByStatusQueryOptions({ params: { status: ['available'] } }),
enabled: !!token,
})
// post, delete, patch请求使用usePlaceOrderMutation 为自动生成 vue-query hook函数
export function usePlaceOrderMutation(options?: {
onSuccess?: (value?: API.Order) => void;
onError?: (error?: DefaultError) => void;
}) {
const { onSuccess, onError } = options || {};
const response = useMutation({
mutationFn: apis.placeOrder,
onSuccess(data: API.Order) {
onSuccess?.(data);
},
onError(error) {
onError?.(error);
},
});
return response;
}
// 定义请求
const { mutate, isPending } = usePlaceOrderMutation({
onSuccess: (data) => {
console.log('success data: ', data)
},
})
// 提交请求
mutate({
body: {
status: 'placed',
complete: true,
}
})
```

BIN
docs/base/17-good.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
docs/base/18-app-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

BIN
docs/base/18-app-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

BIN
docs/base/18-app-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

72
docs/base/18-app.md Normal file
View File

@@ -0,0 +1,72 @@
# App 专题
## 1. 其他端正常,`App` 白屏
请检查 `useXxxStore` 的调用,需要在函数内部调用,而不是在函数外部调用。(估计是顶层调用的时候 `pinia` 没有初始化,导致的问题,`app` 端独有的问题。)
```ts
// 错误写法
const userStore = useUserStore()
function foo() {
userStore.xxx
}
// 正确写法
function foo() {
const userStore = useUserStore()
userStore.xxx
}
```
## 2.unibest 的 `App` 模块配置
> 核心解决办法就是把 `manifest.json` 的内容搬运到 `manifest.config.ts` 中。
我们默认的的 `manifest.config.ts` 只包含了比较基础的 `uniapp` 配置,有的时候我们需要在打包 `app` 时在 `hbuilderx` 里面额外设置一些配置,那么就需要配置好后把 `manifest.json` 中的内容拷贝到 `manifest.config.ts` 中,后面运行就不会丢失了。
举例子,我在 `manifest.json` 里面配置了 2个模块配置如下
![alt text](image-18.png)
点击左侧下面的 `源码视图` 就可以看到增加了如下内容:
![alt text](image-18-2.png)
只需要把对应的内容拷贝到 `manifest.config.ts` 中的 `distribute.plugins` 里面即可。
## 3. `app` 热更新
### 3.1 `ios` 模拟器热更新
- `pnpm dev:app`
-`dist/dev/app` 文件夹导入到 `hbx编辑器` 里面,然后运行。这样在编码的时候是可以热更新的。
> 但是上面的方法在android 模拟器里面不生效。
### 3.2 `android` 热更新
-`android` 里面,把`dist/dev/app` 文件夹导入到 `hbx编辑器` 里面运行,无法热更新!!
- 需要把整个 `unibest 项目中的 src 文件夹` 导入到 `hbx编辑器` 里面,然后运行,这样就可以热更新。
- 不管是模拟器还是真机调试,都是这样。
### 3.3. `鸿蒙` 热更新
`android` 热更新。
## 4. 打包原生插件
> 思路:你把整个 `unibest项目的src` 放到 `hbx编辑器`,然后在 `src/mainifest.json` 里面配置好 `原生插件`。然后 `copy` 到`manifest.config.ts`,接着自定义打包基座。
> 注意,全程不需要用到 `pnp build dev:app` 这个命令.
步骤:
- 1. 先配置好 `原生插件`,再 `copy``manifest.config.ts`
![alt text](18-app-1.png)
- 2. 先打包自定义基座
![alt text](18-app-2.png)
- 3. 使用自定义基座
![alt text](18-app-3.png)
> 其他参看文章 [掘金教程 - Unibest 原生插件模块配置](https://juejin.cn/post/7496807547447427081)

131
docs/base/2-start.md Normal file
View File

@@ -0,0 +1,131 @@
# 快速开始
- 前置依赖
- **Node.js** - `>=v18`
- **pnpm** - `>=7.30`(推荐使用 `8.12+`
- **`VSCode`** - 可选 `WebStrom`
- **`HBuilderX`** - `APP` 的运行和发布还是离不开它
## 创建项目
通过下面的命令可以快速生成项目模板,`pnpm create unibest <项目名称>` ,如果不写 `<项目名称>` 会进入命令行交互模式。
```bash
# 如果没有 pnpm请先安装: npm i -g pnpm
pnpm create unibest my-project
# 时不时加一下 @latest 标识,这样可以使用最新版本的 create-unibest (2025-06-04 发布了 v1.18.5)
pnpm create unibest@latest my-project
```
npm 创建如下(不推荐)
:::details
如果使用 `npm`,可能有缓存,需要加上 `@latest` 标识,如果创建失败,请使用 `pnpm` 安装。
```bash
npm create unibest my-project
# 如果提示报错,或者生成的项目版本太旧,请使用下面的命令,增加 @latest 标识
npm create unibest@latest my-project
```
:::
实际操作截图如下:
![create project](./assets/2-1.png)
`create-unibest``v1.10.0` 开始会有版本号,如下:
![alt text](./assets/2-2.png)
![unibest templates](https://oss.laf.run/ukw0y1-site/xmind/unibest模板.png)
`create unibest` 支持 `-t` 参数选择模板,目前已有两大类 `8` 个模板
- `普通` 模板( `4个` ):分别是 `base``tabbar``spa``i18n``demo`
- `hbx` 模板(`2个` ):分别是 `hbx-base``hbx-demo`
不带 `-t` 参数时会默认生成 `base` 模板。
`base` 模板是最基本的模板,更新最及时,推荐使用 `base` 模板创建新项目。其他几个模板也是基于 `base` 模板得到的。 `demo` 模板则作为参考用。
`base` 模板的改动会自动同步到其他几个分支,通过 `github actions` 实现。
::: details `tabbar 模板``spa 模板` 的区别
- `tabbar` 模板里面的tabbar 路由是属于 `tabbar` 级别的,需要使用 `switchTabbar` 切换,`tabbar` 页面会有缓存,渲染性能较好。
- `spa` 模板类似于前端的 `SPA 应用``tabbar` 完全是一个组件实现的。页面之间切换是通过前端状态控制,简单灵活,不受 `tabbar` 的配置限制,但性能不如 `tabbar` 模板。
- 两者各有优点,按需选用。
:::
```sh
# VS Code 模板
pnpm create unibest my-project # 默认用 base 模板
pnpm create unibest my-project -t base # 基础模板
pnpm create unibest my-project -t tabbar # 自定义 tabbar 模板
pnpm create unibest my-project -t spa # 单页应用 模板使用一个组件模拟tabbar
pnpm create unibest my-project -t i18n # 多语言模板
pnpm create unibest my-project -t demo # 所有demo的模板(包括i18n)
# HBuilderX 模板,方便使用 uniCloud 云开发 (未来可以对接 uni-app x)
pnpm create unibest my-project -t hbx-base # hbx的base模板
pnpm create unibest my-project -t hbx-demo # hbx的demo模板包含所有的demo
```
> 2024-12-29<周日> 发表了一篇文章:[【unibest】可以去掉hbx模版了base模板一统天下](https://mp.weixin.qq.com/s/ybunFNkjKfV5yVLOMvqscg?token=1696234630&lang=zh_CN)
>
> 就是说 hbx 模板可以退出历史舞台了。
## 项目仓库地址
`github``gitee` 实时同步,代码一致。
### 普通模板:
- https://github.com/feige996/unibest
- https://gitee.com/feige996/unibest
> `demo` 模板是在 `hello-unibest` 项目中,仓库地址如下:
- https://github.com/feige996/hello-unibest
- https://gitee.com/feige996/hello-unibest
### hbx 模板
- https://github.com/uni-run/unibest-hbx
> `hbx` 目前由 `青谷` 大佬维护,微信号:`qingguxixi`[青谷 github 地址](https://github.com/Xiphin) 。
## 安装、运行
```bash [pnpm]
pnpm i
pnpm dev
# dev默认运行的是h5其他平台执行dev:<uni-platform>,如:
pnpm dev:mp-weixin
```
`pnpm dev` 之后在浏览器打开 `http://localhost:9000/`。
> 其他平台构建和发布,查看 [运行发布篇](./11-build)。
## 第一次 `commit`
```bash
git add .
git commit -m "feat: init project"
```
## `v3` 代码块
在 `vue` 文件中,输入 `v3` 按 `tab` 即可快速生成页面模板,可以大大加快页面生成。
> 原理:基于 `VSCode` 代码块生成。
![alt text](./assets/2-4.gif)
## 注意事项
- 若代码里面自动引入的 `API` 报错,只需要 `pnpm dev` 即可。
- 若代码运行后,`H5端` 浏览器界面底部没有 `tabbar` 刷新浏览器或者再次 `pnpm dev` 即可。

31
docs/base/20-best.md Normal file
View File

@@ -0,0 +1,31 @@
# 最佳实践
新项目使用 `base` 模板,可选 `tabbar` 模板。如果需要多语言,可以选 `i18n` 模板。
同时参考 `demo` 模板,可以直接 `clone` `demo` 项目,用来参考用。
![unibest templates](https://oss.laf.run/ukw0y1-site/xmind/unibest模板.png)
## 创建项目
推荐使用 `pnpm` :
```sh
# 新项目创建
pnpm create unibest my-project -t base
```
## DEMO 模板
`demo` 模版-在线地址:<https://feige996.github.io/hello-unibest/>
推荐先全部体验一下 `demo` 的示例
## 必看章节
- [介绍](/base/1-introduction)
- [快速开始](/base/2-start)
- [uni 插件](/base/3-plugin)
- [常见问题](/base/14-faq)
- [常见问题 2](/base/15-faq)
- [运行发布](/base/11-build)

158
docs/base/3-plugin.md Normal file
View File

@@ -0,0 +1,158 @@
# uni 插件
## 引言
有群友第一次看到 `unibest` 里面 `vue` 文件 `route-block` 这种写法,表示很奇怪,从来没见过!
```vue
<route lang="json5">
{
layout: 'demo',
style: {
navigationBarTitleText: '标题',
},
}
</route>
<template>
<view class="text-green-500">菲鸽你好我喜欢你</view>
</template>
```
## uni 插件总览
哈哈,这个当然是 `uni插件` 的功劳了,具体点是 `@uni-helper/vite-plugin-uni-pages` 插件的功劳,该插件由 `uni-helper` 官方团队开发。
本文就来说说 `unibest` 都引入了哪些有用的 `uni插件`。下面这个表格描述了各个插件的主要作用。
| 插件名 | 作用 |
| :----------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| @dcloudio/vite-plugin-uni | **最核心的 `uni 插件`**,没有它就不能在 vite 项目跑 uniapp其他所有的 `uni插件` 都需要经通过它的手来编译,所以写法上,都是先写 `UniXXX`,再写 `Uni`,见下文 |
| @uni-helper/vite-plugin-uni-pages | `uni 插件`,也是 `unibest 灵魂插件``route-block` 就是它的功劳,让你可以直接在本文件就能设置页面的路元信息,无需跑去 `pages.json` 配置,同时支持 `pages.config.ts` 编写 `pages.json` |
| @uni-helper/vite-plugin-uni-layouts | `uni 插件`,支持多种 `layouts` 布局,群友脑洞大开,充分利用这个特性实现平时不容易实现的布局 |
| @uni-helper/vite-plugin-uni-manifest | `uni 插件`,支持 `manifest.config.ts` 编写 `manifest.json` |
`UniXXX()` 插件都需要在 `uni()` 之前引入,因为最终都需要 `Uni` 来处理所有的代码。如下图:
![vite uni plugin](./assets/3-1.png)
接下来介绍一下 `uni 插件`,其他 `通用插件` 大家都比较熟悉,不再赘述。
`unibest` 引入了 `uni-helper` 团队的几个重要插件,少了它们 `unibest` 就缺少了灵魂,感谢 `uni-helper` 团队的贡献。`Uni 插件` 列表如下:
- `vite-plugin-uni-pages`
- 介绍:为 `Vite` 下的 `uni-app` 提供基于文件系统的路由
- 额外:使用 `TypeScript` 来编写 `uni-app``pages.json`
- 访问地址:[@uni-helper/vite-plugin-uni-pages](https://github.com/uni-helper/vite-plugin-uni-pages)
- `vite-plugin-uni-layouts`
- 介绍:为 `Vite` 下的 `uni-app` 提供类 `nuxt``layouts` 系统
- 访问地址:[@uni-helper/vite-plugin-uni-layouts](https://github.com/uni-helper/vite-plugin-uni-layouts)
- `vite-plugin-uni-manifest`
- 介绍:使用 `TypeScript` 来编写 `uni-app``manifest.json`
- 访问地址:[@uni-helper/vite-plugin-uni-manifest](https://github.com/uni-helper/vite-plugin-uni-manifest)
## vite-plugin-uni-pages
得益于 [@uni-helper/vite-plugin-uni-pages](https://github.com/uni-helper/vite-plugin-uni-pages),约定式路由(文件路由)的实现轻而易举。
`src/pages` 目录下的每个文件都代表着一个路由。要创建新页面,只需要在这个目录里新增 `.vue` 文件,插件会自动生成对应的 `pages.json` 文件。
`route` 代码块则可以配置页面相关信息,这些信息会自动同步到 `page.json`,无需切换到 `page.json` 进行配置。
> `pages.json` 文件是自动生成的,请不要手动修改,全局的东西请在 `pages.config.ts` 里面配置,页面上的东西请在 `vue` 文件的 `route` 代码块配置,如下图。
```vue [src/pages/index.vue]
<!-- 使用 type="home" 属性设置首页其他页面不需要设置默认为page -->
<!-- 推荐使用json5更强大且允许注释 -->
<route lang="json5" type="home">
{
style: {
navigationStyle: 'custom',
navigationBarTitleText: '首页',
},
}
</route>
<template>
<div>
<h1>欢迎使用 unibest</h1>
<h4>unibest 是最好的 uniapp 开发模板</h4>
</div>
</template>
```
```vue [src/pages/about.vue]
<route lang="json5">
{
style: {
navigationBarTitleText: '关于',
},
}
</route>
<template>
<view>
<view>通过 `/pages/about` 来访问这个页面</view>
</view>
</template>
```
### 设置首页
通过在 `route-block` 里面配置 `type="home"` 即可,尽量保证一个项目 `只有一个` 这个配置,如果有多个,会按照字母顺序来排列,最终可能不是您想要的效果。
### 设置 pages 过滤和分包
- 过滤:默认 `src/pages` 里面的 `vue` 文件都会生成一个页面,如果不需要生成页面可以对 `vite.config.ts` 中的 `UniPages` 进行 `exclude` 配置。
- 分包:如果需要设置 `分包` 则可以通过 `subPackages` 进行配置,该配置项是个数组,可以配置多个 `分包`,注意分包的目录不能为 `src/pages` 里面的子目录。
```ts [vite.config.ts]
UniPages({
exclude: ['**/components/**/**.*'],
subPackages: ['src/pages-sub'], // 是个数组,可以配置多个,但不能为 `src/pages` 里面的子目录
})
```
## vite-plugin-uni-layouts
得益于 [@uni-helper/vite-plugin-uni-layouts](https://github.com/uni-helper/vite-plugin-uni-layouts),你可以轻松地切换不同的布局。
`src/layouts` 文件夹下的 `vue` 文件都会自动生成一个布局,默认的布局文件名为 `default` ,路径 `src/layouts/default.vue` 。
如果需要修改使用的布局,可以通过 `vue` 文件内 `route` 代码块指定需要的布局,如下示例使用 `demo` 布局。
```vue [src/pages/demo.vue]{3}
<route lang="json5">
{
layout: 'demo',
style: {
navigationBarTitleText: '关于',
},
}
</route>
```
```vue [src/layouts/demo.vue]
<template>
<view>
<!-- 这里可以写通用的布局比如导航栏tabbar等 -->
<!-- slot里面装的就是子页面的内容 -->
<slot />
</view>
</template>
```
## vite-plugin-uni-manifest
得益于 [@uni-helper/vite-plugin-uni-manifest](https://github.com/uni-helper/vite-plugin-uni-manifest),你可以使用 `TypeScript` 来编写 `manifest.json`。
> `manifest.json` 文件是自动生成的,请不要手动修改,需要配置的内容请在 `manifest.config.ts` 里面配置。
## 总结
本文介绍了 `unibest` 引入的几个重要的 `uni插件`。
如果还想了解更多信息,可以去 `uni-helper` [github 仓库](https://github.com/uni-helper) 看看。

220
docs/base/4-style.md Normal file
View File

@@ -0,0 +1,220 @@
# 样式篇
本篇主要介绍 `UnoCSS` 的使用,以及如何与 `设计稿尺寸` 对应。
## UnoCSS
[UnoCSS](https://unocss.dev/) 是按需使用的原子 CSS 引擎,提供了良好的样式支持。
![alt text](./assets/4-1.png)
在 VSCode 中还可以预览,
![alt text](./assets/4-2.png)
![alt text](./assets/4-3.png)
> 如果原子化 `UnoCSS` 没有预览效果,请安装 `VSCode` 插件 `antfu.unocss`。
如果不记得原子类,可以查 `UnoCSS 的原子类`[UnoCSS Interactive](https://unocss.dev/interactive/),如下图
![alt text](./assets/4-4.png)
也可以查看 `tailwindcss` 的原子类,更加清晰明了,[链接 - tailwindcss](https://tailwindcss.com/),如下图:
![alt text](./assets/4-5.png)
## 常用的原子类
- 宽高内外边距: `w-2`, `h-4`, `px-6`, `mt-8`
- 前景色背景色:`text-green-400`, `bg-green-500`
- border: `border-2`, `border-solid`, `border-green-600`, `b-r-2` (注意 `border` = `border-1`,就是说边框 `1px` 时,一般简写为 `border` )
- border-radius: `rounded-full`, `rounded-6`, `rounded-sm` (不是 `br-10`, 也不是 `b-r-10`)
- line-height: `leading-10` (不是 `l-10`, 也不是 `lh-10`)
- hover: `hover:text-green-200`, `hover:bg-green-300`, `hover:border-dashed`
- flex: `flex`, `items-center`, `justify-center`, `flex-1`
## `UnoCSS` 配置
下面内容选读:
:::details
`unocss.config.ts` 文件内容如下:
```ts
// uno.config.ts
import {
type Preset,
defineConfig,
presetUno,
presetAttributify,
presetIcons,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
import { presetApplet, presetRemRpx, transformerAttributify } from 'unocss-applet'
// @see https://unocss.dev/presets/legacy-compat
import { presetLegacyCompat } from '@unocss/preset-legacy-compat'
const isMp = process.env?.UNI_PLATFORM?.startsWith('mp') ?? false
const presets: Preset[] = []
if (isMp) {
// 使用小程序预设
presets.push(presetApplet(), presetRemRpx())
} else {
presets.push(
// 非小程序用官方预设
presetUno(),
// 支持css class属性化
presetAttributify(),
)
}
export default defineConfig({
presets: [
...presets,
// 支持图标需要搭配图标库eg: @iconify-json/carbon, 使用 `<button class="i-carbon-sun dark:i-carbon-moon" />`
presetIcons({
scale: 1.2,
warn: true,
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
},
}),
// 将颜色函数 (rgb()和hsl()) 从空格分隔转换为逗号分隔更好的兼容性app端example
// `rgb(255 0 0)` -> `rgb(255, 0, 0)`
// `rgba(255 0 0 / 0.5)` -> `rgba(255, 0, 0, 0.5)`
presetLegacyCompat({
commaStyleColorFunction: true,
}) as Preset,
],
/**
* 自定义快捷语句
* @see https://github.com/unocss/unocss#shortcuts
*/
shortcuts: [['center', 'flex justify-center items-center']],
transformers: [
// 启用 @apply 功能
transformerDirectives(),
// 启用 () 分组功能
// 支持css class组合eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
transformerVariantGroup(),
// Don't change the following order
transformerAttributify({
// 解决与第三方框架样式冲突问题
prefixedOnly: true,
prefix: 'fg',
}),
],
rules: [
[
'p-safe',
{
padding:
'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
},
],
['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
],
})
/**
* 最终这一套组合下来会得到:
* mp 里面mt-4 => margin-top: 32rpx == 16px
* h5 里面mt-4 => margin-top: 1rem == 16px
*
* 另外,我们还可以推算出 UnoCSS 单位与设计稿差别4倍。
* 375 * 4 = 1500把设计稿设置为1500那么设计稿里多少pxunocss就写多少数值。
* 举个例子设计稿显示某元素宽度100px就写w-100即可。
*
* 如果是传统方式写样式,则推荐设计稿设置为 750这样设计稿1px代码写1rpx。
* rpx是响应式的可以让不同设备的屏幕显示效果保持一致。
*/
```
### UnoCSS presets
主要有 `4`个:
- `presetUno` —— `UnoCSS` 默认的预设,`H5端` 适用,`非H5端` 不支持,代码已经作区别处理。
- `presetApplet` 小程序预设,因为默认 `Unocss 预设` 是针对 `WEB` 的,如果不加以处理,会报错,比如小程序不支持 `*` 没有 `body` 等。该预设同样对 `APP` 生效。
- `presetIcons`,专门使用 `UnoCSS Icons`需要搭配图标库使用eg: `@iconify-json/carbon`, 代码编写如 `<button class="i-carbon-sun dark:i-carbon-moon" />`
- `presetLegacyCompat` 针对低端 `APP` 不认识新的函数颜色的兼容性预设,可以将颜色函数 `rgb()和hsl()` 里面空格分隔转换为逗号分隔,更好的兼容性`APP`example
> `rgb(255 0 0)` -> `rgb(255, 0, 0)`
>
> `rgba(255 0 0 / 0.5)` -> `rgba(255, 0, 0, 0.5)`
### UnoCSS shortcuts
```ts
/**
* 自定义快捷语句
* @see https://github.com/unocss/unocss#shortcuts
*/
shortcuts: [['center', 'flex justify-center items-center']],
```
可以编写一些常用的快捷类名,如上表示 `center` 就是 `flex justify-center items-center` 的组合,合理的添加快捷类名可以加快样式编写。
:::
## 设计稿尺寸
不同的编写方式,需要设置不同的设计稿尺寸,请看下文:
### 1. 传统编写方式
如果有设计稿,通常使用传统的编写 `CSS` 的方式,里面的对应尺寸规律如下。以蓝湖为例,假如设计稿宽度为 `750px`,则直接复制样式代码到 css 代码,同时把 `px` 批量替换为 `rpx` 即可。
如果设计稿不是 `750px` 可以调整蓝湖的设置,让设计稿宽度为 `750px`
> 下面为一段辅助说明文案,从 `uniapp` 官网搬运而来。
`rpx` 是相对于基准宽度的单位,可以根据屏幕宽度进行自适应。`uni-app` 规定屏幕基准宽度 `750rpx`
开发者可以通过设计稿基准宽度计算页面元素 `rpx` 值,设计稿 `1px` 与框架样式 `1rpx` 转换公式如下:
`设计稿 1px / 设计稿基准宽度 = 框架样式 1rpx / 750rpx`
换言之,页面元素宽度在 `uni-app` 中的宽度计算公式:
`750 * 元素在设计稿中的宽度 / 设计稿基准宽度`
举例说明:
若设计稿宽度为 `750px`,元素 `A` 在设计稿上的宽度为 `100px`,那么元素 `A``uni-app` 里面的宽度应该设为:`750 * 100 / 750`,结果为:`100rpx`
若设计稿宽度为 `640px`,元素 `A` 在设计稿上的宽度为 `100px`,那么元素 `A``uni-app` 里面的宽度应该设为:`750 * 100 / 640`,结果为:`117rpx`
若设计稿宽度为 `375px`,元素 `B` 在设计稿上的宽度为 `200px`,那么元素 `B``uni-app` 里面的宽度应该设为:`750 * 200 / 375`,结果为:`400rpx`
### 2. UnoCSS 编写方式
经过上一节的 `unocss.config.ts` 配置,可以得到下面的组合:
> mp 里面mt-4 => margin-top: 32rpx == 16px
>
> h5 里面mt-4 => margin-top: 1rem == 16px
我们还是把设计稿设置为 `750`,设计稿上多少 `px` 的元素,写成多少 `rpx` 即可。
元素 `A` 在设计稿上的宽度为 `100px`,则写 `w-100rpx` 即可。
就是把 `传统编写方式` 中写在 `css` 中的样式搬到了 `UnoCSS` 中。
如果要想用 `w-100` 这种方式,需要做额外的处理(待验证):
:::details
太忙了,有空再写吧。
:::
## 总结
本文主要介绍了 `UnoCSS` 的使用,以及 `unocss.config.ts` 中的一些配置项。
同时说明了设计稿在两种编写方式下的宽度的设置,分别为 `750``1500`.
最后说明一下,`原子化CSS``传统方式` 两者不是互斥的,他们是互补的,合适的地方使用合适的方式。

41
docs/base/4-style2.md Normal file
View File

@@ -0,0 +1,41 @@
# 关于使用 TailwindCSS
对于 unibest 项目使用 TailwindCSS 的评估如下:
1. **直接使用TailwindCSS的限制**
- 原生TailwindCSS是为Web设计的在小程序环境下会有兼容性问题
- 不支持rpx单位在小程序适配上有困难
- 需要额外配置postcss和purgeCSS才能在小程序工作
2. **当前UnoCSS的优势**
- 您的项目已经配置了`unocss-applet`,专门为小程序优化
- 支持rpx单位转换通过presetRemRpx
- 支持小程序属性化写法transformerAttributify
- 体积更小,按需生成样式
3. **替代方案建议**
- 保持使用UnoCSS它已经实现了Tailwind的大部分功能
- 可以通过安装`@unocss/preset-wind`来获得Tailwind风格的类名
```bash
pnpm add -D @unocss/preset-wind
```
然后在uno.config.ts中添加
```typescript
import { presetWind } from '@unocss/preset-wind'
export default defineConfig({
presets: [
presetWind(),
// 其他presets...
],
})
```
4. **结论**
在uni-app项目中UnoCSS是比原生TailwindCSS更合适的选择特别是针对小程序开发。通过`preset-wind`可以获得类似Tailwind的开发体验。

195
docs/base/5-icons.md Normal file
View File

@@ -0,0 +1,195 @@
# 图标篇
本文主要介绍了 `图标` 的使用方式,通常有以下几种方式使用图标:
- `UI 库 Icons`
- `UnoCSS Icons`
- `iconfont`
下面笔者一一介绍
## UI 库 Icons
如果您已经引入了 `UI库`,并且正好该 `UI库` 已经有你想要的 `Icons`,那直接用最方便了,无需额外引入其他库,代码也是最少的。
这里介绍几个常用 `UI库` 的图标使用。
### `uni-ui Icons`
> 注意:`uni-ui Icons` 颜色只能通过 `color` 属性设置;使用 `UnoCSS` 设置无效。
```html
<uni-icons type="contact" size="30"></uni-icons>
<uni-icons type="contact" size="30" color="red"></uni-icons>
<uni-icons type="contact" size="30" class="text-green"></uni-icons>
<uni-icons type="contact" size="30" color="red" class="text-green"></uni-icons>
```
![alt text](./assets/5-1.png)
### `wot-ui Icons`
> 注意:`wot-ui icons` 颜色可以通过 `color` 属性设置,也可以通过 `UnoCSS` 设置;同时设置时,`color` 属性优先级高。
```html
<wd-icon name="add-circle"></wd-icon>
<wd-icon name="add-circle" color="red"></wd-icon>
<wd-icon name="add-circle" class="text-green"></wd-icon>
<wd-icon name="add-circle" class="text-green" color="red"></wd-icon>
```
![alt text](./assets/5-2.png)
### `uv-ui Icons`
> 注意:跟 `uni-ui Icons` 一样,`uv-ui Icons` 的颜色只能通过 `color` 属性设置;使用 `UnoCSS` 设置无效。
```html
<uv-icon name="home"></uv-icon>
<uv-icon name="home" color="red"></uv-icon>
<uv-icon name="home" class="text-green"></uv-icon>
<uv-icon name="home" color="red" class="text-green"></uv-icon>
```
![alt text](./assets/5-3.png)
> 注意,经过检测这 `3个UI库Icons` 都不支持使用 `UnoCSS` 改变大小(优先级低被覆盖),必须使用 `size` 属性来设置大小才有效果(行内样式优先于 css 样式)。
>
> 另外,经过检测,都支持动态 `iconName`和动态 `color` ! 即下面这样的写法是生效的:
```ts
const iconName = ref<string>('contact')
const colorName = ref<string>('red')
onLoad(() => {
setTimeout(() => {
iconName.value = 'chat'
colorName.value = 'green'
}, 1000)
})
```
```html
<uni-icons :type="iconName" :color="colorName" class="text-green w-8"></uni-icons>
<!-- 其他2个UI库同样生效 -->
```
## `UnoCSS Icons`
`UnoCSS Icons` 可以方便接入 `iconify` 图标库,后者拥有 `10万+` 的海量图标,总能找到你想要的。
### 1. 安装 iconify
在使用 `iconify` 之前需要安装对应的图标库,安装格式如下:
`pnpm i -D @iconify-json/[the-collection-you-want]`
以安装 `carbon` 为例,执行 `pnpm i -D @iconify-json/carbon` 即可。
> `unibest` 已经装好了 `carbon` 图标库,可以直接使用。
### 2. 找到 iconify 想要的图标名
打开网址:<https://icones.js.org/>
- 在里面找到某个库,如 `carbon`
![alt text](./assets/5-4.png)
- 搜索想要的图表,如 `avatar`,出现的搜索结果,查看类名,也可以点击图标,会出现详情( `details` 里面)。
![alt text](./assets/5-5.png)
![alt text](./assets/5-6.png)
- 如上图( `details` 里面),拿到 `carbon:user-avatar`
### 3. 编写代码
- 代码里面 `class` 填写 `i-carbon-user-avatar`(所有的单词用中划线连接即可)并且支持改颜色。
```html
<view class="i-carbon-user-avatar text-red" />
```
![alt text](./assets/5-7.png)
> 如果图标没有预览效果,请安装 `VSCode` 插件 `antfu.iconify`。
预览效果:
![alt text](./assets/5-8.png)
### 4. 动态图标名
昨天有网友反馈,`UnoCSS Icons` 无法使用动态类名,我来试试:(我先说结论:是支持的!)
```ts
const iconName = ref<string>('i-carbon-car')
onLoad(() => {
setTimeout(() => {
iconName.value = 'i-carbon-user-avatar'
}, 1000)
})
```
```html
<view :class="iconName" />
```
一秒后会由 `i-carbon-car`(一辆车) 变成 `i-carbon-user-avatar`(一个头像),一切都是 OK 的。
### 5.再说动态图标名
有的时候类名是动态的,比如是 a+b 拼凑的,比如是后端返回的,比如是跨文件的,这时候页面是无法显示出该图标的。因为 `UnoCSS` 还不知道具体的类名是啥无法得到对应的图标。解决方案有2种
- 1. 在代码里写出完整的图标类名,并注释掉。(SFC 的任何位置都可以)
- 2. 在 `unocss.config.ts``safelist` 配置该完整类名。[unocss safelist](https://unocss.dev/config/#safelist)
## iconfont 图标库
`iconfont` 同样有海量免费的图标,同时支持上传自己的图标。公司项目通常会有自己的图标,由专业的 `UI设计师` 设计,这时通常会使用 `iconfont` 方式使用图标。
- 1. 打开`阿里巴巴矢量图标库 iconfont`,地址:[https://www.iconfont.cn/](https://www.iconfont.cn/),并登录。
- 2. 寻找需要的图标,加入项目,也可以上传自己的图标。
- 3. 图标方式选择 `Font class``项目设置` 勾选上 `base64`,否则`非H5端` 不支持,然后点击生成链接。
![alt text](./assets/5-9.png)
![alt text](./assets/5-10.png)
- 4. 把上面的 `css` 链接里面的内容写入在 `style/iconfont.css`,并引入到 `style/index.scss`
- 5. 页面上直接写 `<i class="iconfont icon-package text-red"></i>` 即可!
```html
<view class="m-4">
<text mr-2>iconfont:</text>
<i class="iconfont icon-package text-red"></i>
<i class="iconfont icon-chat text-red"></i>
<i class="iconfont icon-my text-red"></i>
</view>
```
预览如下:
![alt text](./assets/5-11.png)
> 上面的选择有疑问的可以看详细版 - [iconfont 详细版](/other/iconfont/iconfont)
## 其它图标库
其他优秀的可以免费商用的图标库:
- 字节跳动的 `IconPark`,链接 [https://iconpark.oceanengine.com](https://iconpark.oceanengine.com/)。
- 不知道谁家的 `yesicon`,链接 [https://yesicon.app](https://yesicon.app/)。
## 总结
本文介绍了 `3` 种使用图标的方式,分别是 `UI 库 Icons``UnoCSS Icons``iconfont`
- `UI 库 Icons` 颜色和大小属性都主要由 `UI 库` 本身控制,且都支持动态图标名和动态颜色。
- `UnoCSS Icons` 最省心,强烈推荐使用。
- `iconfont` 需要勾选 `Base64` 才能兼容多端。
全文完~

176
docs/base/6-svg.md Normal file
View File

@@ -0,0 +1,176 @@
# SVG 篇
上一章《五、图标篇》主要介绍了 `内置图标` 的使用,今天带给大家本地 `SVG` 图标的使用。
> 注意:`小程序` 和 `APP` 都不支持 `SVG` 标签,只能通过 `image` 的方式使用。即下面的 `image + src` 方式。
- `image + src` 方式
- `static目录` 图标
- `相对目录` 图标
- `线上地址` 图标
> PS`小程序` 和 `APP` 对 `图片` 也是这面几种方式,下面统一称为 “图片”。
## `image + src` 方式
如果图片在项目里面,根据放的位置不同,分为 2 种:`static目录`图标 `相对目录`图标。
### 1. `static目录` 图标
这种方式直接编写代码即可,如下:
```html
<image src="/static/svg/demo.svg" mode="scaleToFill" class="h-20 w-20" />
```
### 2. `相对目录` 图标
这种方式需要先引入,再使用,代码编写如下:
```html
<script lang="ts" setup>
import iconUrl from './demo.svg'
</script>
<template>
<image :src="iconUrl" mode="scaleToFill" class="h-20 w-20" />
</template>
```
### 3. `线上地址` 图标
这种方式直接使用,代码编写如下:
```html
<template>
<image src="https://xxx.com/demo.svg" mode="scaleToFill" class="h-20 w-20" />
</template>
```
## H5 额外支持的其他方式
> `SvgComponent` 方式 和 `SvgIcon` 方式,仅 `H5端` 适用,感兴趣的可以阅读下。
> 因为只有 `H5端` 支持,所以 unibest 没有引入这些,但是其他 `web` 项目可以参考。
:::details
### `SvgComponent` 方式
`Web端` 过来的同学都知道 `SvgComponent` 这种方式,只需要引入 `vite-svg-loader` 插件即可,支持 `3种` 方式引入 `svg`: `url`, `raw`, `component`
- URL
SVGs can be imported as URLs using the `?url` suffix:
```js
import iconUrl from './my-icon.svg?url'
// 'data:image/svg+xml...'
```
Used in template:
```html
<template>
<image :src="iconUrl" mode="scaleToFill" class="h-20 w-20" />
</template>
```
- Raw
SVGs can be imported as strings using the `?raw` suffix:
```js
import iconRaw from './my-icon.svg?raw'
// '<?xml version="1.0"?>...'
```
Used in template:
```html
<template>{{ iconRaw }}</template>
```
- Component
SVGs can be explicitly imported as Vue components using the `?component` suffix:
```js
import IconComponent from './my-icon.svg?component'
// <IconComponent />
```
Used in template:
```html
<template>
<IconComponent />
</template>
```
但是目前经过测试,只有 `url` 的方式所有端可以使用,与上面的 `image + src - 相对目录 图标` 是一个效果。至于 `component` 只有 `H5端生效`,其他端不行。
### `SvgIcon` 方式
`Web端` 过来的同学都知道 `SvgIcon` 这种方式,只需要引入 `vite-plugin-svg-icons` 插件 + `vite 配置`,再编写一个通用的 `SvgIcon` 即可,但是同样只有 `H5端生效`,其他端不行。
`vite` 配置如下:
```
createSvgIconsPlugin({
// 指定要缓存的文件夹
iconDirs: [path.resolve(process.cwd(), 'src/assets')],
// 指定symbolId格式
symbolId: 'icon-[dir]-[name]',
}),
```
如上,只需要把 `svg` 放到 `src/assets` 目录即可。
`SvgIcon` 代码如下:
```html
<template>
<svg aria-hidden="true">
<use :href="symbolId" :fill="color" />
</svg>
</template>
<script lang="ts" setup name="SvgIcon">
const props = withDefaults(
defineProps<{
prefix?: string
name: string
color?: string
}>(),
{
prefix: 'icon',
name: '',
color: '#333',
},
)
const symbolId = computed(() => `#${props.prefix}-${props.name}`)
</script>
```
使用方式如下:
```html
<!-- src/assets/demo.svg -->
<SvgIcon name="demo" class="h-20 w-20"></SvgIcon>
<!-- src/assets/dir/demo.svg -->
<SvgIcon name="dir-demo" class="h-20 w-20"></SvgIcon>
```
> `SvgComponent` 依赖 `vite-svg-loader` 插件
>
> `SvgIcon` 依赖 `vite-plugin-svg-icons` 插件
:::
## 总结
适用于跨端的 `svg 和 图片` 的使用方式,只有 `image + src` 的方式。其他方式只能用于 `web` 端,仅供参考。
全文完~

124
docs/base/7-ui.md Normal file
View File

@@ -0,0 +1,124 @@
# UI 库替换篇
## 2025-06-13 更新
因为 `base` 模板加了登录相关的功能,这些功能都是使用 `wot-ui` 开发的,替换起来会很麻烦。如果想要替换成其他 `UI 库`,可以使用 `base-sard-ui` 模板,这个模板是最近添加的。因为最近 `sard-uniapp` 比较火,有群友想要,我就整理了一份最基础的出来,不带登录功能,所以用它来替换成其他 UI 库最合适最方便。
## 默认 UI 库
`unibest` 经过几次更迭,先后使用 `uni-ui``uv-ui`作为默认 UI 库,目前使用 `wot-ui` 为默认 UI 库。
`wot-ui``vue3+ts` 编写的全端支持的 UI 库,编码体验比 `uv-ui` 更好;而官方维护的 `uni-ui` 则样式略丑,组件较少,故弃之。
> `wot-ui` 全称 `wot-design-uni`,是 `wot-design` 的 `uniapp` 版本,文档地址:[https://wot-design-uni.netlify.app/](https://wot-design-uni.netlify.app/).
---
很多群友反馈有其他 `UI` 库的需求,那么更换 `UI 库` 需要哪些步骤呢?
- 先卸载原有的 `wot-ui`
- 再安装其他 `UI 库`
下面我们简单描述一下更换 2 个主流 `UI库` —— `uni-ui` + `uv-ui` 的过程。
> 当然也支持同时存在多个 `UI 库`,有 ES 摇树特性,不必担心打包后的体积。
## 卸载 wot-ui 库
卸载 `wot-ui` 过程如下:
- 1. 删除 `wot-ui` 库:
```sh
pnpm un wot-design-uni
```
- 2. `pages.config.ts` 文件 `easycom.custom` 删除相关配置:
```diff
easycom: {
autoscan: true,
custom: {
- '^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
},
},
```
- 3. ` tsconfig.json` 文件 `compilerOptions.types` 删除相关配置:
```diff
"types": [
"@dcloudio/types",
"@types/wechat-miniprogram",
- "wot-design-uni/global.d.ts",
"./components.d.ts",
"./global.d.ts"
]
```
## 安装 `uni-ui` 库
- 1. 安装 `uni-ui` 库:
```sh
pnpm add @dcloudio/uni-ui
```
- 2. `pages.config.ts` 文件 `easycom.custom` 添加相关配置:
```diff
easycom: {
autoscan: true,
custom: {
+ '^uni-(.*)': '@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue',
},
},
```
- 3. ` tsconfig.json` 文件 `compilerOptions.types` 添加相关配置:
```diff
"types": [
"@dcloudio/types",
"@types/wechat-miniprogram",
+ "@uni-helper/uni-ui-types",
"./components.d.ts",
"./global.d.ts"
]
```
## 安装 `uv-ui` 库
- 1. 安装 `uv-ui` 库:
```sh
pnpm add @climblee/uv-ui
```
- 2. `pages.config.ts` 文件 `easycom.custom` 添加相关配置:
```diff
easycom: {
autoscan: true,
custom: {
+ '^uv-(.*)': '@climblee/uv-ui/components/uv-$1/uv-$1.vue',
},
},
```
- 3. ` tsconfig.json` 文件 `compilerOptions.types` 添加相关配置:
```diff
"types": [
"@dcloudio/types",
"@types/wechat-miniprogram",
+ "@ttou/uv-typings/shim",
+ "@ttou/uv-typings/v2",
"./components.d.ts",
"./global.d.ts"
]
```
> 其他 UI 库的安装类似,不再赘述。
全文完~

252
docs/base/8-request.md Normal file
View File

@@ -0,0 +1,252 @@
# 请求篇
本篇分为三块内容:
- 普通请求
- 图片上传
- 多后台地址
## 普通请求
普通请求分 2 种处理,一种是只在页面请求一次的一次性请求,这种请求占大多数;一种是项目多处用到的请求,这种请求占小部分,需要单独编写一个请求函数放到 `api文件夹` or `service文件夹`
> `unibest` 里面是使用 `service文件夹` 后面不再说明。
下面来分别演示:
### 一次性请求
`template` 部分编码如下:
```html
<template>
<button @click="run">请求</button>
<view v-if="loading" class="text-blue h-10">请求中...</view>
<view v-if="error" class="text-red h-10">请求错误</view>
<view v-else class="text-green h-10">{{ JSON.stringify(data) }}</view>
</template>
```
`script` 部分使用 `菲鸽` 封装好的 `useRequest` 即可实现请求状态一体化,如下:
```ts
<script setup>
type IFooItem = { name: string }
const { loading, error, data, run } = useRequest<IFooItem>(() => httpGet('/foo', { name: '菲鸽' }))
</script>
```
看吧,使用非常简单。
### 重复性请求
`重复性请求``一次性请求``html部分` 是一样的,唯一的区别是 `请求函数` 放到了 `service文件夹`,如下所示:
```ts
<script setup>
import { getFooAPI, IFooItem } from '@/service/index/foo' // 看这里
const { loading, error, data, run } = useRequest<IFooItem>(() => getFooAPI('菲鸽'))
</script>
```
对应的 `src/service/index/foo.ts` 文件如下:
```ts
import { http, httpGet } from '@/utils/http'
export interface IFooItem {
id: string
name: string
}
/** GET 请求 */
export const getFooAPI = (name: string) => {
return http<IFooItem>({
url: `/foo`,
method: 'GET',
query: { name },
})
}
/** GET 请求 - 再次简化,看大家是否喜欢这种简化 */
export const getFooAPI2 = (name: string) => {
return httpGet<IFooItem>('/foo', { name })
}
```
依然非常简洁,深受妹子喜爱。
> 完成范例如下
```ts
/** GET 请求 */
export const getFooAPI = (name: string) => {
return http.get<IFooItem>('/foo', { name })
}
/** GET 请求;支持 传递 header 的范例 */
export const getFooAPI2 = (name: string) => {
return http.get<IFooItem>('/foo', { name }, { 'Content-Type-100': '100' })
}
/** POST 请求 */
export const postFooAPI = (name: string) => {
return http.post<IFooItem>('/foo', { name })
}
/** POST 请求;需要传递 query 参数的范例微信小程序经常有同时需要query参数和body参数的场景 */
export const postFooAPI2 = (name: string) => {
return http.post<IFooItem>('/foo', { name })
}
/** POST 请求;支持 传递 header 的范例 */
export const postFooAPI3 = (name: string) => {
return http.post<IFooItem>('/foo', { name }, { name }, { 'Content-Type-100': '100' })
}
```
## 图片上传
`template` 部分编码如下:
```html
<template>
<view class="p-4 text-center">
<wd-button @click="run">选择图片并上传</wd-button>
<view v-if="loading" class="text-blue h-10">上传...</view>
<template v-else>
<view class="m-2">上传后返回的图片地址:</view>
<view class="m-2">{{ data }}</view>
<view class="h-80 w-full">
<image v-if="data" :src="data" mode="scaleToFill" />
</view>
</template>
</view>
</template>
```
`script` 部分使用 `菲鸽` 封装好的 `useUpload` 即可实现请求状态一体化,如下:
```ts
<script lang="ts" setup>
const { loading, data, run } = useUpload<string>({ user: '菲鸽' })
</script>
```
使用非常简单,深受汉子和妹子的喜爱。
## 多后台地址
上面的 `普通请求` 默认是只有一个请求地址的,在 `.env` 里面配置 `VITE_SERVER_BASEURL`,如下:
```text
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
```
并且在 `src/interceptors/request.ts` 里面有设置:
- 如果是 `http` 开头的请求路径,则直接请求
- 如果不是,则拼接上 `VITE_SERVER_BASEURL`
![alt text](./assets/8-1.png)
但在多后台地址时就不能这么玩了,需要处理如下:(关注上图的箭头部分)
```ts
// 可以写一个映射对象,如:
const proxyMap = {
cms:'http://localhost:8080/cms',
ums:'http://localhost:8080/ums',
}
// 拦截器部分(上图箭头部分)修改如下
Object.keys(proxyMap).forEach(key=>{
if(options.url.startsWith(`/${key}`)){
options.url = proxyMap[key] + options.url
}
}
// 接口调用的地方使用如下格式:
export const getFooAPI = (name: string) => {
return http<IFooItem>({
url: `/cms/foo`, // 看这里,前缀不用!!!
method: 'GET',
query: { name },
})
}
```
## 支持header传递
目前v2.6.2)已经支持 `header` 了,具体使用方法如下:(最后一个参数就是 `header`,不需要不用传,需要才传。)
```ts
/** GET 请求 */
export const getFooAPI = (name: string) => {
return http.get<IFooItem>('/foo', { name }, { 'Content-Type': 'multipart/form-data' })
}
/** POST 请求 */
export const postFooAPI = (name: string) => {
return http.post<IFooItem>('/foo', { name }, { name }, { 'Content-Type': 'multipart/form-data' })
}
```
低于v2.6.2版本需要手动设置header具体使用方法如下(`utils/http.ts`)
```diff
/**
* GET 请求
* @param url 后台地址
* @param query 请求query参数
+ * @param header 请求头默认为json格式
* @returns
*/
export const httpGet = <T>(
url: string,
query?: Record<string, any>,
+ header?: Record<string, any>,
) => {
return http<T>({
url,
query,
method: 'GET',
+ header,
})
}
/**
* POST 请求
* @param url 后台地址
* @param data 请求body参数
* @param query 请求query参数post请求也支持query很多微信接口都需要
+ * @param header 请求头默认为json格式
* @returns
*/
export const httpPost = <T>(
url: string,
data?: Record<string, any>,
query?: Record<string, any>,
+ header?: Record<string, any>,
) => {
return http<T>({
url,
query,
data,
method: 'POST',
+ header,
})
}
```
## 环境变量配置
- `普通请求` 需要在 `.env` 里面配置 `VITE_SERVER_BASEURL`,用在 `src/interceptors/request.ts` 文件拼接请求地址;而 `多后台地址` 时则用不上,可以删除。
```text
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
```
- `图片上传` 需要在 `.env` 里面配置 `VITE_UPLOAD_BASEURL`:
```text
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
```
全文完~

165
docs/base/9-state.md Normal file
View File

@@ -0,0 +1,165 @@
# 状态篇
本文主要介绍了全局状态管理 `pinia` 和 简单状态 `ref` + `reactive`
## pinia
`unibest` 已经内置了 `Pinia` + `pinia-plugin-persistedstate`(数据持久化插件),并提供了开箱即用的示例。
### 兼容性处理
本身 `pinia-plugin-persistedstate` 是不支持 `uniapp` 的,但是 `pinia-plugin-persistedstate` 提供了修改 `storage` 存储 API 的方式(默认是 `localStorage`,是一个 `WEB API``非H5端` 不支持),目前 `unibest` 已经处理好了。关键代码如下:
```ts
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化
const store = createPinia()
store.use(
createPersistedState({
storage: {
getItem: uni.getStorageSync, // 看这里
setItem: uni.setStorageSync, // 看这里
},
}),
)
```
### 定义 `pinia` 全局状态
`src/store/xxx.ts` 里面编写代码,如下是 `src/store/count.ts` 文件。
注意 `defineStore` 第三个参数可以设置是否需要持久化,默认不需要。
```ts [src/store/count.ts]{26}
import { defineStore } from 'pinia'
import { ref } from 'vue'
export const useCountStore = defineStore(
'count',
() => {
const count = ref(0)
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const reset = () => {
count.value = 0
}
return {
count,
decrement,
increment,
reset,
}
},
{
// 如果需要持久化就写 true, 不需要持久化就写 false或者去掉这个配置项
persist: true,
},
)
```
> 请不要随意把数据丢到 `pinia`,能不用就不用。简单状态尽量使用 `ref` 或者 `reactive`。
### 使用 `pinia` 全局状态
在 `vue` 文件中就可以使用了,如下是 `src/pages/demo.vue` 文件:
```vue
<template>
<view class="flex justify-center items-center text-blue-500 mt-4 mb-4">
<view class="w-20">Count: {{ countStore.count }}</view>
<button class="ml-2 mr-2" @click="countStore.decrement">-1</button>
<button class="ml-2 mr-2" @click="countStore.increment">+1</button>
<button class="ml-2 mr-2" @click="countStore.reset">重置</button>
</view>
</template>
<script lang="ts" setup>
import { useCountStore } from '@/store'
const countStore = useCountStore()
</script>
```
## 简单状态
你可以直接使用 `Vue` 提供的 `ref` 或 `reactive` 方法来做简单状态管理。
### ref
如下是 `src/pages/demo/useCount.ts` 文件,定义简单状态。
```ts [src/pages/demo/useCount.ts]
// 全局状态
const globalCount = ref(1)
export function useCount() {
// 本地状态
const localCount = ref(1)
function increment() {
globalCount.value++
localCount.value++
}
return {
globalCount,
localCount,
increment,
}
}
```
如下是 `src/pages/demo/index.vue`,与 `ref` 简单状态文件放到同一个目录下,方便管理。
```vue [src/pages/demo/index.vue]
<script setup lang="ts">
import useCount from './useCount.ts'
const { globalCount, localCount, increment } = useCount()
</script>
<template>
<button @click="increment()">
{{ globalCount }}
{{ localCount }}
</button>
</template>
```
## reactive
`reactive` 与 `ref` 类似。
如下是 `src/pages/demo/count.ts` 文件,定义状态。
```ts [src/pages/demo/count.ts]
export const countStore = reactive({
count: 0,
increment() {
this.count++
},
})
```
如下是 `src/pages/demo/index.vue`,与 `reactive` 简单状态文件放到同一个目录下,方便管理。
```vue [src/pages/demo/index.vue]
<script setup lang="ts">
import { countStore } from './count.ts'
</script>
<template>
<button @click="countStore.increment()">
{{ countStore.count }}
</button>
</template>
```
## 总结
本文介绍了 `unibest` 里面状态管理的 `2` 种方式:`pinia` 全局状态 和 `ref\reactive` 简单状态,分别演示了如何定义状态和使用状态。
注意需要灵活使用 `pinia` 和 `简单状态`,局部的状态尽量使用 `简单状态` 的方式来处理,减少 `pinia` 里面全局变量的数量。
全文完~

BIN
docs/base/assets/1-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

BIN
docs/base/assets/10-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
docs/base/assets/10-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
docs/base/assets/10-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

BIN
docs/base/assets/10-ios.mp4 Normal file

Binary file not shown.

BIN
docs/base/assets/11-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
docs/base/assets/11-10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

BIN
docs/base/assets/11-100.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/base/assets/11-11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

BIN
docs/base/assets/11-12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

BIN
docs/base/assets/11-13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
docs/base/assets/11-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
docs/base/assets/11-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
docs/base/assets/11-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
docs/base/assets/11-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/base/assets/11-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
docs/base/assets/11-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

BIN
docs/base/assets/11-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 888 KiB

BIN
docs/base/assets/11-9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 KiB

BIN
docs/base/assets/13-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
docs/base/assets/13-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
docs/base/assets/13-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
docs/base/assets/13-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
docs/base/assets/13-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/base/assets/13-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

BIN
docs/base/assets/13-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

BIN
docs/base/assets/13-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

BIN
docs/base/assets/14-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
docs/base/assets/14-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
docs/base/assets/14-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
docs/base/assets/14-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
docs/base/assets/14-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

BIN
docs/base/assets/14-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

BIN
docs/base/assets/15-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
docs/base/assets/15-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/base/assets/15-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/base/assets/15-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

BIN
docs/base/assets/15-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
docs/base/assets/15-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
docs/base/assets/2-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

BIN
docs/base/assets/2-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
docs/base/assets/2-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
docs/base/assets/2-4.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 648 KiB

BIN
docs/base/assets/3-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

BIN
docs/base/assets/4-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

BIN
docs/base/assets/4-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
docs/base/assets/4-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

BIN
docs/base/assets/4-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

BIN
docs/base/assets/4-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 KiB

BIN
docs/base/assets/5-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/base/assets/5-10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
docs/base/assets/5-100.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
docs/base/assets/5-11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
docs/base/assets/5-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
docs/base/assets/5-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
docs/base/assets/5-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

BIN
docs/base/assets/5-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
docs/base/assets/5-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

BIN
docs/base/assets/5-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
docs/base/assets/5-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
docs/base/assets/5-9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
docs/base/assets/8-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

BIN
docs/base/image-18-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

BIN
docs/base/image-18.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 KiB

BIN
docs/base/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
docs/base/ui/image-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
docs/base/ui/image-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
docs/base/ui/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

126
docs/base/ui/ui.md Normal file
View File

@@ -0,0 +1,126 @@
# UI 库选型篇
## 背景
`unibest` 作为最好的 `uniapp` 开发模板,那 `UI 框架` 的选择也是要仔细斟酌的。
`unibest` 作为 `vue3` 项目,`vue2` 时代的 `uview` 就不考虑在内了。但是在 `uview` 的基础上衍生出来的支持 `vue3``uview 系``ui框架` 还有不少,而且热度很高。
官方维护的 `uni-ui`,支持全端,而且有类型提示,但样式略丑,且其他优秀的 `UI 库` 已经包含了 `uni-ui` 的组件,所以直接用第三方 `UI 库` 就好了。
> tip1: `uni-ui` 本身是 `js` 开发的,但是官方提供了完备的类型提示( by `@uni-helper/uni-ui-types`)所以看起来就像是 `ts` 开发的一样,开发体验很好。所有的组件都有提示,很方便,很贴心。
> tip2: 再次重申一下 `uview` 不支持 `Vue3`,不然又有人问我为啥不用 `uview`。(臣妾做不到啊~
## UI 库总览
经过搜寻了一番,目前参加对比的 UI 框架有:
- uv-ui (uveiw 系) - [文档地址](https://www.uvui.cn/)
- uview-plus (uveiw 系) - [文档地址](https://uiadmin.net/uview-plus/)
- Wot Design Uni (wot 系) - [文档地址](https://wot-design-uni.netlify.app/)
- TuniaoUI (图鸟系) - [文档地址](https://vue3.tuniaokj.com/zh-CN/)
- Sard uniapp Sard系 - [文档地址](https://sard.wzt.zone/sard-uniapp-docs/)
还有 2 个 UI 框架也很优秀,大部分组件开源免费,还有一部分组件是收费的,有需要的可以看看。
- FirstUI [文档链接](https://doc.firstui.cn/)
- ThorUI [文档链接](https://thorui.cn/doc/)
> 温馨提示:收费没有对错,只要做得好,提供优质的组件,自然有用户买单。
---
下面通过几个方面对 `UI 库` 进行对比
## 开源热度
截止到 `2024-05-30` 发表文章时的数据:
| UI 框架 | uv-ui | uview-plus | wot-ui | TuniaoUI |
| ------------ | :---: | :--------: | :----: | :------: |
| github stars | 568 | 362 | 492 | 192 |
| gitee stars | 555 | 126 | 35 | - |
| github forks | 1.1k | 158 | 188 | 20 |
| gitee forks | 75 | 4 | 30 | - |
其实到这里就一决高下了,`github star 数` `uv-ui(568)` > `wot-ui(492)` > `uview-plus(362)` > `TuniaoUI(192)`,其中 `uv-ui``wot-ui` 拔得头筹。
[![Star History Chart](https://api.star-history.com/svg?repos=Moonofweisheng/wot-design-uni,climblee/uv-ui,ijry/uview-plus,tuniaoTech/tuniaoui-rc-vue3-uniapp&type=Date)](https://star-history.com/#Moonofweisheng/wot-design-uni&climblee/uv-ui&ijry/uview-plus&tuniaoTech/tuniaoui-rc-vue3-uniapp&Date)
源码仓库地址展示如下_纯粹为了方便大家查阅_ (虽然大概率你们也不会去访问,/手动狗头)
| UI 框架 | 文档地址 | github | gitee |
| ---------- | ------------------------------------- | ------------------------------------------------------- | ------------------------------------------------- |
| uv-ui | <https://www.uvui.cn/> | <https://github.com/climblee/uv-ui> | <https://gitee.com/climblee/uv-ui> |
| uview-plus | <https://uiadmin.net/uview-plus/> | <https://github.com/ijry/uview-plus> | <https://gitee.com/uiadmin/uview-plus> |
| wot-ui | <https://wot-design-uni.netlify.app/> | <https://github.com/Moonofweisheng/wot-design-uni> | <https://gitee.com/wot-design-uni/wot-design-uni> |
| TuniaoUI | <https://vue3.tuniaokj.com/zh-CN/> | <https://github.com/tuniaoTech/tuniaoui-rc-vue3-uniapp> | - |
> 接着奏乐接着舞,我们继续正文 ^\_^
## 多端支持情况
| UI 框架 | uv-ui | uview-plus | wot-ui | TuniaoUI |
| ------------ | ----- | ---------- | ------ | -------- |
| h5 | ✅ | ✅ | ✅ | ✅ |
| app(ios) | ✅ | ✅ | ✅ | ✅ |
| app(android) | ✅ | ✅ | ✅ | ✅ |
| 微信小程序 | ✅ | ✅ | ✅ | ✅ |
| 支付宝小程序 | ✅ | ✅ | ✅ | ✅ |
| QQ 小程序 | ✅ | ✅ | ❌ | ❌ |
| 百度小程序 | ✅ | ✅ | ❌ | ❌ |
| 头条小程序 | ✅ | ✅ | ❌ | ❌ |
## 组件数量
| UI 框架 | uv-ui | uview-plus | wot-ui | TuniaoUI |
| -------- | :---: | :--------: | :----: | :------: |
| 总数 | 67 | 67 | 71 | 55 |
| 基础组件 | 8 | 11 | 8 | 5 |
| 表单组件 | 16 | 17 | 20 | 14 |
| 数据组件 | 13 | 4 | 18 | 4 |
| 反馈组件 | 8 | 10 | 16 | 8 |
| 布局组件 | 7 | 9 | - | 8 |
| 导航组件 | 8 | 8 | 9 | 5 |
| 其他组件 | 7 | 8 | - | 5 |
| 内容组件 | - | - | - | 6 |
组件数:`wot(71)` > `uv-ui(67)` = `uview-plus(67)` > `TuniaoUI(55)`
## `ts` 支持情况
查看 4 个组件库的源码,可以了解到:
- `uv-ui``uView-plus` 都是 `js` 写的,并非 `ts`,可以通过 `ttou/uv-typings` 提供类型支持。
- `wot``TuniaoUI` 都是 `ts` 写的,编码体验会好很多。
> 小知识:代码里如何辨别一个库是否有 ts 支持,写代码的时候按 `ctrl + i` (Mac 里 `cmd + i`),如果有提示就是有,啥都没有就是没有。
>
> 举个例子,编写 `<xx-button type="" ...`,在 `type=""` 双引号里面按 `ctrl + i`,看提示就知道了。
- `wot` 有提示
![alt text](image.png)
- ~~`uv-ui` 无提示~~
![alt text](image-1.png)
`tsconfig.json` 文件里面 `types` 里面的 `@ttou/uv-typings/v3` 改为 `@ttou/uv-typings/v2` 就正常了(也是群友发现的),如下。
![alt text](image-2.png)
## `wot-ui` 和 `uv-ui` 皇城 `PK`
`wot-ui``uv-ui` 皇城 `PK`
[![Star History Chart](https://api.star-history.com/svg?repos=Moonofweisheng/wot-design-uni,climblee/uv-ui&type=Date)](https://star-history.com/#Moonofweisheng/wot-design-uni&climblee/uv-ui&Date)
目前 `wot-ui` 还是比不过 `uv-ui` 的,但是我 `wot-ui` 有反超的势头。主要是看了源码后,还是选定了 `vue3+ts` 编写的 `wot-ui`
> 别说我偏心,两位 `ui` 框架的作者都是我的好友,我是 `uv-ui` 群的管理员,`wot-ui` 作者在我的大群里面。选择 `wot-ui` 确实因为它很优秀。
## 总结
很高兴我们已经为宇宙最强 `uniapp` 开发模板 `unibest` 选好了 `UI 组件库``wot-ui` 是最终的幸运儿。为此我特意去 `wot-ui` 官网里面捐赠了一杯咖啡钱给作者,开源不易,要支持一下。