uni-shop-vue3/笔记.md
2025-05-15 16:48:32 +08:00

1691 lines
50 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# uni-app
> 基于 `vue` 语法的 `全端开发框架`
官网: https://uniapp.dcloud.io/
## 创建项目
https://uniapp.dcloud.net.cn/quickstart-cli.html
vue 3:
1. 创建项目
```sh
npx degit 'dcloudio/uni-preset-vue#vite-ts' uni-shop # zsh中#不用引号包起来会报错
# 直接下载模板的话在 https://github.com/dcloudio/uni-preset-vue/tree/vite-ts
pnpn i
pnpm add -D sass
```
2. 运行项目 `pnpm run dev:mp-weixin`
3. 使用微信小程序开发者工具来导入和预览项目 `dist/dev/mp-weixin`
==注意: 只能修改uni-app项目的文件, 不能修改编译后的小程序文件==
旧项目更新编译器主要依赖:
```sh
npx @dcloudio/uvm@latest
```
## 小程序的注意项
- 标签: div 和 ul 和 li 等改为 `view`、span 改为 `text`、a 改为 `navigator`、img 改为 `image`
- 不能使用浏览器对象: 比如document、xmlhttp、cookie、window、location、navigator、localstorage、websql、indexdb、webgl等对象
- style 不需要设置 soped, 多页面样式互相隔离
- css 不能使用 \* 选择器, body的元素选择器请改为page
- [微信小程序当前bug列表](https://developers.weixin.qq.com/community/develop/issueList?type=%E4%BF%AE%E5%A4%8D%E4%B8%AD&block=bug)
## 目录结构
```
├─pages 业务页面文件存放的目录
│ └─index
│ └─index.vue index页面
├─static 存放应用引用的本地静态资源的目录(注意:静态资源只能存放于此)
├─unpackage 非工程代码,一般存放运行或发行的编译结果
├─index.html H5端页面
├─main.js Vue初始化入口文件
├─App.vue 配置App全局样式、监听应用生命周期
├─pages.json 配置页面路由、导航栏、tabBar等页面类信息
├─manifest.json 配置appid、应用名称、logo、版本等打包信息
└─uni.scss uni-app内置的常用样式变量
```
## pages.json 页面路由
> 对 uni-app 进行全局配置决定页面文件的路径、窗口样式、原生的导航栏、底部的原生tabbar 等
https://uniapp.dcloud.net.cn/collocation/pages.html
`src\pages.json`
```json
{
// https://uniapp.dcloud.io/collocation/pages
// 页面路由
// pages数组中第一项表示应用启动页
"pages": [
{
"path": "pages/index/index",
// 页面样式
"style": {
"navigationStyle": "custom", // 隐藏顶部默认导航
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/my/index",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "我的"
}
}
],
// 全局样式
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
```
### 导航栏
```json
"navigationStyle": "custom", // 隐藏顶部默认导航
```
https://ask.dcloud.net.cn/article/34921
### easycom 组件自动导入
```json
"easycom": {
// 是否开启自动扫描
"autoscan": true,
// 以正则方式自定义组件匹配规则
"custom": {
// uni-ui 规则如下配置
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
// 以 Jbc 开头的组件,在 components 文件夹中查找引入(需要重启服务器)
"^Jbc(.*)": "@/components/Jbc$1.vue"
}
},
```
### 底部标签栏
https://uniapp.dcloud.net.cn/collocation/pages.html#tabbar
```json
"tabBar": {
"color": "#333",
"selectedColor": "#18c7ff",
"backgroundColor": "#fff",
"borderStyle": "white",
"spacing": "5px",
"list": [
{
"text": "首页",
"pagePath": "pages/index/index",
"iconPath": "static/tabs/home_default.png",
"selectedIconPath": "static/tabs/home_selected.png"
},
{
"text": "我的",
"pagePath": "pages/my/index",
"iconPath": "static/tabs/user_default.png",
"selectedIconPath": "static/tabs/user_selected.png"
}
]
},
```
## manifest.json 应用配置
> 例如修改 appid
https://uniapp.dcloud.net.cn/collocation/manifest.html
```
src\manifest.json
```
修改了项目的配置文件, 需要重启
最外面的appid, 是需要用到 Dcloud 云服务 (例如打包成 ios) 自动分配的, `mp-weixin => appid` 这里的才是微信小程序的
## uni-app 生命周期
- 应用生命周期:与 **小程序** 应用的生命周期一致onLaunch、onShow、onHide 等) https://uniapp.dcloud.net.cn/collocation/App.html#applifecycle
- 页面生命周期:与 **小程序** 页面的生命周期一致onLoad、onUnload、onShow 等) https://uniapp.dcloud.net.cn/tutorial/page.html#lifecycle
- 组件生命周期:与 **Vue.js** 组件的生命周期一致created、mounted等 https://uniapp.dcloud.net.cn/tutorial/page.html#componentlifecycle
**页面组件优先使用小程序的生命周期钩子**,也就是 onShow、onHide 这些,普通组件就用 Vue 生命周期钩子
### 页面生命周期
https://uniapp.dcloud.net.cn/tutorial/page.html#lifecycle
![|700x1115](https://img.081024.xyz/image-1744773925981.jpeg)
1. 首先根据pages.json的配置创建页面
2. 根据页面template里的组件创建静态dom
3. 触发onLoad
1. 比较适合的操作是接受上页的参数联网取数据更新data
2. 不能操作 dom
4. 转场动画开始
5. 页面onReady
6. 转场动画结束
#### onShow和onHide
a页面刚进入时会触发a页面的onShow
当a跳转到b页面时a会触发onHide而b会触发onShow
但当b被关闭时b会触发onUnload此时a再次显示出现会再次触发onShow
不同tab页面互相切换时会触发各自的onShow和onHide
#### onBackPress
> 触发返回
```js
onBackPress(options) {
console.log('from:' + options.from)
}
// from:
// 'backbutton'——左上角导航栏按钮及安卓返回键;
// 'navigateBack'——uni.navigateBack() 方法
```
### 组件生命周期
> 和 vue 一样
## 页面和路由
https://uniapp.dcloud.net.cn/api/router.html#navigateto
- `uni.navigateTo`: ===保留当前页面, 跳转到新页面===, 旧页面并没有被销毁, 而是放入页面栈
- `uni.navigateBack` 后退时不会触发 onLoad 而是 onShow
- `uni.redirectTo` 销毁页面跳转到新页面
## 页面通讯
`uni.$emit(eventName,OBJECT)`
`uni.$on(eventName,callback)`
`uni.$once(eventName,callback)`
`uni.$off(eventName, callback)`
## ui 库
uview 不支持 vue 3, 使用 uni-app 自带组件
https://uniapp.dcloud.net.cn/component/
### 内置组件
#### 视图容器
| 组件名 | 说明 |
| ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| [view](https://uniapp.dcloud.net.cn/component/view.html) | 视图容器类似于HTML中的div |
| [scroll-view](https://uniapp.dcloud.net.cn/component/scroll-view.html) | 可滚动视图容器 |
| [swiper](https://uniapp.dcloud.net.cn/component/swiper.html) | 滑块视图容器比如用于轮播banner |
| [match-media](https://uniapp.dcloud.net.cn/component/match-media.html) | 屏幕动态适配组件,比如窄屏上不显示某些内容 |
| [movable-area](https://uniapp.dcloud.net.cn/component/movable-view.html#movable-area) | 可拖动区域 |
| [movable-view](https://uniapp.dcloud.net.cn/component/movable-view.html#movable-view) | 可移动的视图容器在页面中可以拖拽滑动或双指缩放。movable-view必须在movable-area组件中 |
| [cover-view](https://uniapp.dcloud.net.cn/component/cover-view#cover-view) | 可覆盖在原生组件的上的文本组件 |
| [cover-image](https://uniapp.dcloud.net.cn/component/cover-view#cover-image) | 可覆盖在原生组件的上的图片组件 |
#### 基础内容
| 组件名 | 说明 |
| ------------------------------------------------------------------ | -------------- |
| [icon](https://uniapp.dcloud.net.cn/component/icon.html) | 图标 |
| [text](https://uniapp.dcloud.net.cn/component/text.html) | 文字 |
| [rich-text](https://uniapp.dcloud.net.cn/component/rich-text.html) | 富文本显示组件 |
| [progress](https://uniapp.dcloud.net.cn/component/progress.html) | 进度条 |
#### 表单组件
| 标签名 | 说明 |
| ---------------------------------------------------------------------- | -------------------- |
| [button](https://uniapp.dcloud.net.cn/component/button.html) | 按钮 |
| [checkbox](https://uniapp.dcloud.net.cn/component/checkbox.html) | 多项选择器 |
| [editor](https://uniapp.dcloud.net.cn/component/editor.html) | 富文本输入框 |
| [form](https://uniapp.dcloud.net.cn/component/form.html) | 表单 |
| [input](https://uniapp.dcloud.net.cn/component/input.html) | 输入框 |
| [label](https://uniapp.dcloud.net.cn/component/label.html) | 标签 |
| [picker](https://uniapp.dcloud.net.cn/component/picker.html) | 弹出式列表选择器 |
| [picker-view](https://uniapp.dcloud.net.cn/component/picker-view.html) | 窗体内嵌式列表选择器 |
| [radio](https://uniapp.dcloud.net.cn/component/radio.html) | 单项选择器 |
| [slider](https://uniapp.dcloud.net.cn/component/slider.html) | 滑动选择器 |
| [switch](https://uniapp.dcloud.net.cn/component/switch.html) | 开关选择器 |
| [textarea](https://uniapp.dcloud.net.cn/component/textarea.html) | 多行文本输入框 |
#### 路由与页面跳转
| 组件名 | 说明 |
| ------------------------------------------------------------------ | ----------------------------- |
| [navigator](https://uniapp.dcloud.net.cn/component/navigator.html) | 页面链接。类似于HTML中的a标签 |
#### 媒体组件
| 组件名 | 说明 |
| ---------------------------------------------------------------------- | ---------------------------- |
| [audio](https://uniapp.dcloud.net.cn/component/audio.html) | 音频 |
| [camera](https://uniapp.dcloud.net.cn/component/camera.html) | 相机 |
| [image](https://uniapp.dcloud.net.cn/component/image.html) | 图片 |
| [video](https://uniapp.dcloud.net.cn/component/video.html) | 视频 |
| [live-player](https://uniapp.dcloud.net.cn/component/live-player.html) | 直播播放 |
| [live-pusher](https://uniapp.dcloud.net.cn/component/live-pusher.html) | 实时音视频录制,也称直播推流 |
##### image
- `mode`:
- aspectFit: 保持纵横比缩放图片,使图片的长边能完全显示出来
- aspectFill : 保持纵横比缩放图片,只保证图片的短边能完全显示出来
- widthFix: 宽度不变,高度自动变化,保持原图宽高比不变
#### 地图
| 组件名 | 说明 |
| ------------------------------------------------------ | ---- |
| [map](https://uniapp.dcloud.net.cn/component/map.html) | 地图 |
#### 画布
| 组件名 | 说明 |
| ------------------------------------------------------------ | ---- |
| [canvas](https://uniapp.dcloud.net.cn/component/canvas.html) | 画布 |
#### webview
| 组件名 | 说明 |
| ---------------------------------------------------------------- | ------------- |
| [web-view](https://uniapp.dcloud.net.cn/component/web-view.html) | web浏览器组件 |
#### 广告
| 组件名 | 说明 |
| -------------------------------------------------------------- | ------------------ |
| [ad](https://uniapp.dcloud.net.cn/component/ad.html) | 广告组件 |
| [ad-draw](https://uniapp.dcloud.net.cn/component/ad-draw.html) | 沉浸视频流广告组件 |
#### 页面属性配置
| 组件名 | 说明 |
| ---------------------------------------------------------------------------- | -------------------- |
| [custom-tab-bar](https://uniapp.dcloud.net.cn/component/custom-tab-bar.html) | 底部tabbar自定义组件 |
| [navigation-bar](https://uniapp.dcloud.net.cn/component/navigation-bar.html) | 页面顶部导航 |
| [page-meta](https://uniapp.dcloud.net.cn/component/page-meta.html) | 页面属性配置节点 |
#### uniCloud
| 组件名 | 说明 |
| ----------------------------------------------------------------- | ---------------------------- |
| [unicloud-db组件](https://doc.dcloud.net.cn/uniCloud/unicloud-db) | uniCloud数据库访问和操作组件 |
### uni-ui
> 扩展组件, 符合 easycom 标准, 会自动导入
```sh
# 安装
pnpm add @dcloudio/uni-ui
```
| 组件名 | 组件说明 |
| --------------------- | ------------------------------------------------------------------------------- |
| uni-badge | [数字角标](https://ext.dcloud.net.cn/plugin?name=uni-badge) |
| uni-calendar | [日历](https://ext.dcloud.net.cn/plugin?name=uni-calendar) |
| uni-card | [卡片](https://ext.dcloud.net.cn/plugin?name=uni-card) |
| uni-collapse | [折叠面板](https://ext.dcloud.net.cn/plugin?name=uni-collapse) |
| uni-combox | [组合框](https://ext.dcloud.net.cn/plugin?name=uni-combox) |
| uni-countdown | [倒计时](https://ext.dcloud.net.cn/plugin?name=uni-countdown) |
| uni-data-checkbox | [数据选择器](https://ext.dcloud.net.cn/plugin?name=uni-data-checkbox) |
| uni-data-picker | [数据驱动的picker选择器](https://ext.dcloud.net.cn/plugin?name=uni-data-picker) |
| uni-dateformat | [日期格式化](https://ext.dcloud.net.cn/plugin?name=uni-dateformat) |
| uni-datetime-picker | [日期选择器](https://ext.dcloud.net.cn/plugin?name=uni-datetime-picker) |
| uni-drawer | [抽屉](https://ext.dcloud.net.cn/plugin?name=uni-drawer) |
| uni-easyinput | [增强输入框](https://ext.dcloud.net.cn/plugin?name=uni-easyinput) |
| uni-fab | [悬浮按钮](https://ext.dcloud.net.cn/plugin?name=uni-fab) |
| uni-fav | [收藏按钮](https://ext.dcloud.net.cn/plugin?name=uni-fav) |
| uni-file-picker | [文件选择上传](https://ext.dcloud.net.cn/plugin?name=uni-file-picker) |
| uni-forms | [表单](https://ext.dcloud.net.cn/plugin?name=uni-forms) |
| uni-goods-nav | [商品导航](https://ext.dcloud.net.cn/plugin?name=uni-goods-nav) |
| uni-grid | [宫格](https://ext.dcloud.net.cn/plugin?name=uni-grid) |
| uni-group | [分组](https://ext.dcloud.net.cn/plugin?name=uni-group) |
| uni-icons | [图标](https://ext.dcloud.net.cn/plugin?name=uni-icons) |
| uni-indexed-list | [索引列表](https://ext.dcloud.net.cn/plugin?name=uni-indexed-list) |
| uni-link | [超链接](https://ext.dcloud.net.cn/plugin?name=uni-link) |
| uni-list | [列表](https://ext.dcloud.net.cn/plugin?name=uni-list) |
| uni-load-more | [加载更多](https://ext.dcloud.net.cn/plugin?name=uni-load-more) |
| uni-nav-bar | [自定义导航栏](https://ext.dcloud.net.cn/plugin?name=uni-nav-bar) |
| uni-notice-bar | [通告栏](https://ext.dcloud.net.cn/plugin?name=uni-notice-bar) |
| uni-number-box | [数字输入框](https://ext.dcloud.net.cn/plugin?name=uni-number-box) |
| uni-pagination | [分页器](https://ext.dcloud.net.cn/plugin?name=uni-pagination) |
| uni-popup | [弹出层](https://ext.dcloud.net.cn/plugin?name=uni-popup) |
| uni-rate | [评分](https://ext.dcloud.net.cn/plugin?name=uni-rate) |
| uni-row | [布局-行](https://ext.dcloud.net.cn/plugin?name=uni-row) |
| uni-search-bar | [搜索栏](https://ext.dcloud.net.cn/plugin?name=uni-search-bar) |
| uni-segmented-control | [分段器](https://ext.dcloud.net.cn/plugin?name=uni-segmented-control) |
| uni-steps | [步骤条](https://ext.dcloud.net.cn/plugin?name=uni-steps) |
| uni-swipe-action | [滑动操作](https://ext.dcloud.net.cn/plugin?name=uni-swipe-action) |
| uni-swiper-dot | [轮播图指示点](https://ext.dcloud.net.cn/plugin?name=uni-swiper-dot) |
| uni-table | [表格](https://ext.dcloud.net.cn/plugin?name=uni-table) |
| uni-tag | [标签](https://ext.dcloud.net.cn/plugin?name=uni-tag) |
| uni-title | [章节标题](https://ext.dcloud.net.cn/plugin?name=uni-title) |
| uni-transition | [过渡动画](https://ext.dcloud.net.cn/plugin?name=uni-transition) |
## uni-api
- 出现目的
1. 很多的原生的小程序的api 不支持 promise `wx.request`
2. 想要做一个跨平台 应用 那么 发送请求 该用哪一代码来发送 3. axios 针对 网页的!!! 4. wx.request 只针对 微信小程序 5. 自己去封装一个api 可以去兼容适配所有的客户端
```
if (微信小程序) wx.request
if (web) axios
if (支付宝) ...
```
- 如何使用
```js
uni.request
```
## 语法
1. 标签优先使用 uni-app 的
2. js语法和vue一样
3. 特有的api: `uni.request`, `uni.showToast`
4. 生命周期参考上面的
### css 单位 rpx
- 以 750 px 宽的屏幕为基准750rpx 恰好为屏幕宽度
- 设计师可以用 iPhone6 作为视觉稿的标准, 对应 750rpx
## 分包优化
https://uniapp.dcloud.net.cn/collocation/manifest.html#关于分包优化的说明
## 项目实战
视频: https://www.bilibili.com/video/BV1Bp4y1379L
笔记: https://gitee.com/Megasu/uni-app-shop-note
高星框架: https://github.com/codercup/unibest
### 框架搭建
#### vscode 插件
`.vscode/extensions.json`
```json
{
// 推荐的扩展插件
"recommendations": [
"mrmaoddxxaa.create-uniapp-view", // 创建 uni-app 页面
"uni-helper.uni-helper-vscode", // uni-app 代码提示
"evils.uniapp-vscode", // uni-app 文档
"vue.volar", // vue3 语法支持
"editorconfig.editorconfig", // editorconfig
"dbaeumer.vscode-eslint", // eslint
"esbenp.prettier-vscode" // prettier
]
}
```
#### create-uniapp-view 设置
设置: 创建同名文件夹、不使用 scoped、预编译器 scss、vue 3
#### 类型提示
> uni-app 类型, 由第三方 uni-helper 提供
```sh
pnpm add @uni-helper/uni-app-types @uni-helper/uni-ui-types -D
```
> 微信小程序的类型提示
```sh
pnpm add miniprogram-api-typings -D
```
> nodejs
```sh
pnpm add @types/node -D
```
`tsconfig.json` 的 types 中添加
```json
{
//...
"compilerOptions": {
//...
"types": [
"@dcloudio/types",
"miniprogram-api-typings",
"@uni-helper/uni-app-types",
"@uni-helper/uni-ui-types"
]
},
//...
"vueCompilerOptions": {
"plugins": ["@uni-helper/uni-app-types/volar-plugin"]
}
}
```
- TypeScript 默认设置 typeRoots 包含了 `node_modules/@types`, 以 `@types` 开头的包不需要手动添加
#### vscode 设置
`.vscode/settings.json`
```json
{
// 在保存时格式化文件
"editor.formatOnSave": true,
// 文件格式化配置
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 控制资源管理器是否应以紧凑形式呈现文件夹。单个子文件夹将被压缩在组合的树元素中。
"explorer.compactFolders": false,
// 配置 vue 文件的默认格式化工具为 prettier
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 配置 html 文件的默认格式化工具为 prettier
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 配置 ts 文件的默认格式化工具为 prettier
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 配置语言的文件关联
"files.associations": {
"pages.json": "jsonc", // pages.json 可以写注释
"manifest.json": "jsonc" // manifest.json 可以写注释
}
}
```
#### prettierrc
```sh
pnpm add 'prettier@^3' '@vue/eslint-config-prettier@^9' -D
```
`.prettierrc.json`
```json
{
"singleQuote": true,
"semi": false,
"printWidth": 120,
"trailingComma": "all",
"endOfLine": "auto"
}
```
#### eslint
```sh
pnpm add 'eslint@^8' 'eslint-plugin-vue@^9' '@vue/eslint-config-typescript@^11' '@rushstack/eslint-patch@^1' -D
```
`.eslintrc.cjs`
```json
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-typescript',
'@vue/eslint-config-prettier',
],
// 小程序全局变量
globals: {
uni: true,
wx: true,
WechatMiniprogram: true,
getCurrentPages: true,
UniApp: true,
UniHelper: true,
Page: true,
AnyObject: true,
},
parserOptions: {
ecmaVersion: 'latest',
},
rules: {
'prettier/prettier': [
'warn',
{
singleQuote: true,
semi: false,
printWidth: 100,
trailingComma: 'all',
endOfLine: 'auto',
},
],
'vue/multi-word-component-names': ['off'],
'vue/no-setup-props-destructure': ['off'],
'vue/no-deprecated-html-element-is': ['off'],
'@typescript-eslint/no-unused-vars': ['off'],
},
}
```
#### editorconfig
`.editorconfig`
```ini
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
```
#### husky
> 提交前格式校验
```sh
pnpm add husky lint-staged -D
```
`.husky/pre-commit`
```sh
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint-staged
```
`package.json`
```json
{
// ...
"scripts": {
// ...
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"prepare": "husky install",
"lint-staged": "lint-staged"
},
"lint-staged": {
"*.{js,ts,vue}": ["eslint --fix"]
}
//...
}
```
### 修改appid
微信公众号平台: https://mp.weixin.qq.com/
注册 --> 管理 --> 开发管理 --> AppID(小程序ID)
可以修改 `src/manifest.json` 中的 `mp-weixin => appid`, 或者直接在模拟器的详情中修改
### pinia 持久化
```sh
pnpm add 'pinia@^2' 'pinia-plugin-persistedstate@^3'
```
- `pinia-plugin-persistedstate` 默认使用 `localStorage`, 需要改为 uni app 的 `uni.setStorageSync()``uni.getStorageSync()`
- https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/config.html#storage
- pinia 3 暂时不兼容: https://github.com/dcloudio/uni-app/issues/5326
`src/stores/index.ts`
```ts
import { createPinia } from 'pinia'
import persist from 'pinia-plugin-persistedstate'
// 创建 pinia 实例
const pinia = createPinia()
// 使用持久化存储插件
pinia.use(persist)
// 默认导出,给 main.ts 使用
export default pinia
```
`main.ts`
```ts
// 导入 pinia 实例
import pinia from './stores/'
// ...
// 在 return 前添加
// 使用 pinia
app.use(pinia)
```
`stores/modules/member.ts`
- 小程序没有 localStorage
```ts
export const useMemberStore = defineStore(
'member',
() => {
//…省略
},
{
// 配置持久化
persist: {
// 调整为兼容多端的API
storage: {
setItem(key, value) {
uni.setStorageSync(key, value) // [!code warning]
},
getItem(key) {
return uni.getStorageSync(key) // [!code warning]
},
},
},
},
)
```
### 拦截器
> 应用场景: 给请求 `request` 添加基础地址、超时时间、请求头标识、token
> 文件上传 `uploadFile` 同样添加
https://uniapp.dcloud.net.cn/api/interceptor.html#addinterceptor
- 添加拦截器: `uni.addInterceptor`
```js
uni.request({
url: 'request/login', //仅为示例,并非真实接口地址。
success: (res) => {
console.log(res.data)
// 打印: {code:1,...}
},
})
uni.addInterceptor('request', {
invoke(args) {
// 拦截前触发
args.url = 'https://www.example.com/' + args.url
},
success(args) {
// 成功回调拦截
args.data.code = 1
},
fail(err) {
// 失败回调拦截
console.log('interceptor-fail', err)
},
complete(res) {
// 完成回调拦截
console.log('interceptor-complete', res)
},
})
uni.addInterceptor({
returnValue(args) {
// 方法调用后触发,处理返回值
return args.data
},
})
```
- 删除拦截器 `uni.removeInterceptor`
```js
uni.removeInterceptor('request')
```
### 响应封装
- 成功:
- 提取 res. data
- 添加类型支持
- 失败
- 网络错误: 提示换网络
- 401: 清理用户信息, 跳转登录页
- 其他错误: 轻提示
注意项:
- uni-app 的 request 只有网络错误才会走 fail, 权限不足等后端有响应的, 都是 success. 需要通过 `res.statusCode` 状态码进一步控制
### 自定义导航栏
https://ask.dcloud.net.cn/article/34921
`src/pages/index/components/CustomNavbar.vue`
样式适配: 安全区域 (例如刘海屏)
```ts
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
```
用来设置 paddingTop
```vue
<view class="navbar" :style="{ paddingTop: safeAreaInsets!.top + 10 + 'px' }">
</view>
```
### 轮播图通用组件
#### 配置 easycom 自动导入
`page.json`
```json
"easycom": {
// 是否开启自动扫描
"autoscan": true,
// 以正则方式自定义组件匹配规则
"custom": {
// ...
// 以 Jbc 开头的组件,在 components 文件夹中查找引入(需要重启服务器)
"^Jbc(.*)": "@/components/Jbc$1.vue"
}
},
```
#### 组件
`src/components/JbcSwiper.vue`
使用: `swiper` 滑动视图容器
https://uniapp.dcloud.net.cn/component/swiper.html
`onload` 钩子中调用 api
- `swiper` 绑定 `change` 事件的类型: `UniHelper.SwiperOnChange`
```ts
const handleChange: UniHelper.SwiperOnChange = (e) => {
activeIndex.value = e.detail!.current
}
```
#### 类型提示
`src/types/components.d.ts`
```ts
import JbcSwiper from '@/components/JbcSwiper.vue'
declare module 'vue' {
export interface GlobalComponents {
JbcSwiper: typeof JbcSwiper
}
}
// 组件实例类型 (typeof xxx组件 获取到的是构造函数类型)
export type JbcSwiperInstance = InstanceType<typeof JbcSwiper>
```
- declare module 会与现有模块类型合并,而不是覆盖
#### api 封装
`src/services/home.ts`
- `uni.request()` : 当请求类型为 get 时, 把 data 里的键值对转化为 query 参数
### 首页分类组件
### 热门推荐
### 猜你喜欢通用模块
参考: `JbcSwiper.vue`
首页添加滚动组件:
- `scroll-view` 组件:
- @scrolltolower: 滚动到底触发
父组件调用子组件的方法:
- 子组件通过 `defineExpose({methodName})` 暴露方法
- 父组件给组件绑定 `ref="xxx"`, 通过 `xxx.value.methodName()`
- 子组件的类型为自定义组件实例类型 `JbcGuessInstance`
滚动到底时调用猜你喜欢模块获取下页数据插入数组
### 首页下拉刷新
- `scroll-view` 组件:
- refresher-enabled: 开启自定义下拉刷新
- @refresherrefresh: 自定义下拉刷新被触发
- refresher-triggered: 设置当前下拉刷新状态
- enable-back-to-top: iOS点击顶部状态栏、安卓双击标题栏时滚动条返回顶部只支持竖向
刷新时, 多个 api 用 `Promise.allSettled()`
bug 修复: 下拉刷新后轮播图指示器错乱
原因:
- 每次刷新, 获取的轮播图数据都会随机打乱, 用 id 作为 key 导致 dom 更新异常改为 `:key="item.id + index"`
- `watch` 监听 `props.list` 的变化, 复位 `activeIndex.value=0`
### 首页骨架屏
- 在微信开发者工具模拟器右下角三个小点的菜单里, 有生成骨架屏选项
- 复制 `index.skeleton.wxml` 到 template, 复制 `index.skeleton.wxss` 到 style
- 自定义导航栏不需要加载数据, 只保留滚动容器里面的部分
### 热门推荐详情页
- uni-app 获取路由参数
```ts
// uni-app获取路由参数
const query = defineProps<{
type: string
}>()
console.log(query)
```
- uni-app 动态设置标题
```ts
// 动态设置标题
uni.setNavigationBarTitle({ title: '标题名' })
```
- 判断当前是不是开发环境, 方便设置模拟数据
```ts
{
page: import.meta.env.DEV ? 33 : 1,
pageSize: 10,
}
```
### 商品分类页
- 小程序开发者工具直接显示分类页, 免去点击跳转:
点击'普通编译 -> 添加编译模式 -> 选择要启动页面为要编译的页面'
- 切换分类时, 滚动条不恢复到顶部:
```ts
// 切换分类时,滚动到顶部
const scrollTop = ref(0) // scrollTop绑定到 scroll-view 的 scroll-top 属性上
// 记录滚动位置, 保存到另一个变量
// 切换分页的时候,先还原scrollTop的旧值,然后在视图渲染后,再设置scrollTop等于0触发还原
const scroolTopOld = ref(0)
type ScrollEvent = {
detail: {
scrollTop: number
scrollLeft: number
scrollHeight: number
scrollWidth: number
deltaX: number
deltaY: number
}
[key: string]: any
}
const handlerScrool = (e: ScrollEvent) => {
scroolTopOld.value = e.detail.scrollTop
}
watch(activeIndex, () => {
scrollTop.value = scroolTopOld.value
nextTick(() => {
scrollTop.value = 0
})
})
```
### 商品详情页
- 图标: 使用阿里巴巴字体图标 https://www.iconfont.cn/
字体可以上传到 cdn, `iconfont.css` 需要修改 `font-face`
```css
@font-face {
font-family: 'iconfont'; /* Project id */
src: url('iconfont.ttf?t=1745547896290') format('truetype');
}
```
- 操作栏有按钮, 需要设置安全区域 (可能有小白条) 防止误操作
```vue
<view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
</view>
```
- 弹出层组件 `uni-popup`
https://uniapp.dcloud.net.cn/component/uniui/uni-popup.html
属性:
- type: 弹出方式, top center bottom left right message dialog share
- background-color: 主窗口背景色
- 先用 ref 绑定 `uni-popup`, 再调用 ref 的 open 和 close 方法控制是否显示
类型定义: `UniHelper.UniPopupInstance` 类型里的 `open()``close()` 是可选的, 会导致 `popup.value?.open()` 报错, 需要使用交叉类型
```ts
const popup = ref<
UniHelper.UniPopupInstance & {
open: (type?: UniHelper.UniPopupType) => void
close: () => void
}
>()
```
- `NonNullable<type>`: 去除类型里的 null 和 undefined
### 登录模块
#### 快捷登录
- 登陆流程
![|700x710](https://img.081024.xyz/image-1745582023800.jpeg)
- 获取用户凭证 `code`
===有效期五分钟===。开发者需要在开发者服务器后台调用 code2Session使用 code 换取 openid、unionid、session_key 等信息
不能在 `getphonenumber` 里调用, 在 `onload`
```ts
wx.login(Object object)
```
https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html
- 获取手机号 (小程序有隐私保护, 需要用户同意, 且个人开发者没法调用)
`button` 需要设置 `open-type=getPhoneNumber`, 然后在 `@getphonenumber` 回调中拿到手机号
https://uniapp.dcloud.net.cn/component/button.html#button
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
### 个人中心
- 封装组合式函数 `src/composables/index.ts`
> 函数命名以 use 开头, 放 composables 文件夹下
```ts
import type { JbcGuessInstance } from '@/types/components'
import { ref } from 'vue'
export const useGuessList = () => {
const guessRef = ref<JbcGuessInstance>()
const onScrollToLower = () => {
guessRef.value?.getMore()
}
return { guessRef, onScrollToLower }
}
```
调用:
```ts
import { useGuessList } from '@/composables'
const { guessRef, onScrollToLower } = useGuessList()
```
- 设置页分包
> 把不常用的页面拆成另外的包, 当某些步骤可能需要打开时, 提前预加载
存放目录: 和 `pages` 同级, 例如 `pagesMember/settings/settings.vue`
`pages.json` 中添加 `subPackages`, 和 `pages` 同级
```json
// 分包
"subPackages": [
{
// 子包的根目录
"root": "pagesMember",
// pages参数和主包一样
"pages": [
{
"path": "settings/settings",
"style": {
"navigationBarTitleText": "设置"
}
}
]
}
]
```
- 设置预下载 `page.json`
> key 是页面路径
https://uniapp.dcloud.net.cn/collocation/pages.html#preloadrule
```json
// 预下载
"preloadRule": {
"pages/my/my": {
"network": "all",
"packages": ["pagesMember"]
}
}
```
### 设置页面
- 显示模态框: `uni.showModal()`
- 路由后退: `uni.navigateBack()`
### 修改用户信息
- `Pick<Type, Keys>` : 从类型中选取指定属性
- `picker` 选择器
- 时间选择器: `mode = time`
- 省市区选择器: `mode = region`
https://uniapp.dcloud.net.cn/component/picker.html
- `radio` 选择器
https://uniapp.dcloud.net.cn/component/radio.html
- 修改头像: 调用拍照 api `uni.chooseMedia`
- 上传头像: `uni.uploadFile`
### 收货地址
#### 地址表单
- 切换开关 `switch` 组件
https://uniapp.dcloud.net.cn/component/switch.html
- `uni.navigateBack`, 触发的是 onShow 事件, 要把请求列表放到 onShow 而不是 onLoad
#### 数据校验
- 表单校验: 使用 `<uni-forms>` 组件
- 表单绑定 `ref modelValue rules`
- 表单项设置 `label name`
- 原生 checkbox 或三方组件不支持 v-model 等, `onFieldChange`
- 提交前再调用 formRef 的 `.validate()`, 返回的是 Promise
https://uniapp.dcloud.net.cn/component/uniui/uni-forms.html
```ts
// 表单验证规则
const rules: UniFormsRules = {
receiver: {
rules: [
{ required: true, errorMessage: '请输入收货人姓名' },
{
minLength: 2,
maxLength: 5,
errorMessage: '姓名长度在 {minLength} 到 {maxLength} 个字符',
},
],
},
contact: {
rules: [
{ required: true, errorMessage: '请输入联系电话' },
{
pattern: /^1[3-9]\d{9}$/,
errorMessage: '手机号码为11位数字',
},
],
},
fullLocation: {
rules: [{ required: true, errorMessage: '请选择省市区' }],
},
address: {
rules: [
{ required: true, errorMessage: '请输入详细地址' },
{
minLength: 2,
maxLength: 20,
errorMessage: '详细地址长度在 {minLength} 到 {maxLength} 个字符',
},
],
},
}
```
#### 删除功能
- 删除地址: 侧划组件 `uni-swipe-action`
https://uniapp.dcloud.net.cn/component/uniui/uni-swipe-action.html
### SKU 模块 - 最小存货单位
- SKU 算法: https://juejin.cn/post/7002746459456176158
- 插件市场: https://ext.dcloud.net.cn/
- 插件下载: https://gitee.com/vk-uni/vk-u-goods-sku-popup
#### 类型定义
创建 `vk-data-goods-sku-popup.d.ts`, 放在 `vk-data-goods-sku-popup` 目录下
```ts
import { Component } from '@uni-helper/uni-app-types'
/** SKU 弹出层 */
export type SkuPopup = Component<SkuPopupProps>
/** SKU 弹出层实例 */
export type SkuPopupInstance = InstanceType<SkuPopup>
/** SKU 弹出层属性 */
export type SkuPopupProps = {
/** 双向绑定true 为打开组件false 为关闭组件 */
modelValue: boolean
/** 商品信息本地数据源 */
localdata: SkuPopupLocaldata
/** 按钮模式 1:都显示 2:只显示购物车 3:只显示立即购买 */
mode?: 1 | 2 | 3
/** 该商品已抢完时的按钮文字 */
noStockText?: string
/** 库存文字 */
stockText?: string
/** 点击遮罩是否关闭组件 */
maskCloseAble?: boolean
/** 顶部圆角值 */
borderRadius?: string | number
/** 最小购买数量 */
minBuyNum?: number
/** 最大购买数量 */
maxBuyNum?: number
/** 每次点击后的数量 */
stepBuyNum?: number
/** 是否只能输入 step 的倍数 */
stepStrictly?: boolean
/** 是否隐藏库存的显示 */
hideStock?: false
/** 主题风格 */
theme?: 'default' | 'red-black' | 'black-white' | 'coffee' | 'green'
/** 默认金额会除以100即100=1元若设置为0则不会除以100即1=1元 */
amountType?: 1 | 0
/** 自定义获取商品信息的函数已知支付宝不支持支付宝请改用localdata属性 */
customAction?: () => void
/** 是否显示右上角关闭按钮 */
showClose?: boolean
/** 关闭按钮的图片地址 */
closeImage?: string
/** 价格的字体颜色 */
priceColor?: string
/** 立即购买 - 按钮的文字 */
buyNowText?: string
/** 立即购买 - 按钮的字体颜色 */
buyNowColor?: string
/** 立即购买 - 按钮的背景颜色 */
buyNowBackgroundColor?: string
/** 加入购物车 - 按钮的文字 */
addCartText?: string
/** 加入购物车 - 按钮的字体颜色 */
addCartColor?: string
/** 加入购物车 - 按钮的背景颜色 */
addCartBackgroundColor?: string
/** 商品缩略图背景颜色 */
goodsThumbBackgroundColor?: string
/** 样式 - 不可点击时,按钮的样式 */
disableStyle?: object
/** 样式 - 按钮点击时的样式 */
activedStyle?: object
/** 样式 - 按钮常态的样式 */
btnStyle?: object
/** 字段名 - 商品表id的字段名 */
goodsIdName?: string
/** 字段名 - sku表id的字段名 */
skuIdName?: string
/** 字段名 - 商品对应的sku列表的字段名 */
skuListName?: string
/** 字段名 - 商品规格名称的字段名 */
specListName?: string
/** 字段名 - sku库存的字段名 */
stockName?: string
/** 字段名 - sku组合路径的字段名 */
skuArrName?: string
/** 字段名 - 商品缩略图字段名(未选择sku时) */
goodsThumbName?: string
/** 被选中的值 */
selectArr?: string[]
/** 打开弹出层 */
onOpen: () => void
/** 关闭弹出层 */
onClose: () => void
/** 点击加入购物车时需选择完SKU才会触发*/
onAddCart: (event: SkuPopupEvent) => void
/** 点击立即购买时需选择完SKU才会触发*/
onBuyNow: (event: SkuPopupEvent) => void
}
/** 商品信息本地数据源 */
export type SkuPopupLocaldata = {
/** 商品 ID */
_id: string
/** 商品名称 */
name: string
/** 商品图片 */
goods_thumb: string
/** 商品规格列表 */
spec_list: SkuPopupSpecItem[]
/** 商品SKU列表 */
sku_list: SkuPopupSkuItem[]
}
/** 商品规格名称的集合 */
export type SkuPopupSpecItem = {
/** 规格名称 */
name: string
/** 规格集合 */
list: { name: string }[]
}
/** 商品SKU列表 */
export type SkuPopupSkuItem = {
/** SKU ID */
_id: string
/** 商品 ID */
goods_id: string
/** 商品名称 */
goods_name: string
/** 商品图片 */
image: string
/** SKU 价格 * 100, 注意:需要乘以 100 */
price: number
/** SKU 规格组成, 注意:需要与 spec_list 数组顺序对应 */
sku_name_arr: string[]
/** SKU 库存 */
stock: number
}
/** 当前选择的sku数据 */
export type SkuPopupEvent = SkuPopupSkuItem & {
/** 商品购买数量 */
buy_num: number
}
/** 全局组件类型声明 */
declare module 'vue' {
export interface GlobalComponents {
'vk-data-goods-sku-popup': SkuPopup
}
}
```
### 购物车
- 步进器组件 `vk-data-input-number-box.vue`
- 步进器组件类型定义: `vk-data-input-number-box.d.ts`
```ts
import { Component } from '@uni-helper/uni-app-types'
/** 步进器 */
export type InputNumberBox = Component<InputNumberBoxProps>
/** 步进器实例 */
export type InputNumberBoxInstance = InstanceType<InputNumberBox>
/** 步进器属性 */
export type InputNumberBoxProps = {
/** 输入框初始值默认1 */
modelValue: number
/** 用户可输入的最小值默认0 */
min: number
/** 用户可输入的最大值默认99999 */
max: number
/** 步长每次加或减的值默认1 */
step: number
/** 是否禁用操作,包括输入框,加减按钮 */
disabled: boolean
/** 输入框宽度单位rpx默认80 */
inputWidth: string | number
/** 输入框和按钮的高度单位rpx默认50 */
inputHeight: string | number
/** 输入框和按钮的背景颜色(默认#F2F3F5 */
bgColor: string
/** 步进器标识符 */
index: string
/** 输入框内容发生变化时触发 */
change: (event: InputNumberBoxEvent) => void
/** 输入框失去焦点时触发 */
blur: (event: InputNumberBoxEvent) => void
/** 点击增加按钮时触发 */
plus: (event: InputNumberBoxEvent) => void
/** 点击减少按钮时触发 */
minus: (event: InputNumberBoxEvent) => void
}
/** 步进器事件对象 */
export type InputNumberBoxEvent = {
/** 输入框当前值 */
value: number
/** 步进器标识符 */
index: string
}
/** 全局组件类型声明 */
declare module 'vue' {
export interface GlobalComponents {
'vk-data-input-number-box': InputNumberBox
}
}
```
- 购物车有两个, 一个页底部导航栏的, 一个是商品页面按钮的
### 订单页
- 送货时间: `picker` 自定义选择列表
- 商品页跳转快速下单: 小程序开发者工具左下角可以看到页面参数
- 跳转订单详情页使用 `uni.redirectTo`
### 订单详情页
#### 自定义导航栏
- 自定义导航栏设置左上角按钮
- 获取当前页面栈 : https://developers.weixin.qq.com/miniprogram/dev/reference/api/getCurrentPages.html
- `getCurrentPages()` 最后一个就是当前页面
#### 滚动时切换 title
- 向下滚动时切换 title
- 滚动驱动的动画: https://developers.weixin.qq.com/miniprogram/dev/framework/view/animation.html#滚动驱动的动画
- 获取当前页面的实例
- 在页面渲染完毕 `onReady` 生命周期执行
```ts
onReady(() => {
// 动画效果: 导航栏背景色
pageInstance.animate(
'.navbar',
[
{
backgroundColor: 'transparent',
},
{
backgroundColor: '#f8f8f8',
},
],
1000,
{
scrollSource: '#scroller',
timeRange: 1000,
startScrollOffset: 0,
endScrollOffset: 50,
},
)
// 动画效果: 导航栏标题
pageInstance.animate(
'.navbar .title',
[
{
color: 'transparent',
},
{
color: '#000',
},
],
1000,
{
scrollSource: '#scroller',
timeRange: 1000,
startScrollOffset: 0,
endScrollOffset: 50,
},
)
// 动画效果: 返回按钮
pageInstance.animate(
'.navbar .back',
[
{
color: '#fff',
},
{
color: '#000',
},
],
1000,
{
scrollSource: '#scroller',
timeRange: 1000,
startScrollOffset: 0,
endScrollOffset: 50,
},
)
})
```
#### 支付
- 支付倒计时: `uni-countdown` https://uniapp.dcloud.net.cn/component/uniui/uni-countdown.html
- 去支付
- 生产环境:
1. 需要通过认证的应用对应的 appid https://uniapp.dcloud.net.cn/tutorial/app-payment-weixin.html#%E5%BC%80%E9%80%9A
2. 提交资料申请开通获得商户号 `PartnerID` 和密码, 置APIv2密钥
3. 服务器接入 https://pay.weixin.qq.com/doc/v3/merchant/4012791911
4. 流程: ![|700x537](https://img.081024.xyz/image-1746845896307.jpeg)
- 开发环境:
- 模拟支付
- 微信支付参数的返回类型: `WechatMiniprogram.RequestPaymentOption`, 给 `wx.requestPayment` 调用
- 复制剪切板: `uni.setClipboardData`
### 订单列表页
todo: 5 个不同类型的列表并发太慢
### 小程序打包
1. 编译
```sh
pnpm build:mp-weixin
```
2. 开发者工具: 点击上传
1. 或者使用 ci: https://developers.weixin.qq.com/miniprogram/dev/devtools/ci.html
3. 公众号平台提交审核 https://mp.weixin.qq.com
#### 条件编译
> 是通过注释实现
https://uniapp.dcloud.net.cn/tutorial/platform.html#preprocessor
-`#ifdef``#ifndef``%PLATFORM%` 开头,以 `#endif` 结尾。
- `#ifdef`if defined 仅在某平台存在
- `#ifndef`if not defined 除了某平台均存在
- `%PLATFORM%`:平台名称
| 条件编译写法 | 说明 |
| ------------------------------------------------------------ | --------------------------------------------------------------------------------- |
| `#ifdef APP-PLUS`<br>需条件编译的代码 <br>`#endif ` | 仅出现在 App 平台下的代码 |
| `#ifndef H5` <br>需条件编译的代码<br>`#endif` | 除了 H5 平台其它平台均存在的代码注意if后面有个n |
| ` #ifdef H5 丨丨 MP-WEIXIN`<br>需条件编译的代码 <br>`#endif` | 在 H5 平台或微信小程序平台存在的代码(这里只有 \|\|,不可能出现&&,因为没有交集) |
- 支持的文件:
- .vue/.nvue/.uvue
- .js/.uts
- .css
- pages.json
- 各预编译语言文件,如:.scss、.less、.stylus、.ts、.pug
**注意:** 
1. 样式的条件编译,无论是 css 还是 sass/scss/less/stylus 等预编译语言中,必须使用 `/*注释*/` 的写法。
2. 通过 import 导入的 scss 中的条件编译似乎不生效
#### h5 预览
- 相对路径
`manifest.json`
```json
/* H5特有相关 */
"h5": {
"router": {
"base": "./"
}
},
```
#### 安卓打包
调试:
- 需要 `HBuilderX`
- 安装运行插件和基座
- 调试手机开启开发者工具
云打包:
- 注册 DCloud 账号
- 获取 DCloud AppID: 在 HBuilderX 中打开 `manifest.json`
- 设置应用名称图标
- 发行-云打包-使用云端证书
#### ios 打包
调试:
- 需要mac
- 安装 xcode
- 运行到模拟器
打包:
- 需要苹果开发者账号和年费
#### 样式兼容
- 小程序不支持 \* 选择器
- 页面视口差异
- 小程序: 分tabBar 页和普通页
- 在 tabBar 页中, 视口不包括顶部和底部
- 在普通页中, 视口不包括顶部
- h 5: 视口包括浏览器整个区域
- H 5 端默认开启 scoped
- uni-app 跨端注意
- uni-app css 支持
- uni-app 条件编译
实际遇到的问题:
- h5 中的顶栏和底栏都是 div 模拟的, 这样会有遮住的问题
uni-app 内置的 css 变量: https://uniapp.dcloud.net.cn/tutorial/syntax-css.html#css-%E5%8F%98%E9%87%8F
| CSS 变量 | 描述 | App | 小程序 | H5 |
| ------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- | ------ | -------------------- |
| --status-bar-height | 系统状态栏高度 | [系统状态栏高度](http://www.html5plus.org/doc/zh_cn/navigator.html#plus.navigator.getStatusbarHeight)、nvue 注意见下 | 25px | 0 |
| --window-top | 内容区域距离顶部的距离 | 0 | 0 | NavigationBar 的高度 |
| --window-bottom | 内容区域距离底部的距离 | 0 | 0 | TabBar 的高度 |
示例:
```scss
/* 吸底工具栏 */
.toolbar {
/* ... */
bottom: calc(var(--window-bottom));
/* ... */
}
```
- 骨架屏样式错位
原因: h5 样式隔离
具体点: 骨架屏是由小程序开发工具生成的, 自动生成的样式和 h5 样式隔离后的 class 对应不上
解决方法: 把骨架屏里对应的子组件样式单独拆分, 然后在骨架屏中条件编译导入, 搜索 `is=` 对应的组件, 就是需要处理的
- App flex 布局不生效: App 没有 page 元素, 只有 `#app`
#### 组件兼容
小程序特有的:
- `open-type=`, 例外: `navigate` `switchTab` `redirect`
- `wx.`
- `.animate` 动画效果
- `uni-navigator`: 在 h5 中实现, 实际在外层嵌套了一个 a 标签, 可能导致原来的样式失效
- 解决方法: 添加属性 `:render-link="false"`
#### API 兼容
- 选择头像的 `uni.chooseMedia` 只支持小程序, 可以使用停止维护的 api `uni.chooseImage`