uni-app 基础
1.创建项目
uni-app 支持两种方式创建项目:
- 通过 HBuilderX 创建(需安装 HBuilderX 编辑器)
- 通过命令行创建(需安装 NodeJS 环境)
1.1 HBuilderX 创建 uni-app 项目
1.下载安装 HbuilderX 编辑器
2.通过 HbuilderX 创建 uni-app vue3 项目
3.安装 uni-app vue3 编译器插件
4.编译成微信小程序端代码
5.开启服务端口
小技巧分享:模拟器窗口分离和置顶
HBuildeX 和 微信开发者工具 关系
1.2 pages.json 和 tabBar 案例
目录结构
├─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:用于配置页面路由、导航栏、tabBar 等页面类信息
代码:
{
// 页面路由
"pages": [
{
"path": "pages/index/index",
// 页面样式配置
"style": {
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/my/my",
"style": {
"navigationBarTitleText": "我的"
}
}
],
// 全局样式配置
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#27BA9B",
"backgroundColor": "#F8F8F8"
},
// tabBar 配置
"tabBar": {
"selectedColor": "#27BA9B",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "static/tabs/home_default.png",
"selectedIconPath": "static/tabs/home_selected.png"
},
{
"pagePath": "pages/my/my",
"text": "我的",
"iconPath": "static/tabs/user_default.png",
"selectedIconPath": "static/tabs/user_selected.png"
}
]
}
}
1.3 uni-app 和原生小程序区别
开发区别
uni-app 项目每个页面是一个 .vue
文件,数据绑定及事件处理同 Vue.js
规范:
- 属性绑定
src="{ { url }}"
升级成:src="url"
- 事件绑定
bindtap="eventName"
升级成@tap="eventName"
,支持()传参 - 支持 Vue 常用指令
v-for
、v-if
、v-show
、v-model
等
其他区别补充
- 调用接口能力,建议前缀
wx
替换为uni
,养成好习惯,支持多端开发。 <style>
页面样式不需要写scoped
,小程序是多页面应用,页面样式自动隔离。- 生命周期分三部分:应用生命周期(小程序),页面生命周期(小程序),组件生命周期(Vue)
1.4 案例:轮播图与预览图片
主要功能
- 滑动轮播图
- 点击大图预览
参考代码
<template>
<swiper class="banner" indicator-dots circular :autoplay="false">
<swiper-item v-for="item in pictures" :key="item.id">
<image @tap="onPreviewImage(item.url)" :src="item.url"></image>
</swiper-item>
</swiper>
</template>
<script>
export default {
data() {
return {
// 轮播图数据
pictures: [
{
id: "1",
url: "https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_preview_1.jpg",
},
{
id: "2",
url: "https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_preview_2.jpg",
},
{
id: "3",
url: "https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_preview_3.jpg",
},
{
id: "4",
url: "https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_preview_4.jpg",
},
{
id: "5",
url: "https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/uploads/goods_preview_5.jpg",
},
],
};
},
methods: {
onPreviewImage(url) {
// 大图预览
uni.previewImage({
urls: this.pictures.map((v) => v.url), //放置预览的数组
current: url, //当前预览的url
});
},
},
};
</script>
<style>
.banner,
.banner image {
width: 750rpx;
height: 750rpx;
}
</style>
1.5 命令行创建 uni-app 项目
vue3 + ts 版
# 通过 npx 从 github 下载
npx degit dcloudio/uni-preset-vue#vite-ts 项目名称
# 通过 git 从 gitee 克隆下载 (👉备用地址)
git clone -b vite-ts https://gitee.com/dcloud/uni-preset-vue.git
编译和运行 uni-app 项目
安装依赖
SHpnpm install
编译成微信小程序
SHpnpm dev:mp-weixin
导入微信开发者工具
温馨提示 编译成 H5 端可运行
pnpm dev:h5
通过浏览器预览项目。
1.6 用 VS Code 开发 uni-app 项目
VS Code 开发配置
👉 前置工作:安装 Vue3 插件,点击查看官方文档
- 安装 Vue Language Features (Volar) :Vue3 语法提示插件
- 安装 TypeScript Vue Plugin (Volar) :Vue3+TS 插件
- 工作区禁用 Vue2 的 Vetur 插件(Vue3 插件和 Vue2 冲突)
- 工作区禁用 @builtin typescript 插件(禁用后开启 Vue3 的 TS 托管模式)
👉 安装 uni-app 开发插件
- uni-create-view :快速创建 uni-app 页面
- uni-helper uni-app :代码提示
- uniapp 小程序扩展 :鼠标悬停查文档
👉 TS 类型校验
安装 类型声明文件
shpnpm i -D miniprogram-api-typings @uni-helper/uni-app-types
配置
tsconfig.json
👉 JSON 注释问题
- 设置文件关联,把
manifest.json
和pages.json
设置为jsonc
- 设置文件关联,把
tsconfig.json
参考
// tsconfig.json
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"lib": ["esnext", "dom"],
// 类型声明文件
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types" // uni-app 组件类型
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
// 原配置 `experimentalRuntimeMode` 现调整为 `nativeTags`
"nativeTags": ["block", "component", "template", "slot"]
//"experimentalRuntimeMode": "runtime-uni-app"
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
工作区设置参考
// .vscode/settings.json
{
// 在保存时格式化文件
"editor.formatOnSave": true,
// 文件格式化配置
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 配置语言的文件关联
"files.associations": {
"pages.json": "jsonc", // pages.json 可以写注释
"manifest.json": "jsonc" // manifest.json 可以写注释
}
}
danger 版本升级
- 原依赖
@types/wechat-miniprogram
现调整为 miniprogram-api-typings。 - 原配置
experimentalRuntimeMode
现调整为nativeTags
。
这一步处理很关键,否则 TS 项目无法校验组件属性类型。
1.7 配置 eslint/prettier
安装 eslint 和 prettier
pnpm i -D eslint prettier eslint-plugin-vue @vue/eslint-config-prettier @vue/eslint-config-typescript @rushstack/eslint-patch @vue/tsconfig
根目录下新建.eslintrc.cjs
文件
/* 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,
getApp: true,
UniApp: true,
UniHelper: true,
App: true,
Page: true,
Component: 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"],
},
};
根目录新建prettierrc.cjs
{
"singleQuote": true,
"semi": false,
"printWidth": 100,
"trailingComma": "all",
"endOfLine": "auto"
}
配置 package.json
{
"script": {
// ... 省略 ...
"lint": "eslint . --ext .vue,.js,.ts --fix --ignore-path .gitignore"
}
}
运行
pnpm lint
1.8 配置 husky 和 lint-staged
安装并初始化
pnpm dlx husky-init //安装初始化husky
pnpm i -D lint-staged //安装lint-staged
配置 package.json
{
"script": {
// ... 省略 ...
"lint-staged": "lint-staged"
},
"lint-staged": {
"*.{vue,ts,js}": ["eslint --fix"]
}
}
修改 husky 的 pre-commit 文件
npm run lint-staged
2.拉取小兔鲜模板代码
模板已经内置了 4 个 tabbar,和 eslint/prettier/husky
git clone -b template https://gitee.com/heima-fe/uniapp-shop-vue3-ts.git 项目名称
pnpm i
pnpm dev:mp-weixin
注意:需要自己添加 appid
3.引入 uni-ui
pnpm i @dcloudio/uni-ui
配置自动导入组件
// pages.json
{
// 组件自动导入
"easycom": {
"autoscan": true,
"custom": {
// uni-ui 规则如下配置
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [
// …省略
]
}
安装类型声明文件
现在的 uni-ui 已经自己发布了 TS 类型声明文件,所以不用配置了。
pnpm i -D @uni-helper/uni-ui-types
配置类型声明文件
// tsconfig.json
{
"compilerOptions": {
// ...
"types": [
"@dcloudio/types", // uni-app API 类型
"miniprogram-api-typings", // 原生微信小程序类型
"@uni-helper/uni-app-types", // uni-app 组件类型
"@uni-helper/uni-ui-types" // uni-ui 组件类型
]
},
// vue 编译器类型,校验标签类型
"vueCompilerOptions": {
"nativeTags": ["block", "component", "template", "slot"]
}
}
4.配置 pinia
在小程序中,是没有网页端的 Localstorage 对象 API 的,所以需要调整一下小程序中的持久化插件的 API。
安装 pinia
pnpm i pinia //安装pinia
pnpm i pinia-plugin-persistedstate //pinia持久化
stores/index.ts
import { createPinia } from "pinia";
import persist from "pinia-plugin-persistedstate";
// 创建 pinia 实例
const pinia = createPinia();
// 使用持久化存储插件
pinia.use(persist);
// 默认导出,给 main.ts 使用
export default pinia;
// 模块统一导出
export * from "./modules/member";
main.ts
import { createSSRApp } from "vue";
import pinia from "./stores";
import App from "./App.vue";
export function createApp() {
const app = createSSRApp(App);
app.use(pinia);
return {
app,
};
}
stores/module/member.ts
import { defineStore } from "pinia";
import { ref } from "vue";
// 定义 Store
export const useMemberStore = defineStore(
"member",
() => {
// 会员信息
const profile = ref<any>();
// 保存会员信息,登录时使用
const setProfile = (val: any) => {
profile.value = val;
};
// 清理会员信息,退出时使用
const clearProfile = () => {
profile.value = undefined;
};
// 记得 return
return {
profile,
setProfile,
clearProfile,
};
},
// TODO: 持久化
{
persist: {
storage: {
setItem(key: string, value: string) {
uni.setStorageSync(key, value);
},
getItem(key: string) {
return uni.getStorageSync(key);
},
},
},
}
);
多端的持久化 API
// 网页端API
localStorage.setItem()
localStorage.getItem()
多端持久化 API
// 兼容多端API
uni.setStorageSync()
uni.getStorageSync()
5.配置请求工具
5.1 请求拦截器--配置 baseUrl 和 header
- 拼接基础地址
- 设置超时时间
- 添加请求头标识
- 添加 token
src/utils/http/request.ts
uni.addInterceptor(api,config) 可以拦截 uni 的 api。
// 请求基地址
const baseURL = "https://pcapi-xiaotuxian-front-devtest.itheima.net";
// 拦截器配置
const httpInterceptor = {
// 拦截请求 options:拦截这一次请求的请求参数,相当于axios请求拦截器的congig
invoke(options: UniApp.RequestOptions) {
// 1. 非 http 开头需拼接地址
if (!options.url.startsWith("http")) {
options.url = baseURL + options.url;
}
// 2. 请求超时
options.timeout = 10000;
// 3. 添加小程序端请求头标识
options.header = {
"source-client": "miniapp",
...options.header,
};
// 4. 添加 token 请求头标识
const memberStore = useMemberStore();
const token = memberStore.profile?.token;
if (token) {
options.header.Authorization = token;
}
},
};
// 拦截 request 请求
uni.addInterceptor("request", httpInterceptor); //相当于拦截所有的uni.request
// 拦截 uploadFile 文件上传
uni.addInterceptor("uploadFile", httpInterceptor); //相当于拦截所有的uni.uploadFile
常见问题
问: 为什么用手机预览没有数据?
答: 微信小程序端,需登录 微信公众平台 配置以下地址为合法域名 👇
https://pcapi-xiaotuxian-front-devtest.itheima.net
5.2 二次封装 request
utils/http/request.ts
import { checkStatus } from "./checlStatus";
import { useMemberStore } from "@/stores/modules/member";
const baseUrl = "https://pcapi-xiaotuxian-front-devtest.itheima.net";
// 小程序拦截器
const httpConfig = {
//拦截请求
invoke(options: UniApp.RequestOptions) {
//配置baseurl
if (!(options.url.startsWith("http") || options.url.startsWith("https"))) {
options.url = baseUrl + options.url;
}
// 设置超时时间
options.timeout = 5000;
// 设置请求头
options.header = {
"source-client": "miniapp",
...options.header,
};
// 设置token
const memberStore = useMemberStore();
const token = memberStore.profile?.token;
if (token) {
options.header.Authorization = token;
}
console.log("拦截请求", options);
},
};
uni.addInterceptor("request", httpConfig); //拦截所有的uni.request()
uni.addInterceptor("upload", httpConfig); //拦截小程序的upload上传
// 二次封装
export interface Data<T> {
code: string;
msg: string;
result: T;
}
export const http = <T>(options: UniApp.RequestOptions) => {
return new Promise<Data<T>>((resolve, reject) => {
uni.request({
...options,
// 响应成功
success(res: UniApp.RequestSuccessCallbackResult) {
if (res.statusCode >= 200 && res.statusCode <= 300) {
resolve(res.data as Data<T>);
} else {
checkStatus(res.statusCode, (res.data as Data<T>).msg, reject, res);
}
},
// 响应失败
fail(err) {
uni.showToast({
icon: "none",
title: "网络错误,换个网络试试",
});
reject(err);
},
});
});
};
checkStatus.ts
import { useMemberStore } from "@/stores";
const memberStore = useMemberStore();
export function checkStatus(
status: any,
errmsg: string,
reject: (reason?: any) => void,
res: UniApp.RequestSuccessCallbackResult
) {
let errMessage: string = `${errmsg || "请求错误"}`;
switch (status) {
case 400:
break;
// 401
case 401:
errMessage = "token过期";
memberStore.clearProfile();
uni.navigateTo({ url: "/pages/login/login" });
break;
case 403:
// errMessage = "403";
break;
// 404请求不存在
case 404:
// errMessage = "请求页面不存在";
break;
case 405:
// errMessage = "405";
break;
case 408:
// errMessage = "408";
break;
case 500:
// errMessage = "服务器错误";
break;
case 501:
// errMessage = "服务器错误";
break;
case 502:
// errMessage = "服务器错误";
break;
case 503:
// errMessage = "服务器错误";
break;
case 504:
// errMessage = "服务器错误";
break;
case 505:
// errMessage = "服务器错误";
break;
default:
errMessage = "无网络";
}
uni.showToast({
icon: "none",
title: errMessage,
});
reject(res);
}
6.【拓展】代码规范
为什么需要代码规范
如果没有统一代码风格,团队协作不便于查看代码提交时所做的修改。
6.1 统一代码风格 ESLint/Prettier
- 安装
eslint
+prettier
pnpm i -D eslint prettier eslint-plugin-vue @vue/eslint-config-prettier @vue/eslint-config-typescript @rushstack/eslint-patch @vue/tsconfig
- 新建
.eslintrc.cjs
文件,添加以下eslint
配置
/* 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,
getApp: true,
UniApp: true,
UniHelper: true,
App: true,
Page: true,
Component: 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"],
},
};
- 配置
package.json
{
"script": {
// ... 省略 ...
"lint": "eslint . --ext .vue,.js,.ts --fix --ignore-path .gitignore"
}
}
- 运行
pnpm lint
温馨提示
到此,你已完成 eslint
+ prettier
的配置。
6.2 Git 工作流规范--husky/lint-staged
- 安装并初始化
husky
pnpm dlx husky-init
npx husky-init
- 安装
lint-staged
pnpm i -D lint-staged
- 配置
package.json
{
"script": {
// ... 省略 ...
},
"lint-staged": {
"*.{vue,ts,js}": ["eslint --fix"]
}
}
- 修改
.husky/pre-commit
文件
npm test
npm run lint-staged
温馨提示
到此,你已完成 husky
+ lint-staged
的配置。