Skip to content

uni-app 基础

1.创建项目

uni-app 支持两种方式创建项目:

  1. 通过 HBuilderX 创建(需安装 HBuilderX 编辑器)
  2. 通过命令行创建(需安装 NodeJS 环境)

1.1 HBuilderX 创建 uni-app 项目

1.下载安装 HbuilderX 编辑器

2.通过 HbuilderX 创建 uni-app vue3 项目

3.安装 uni-app vue3 编译器插件

4.编译成微信小程序端代码

5.开启服务端口

小技巧分享:模拟器窗口分离和置顶

HBuildeX 和 微信开发者工具 关系

温馨提示

HBuildeXuni-app 都属于 DCloud 公司的产品。

1.2 pages.json 和 tabBar 案例

目录结构

sh
├─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 等页面类信息

代码:

json
{
  // 页面路由
  "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 规范:

  1. 属性绑定 src="{ { url }}" 升级成 :src="url"
  2. 事件绑定 bindtap="eventName" 升级成 @tap="eventName"支持()传参
  3. 支持 Vue 常用指令 v-forv-ifv-showv-model

其他区别补充

  1. 调用接口能力,建议前缀 wx 替换为 uni ,养成好习惯,支持多端开发
  2. <style> 页面样式不需要写 scoped,小程序是多页面应用,页面样式自动隔离
  3. 生命周期分三部分:应用生命周期(小程序),页面生命周期(小程序),组件生命周期(Vue)

1.4 案例:轮播图与预览图片

主要功能

  1. 滑动轮播图
  2. 点击大图预览

参考代码

vue
<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 版

sh
# 通过 npx 从 github 下载
npx degit dcloudio/uni-preset-vue#vite-ts 项目名称
sh
# 通过 git 从 gitee 克隆下载 (👉备用地址)
git clone -b vite-ts https://gitee.com/dcloud/uni-preset-vue.git

编译和运行 uni-app 项目

  1. 安装依赖

    SH
    pnpm install
  2. 编译成微信小程序

    SH
    pnpm dev:mp-weixin
  3. 导入微信开发者工具

    温馨提示 编译成 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 类型校验

    • 安装 类型声明文件

      sh
      pnpm i -D miniprogram-api-typings @uni-helper/uni-app-types
    • 配置 tsconfig.json

  • 👉 JSON 注释问题

    • 设置文件关联,把 manifest.jsonpages.json 设置为 jsonc

tsconfig.json 参考

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"]
}

工作区设置参考

json
// .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

sh
pnpm i -D eslint prettier eslint-plugin-vue @vue/eslint-config-prettier @vue/eslint-config-typescript @rushstack/eslint-patch @vue/tsconfig

根目录下新建.eslintrc.cjs文件

js
/* 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

ts
{
  "singleQuote": true,
  "semi": false,
  "printWidth": 100,
  "trailingComma": "all",
  "endOfLine": "auto"
}

配置 package.json

json
{
  "script": {
    // ... 省略 ...
    "lint": "eslint . --ext .vue,.js,.ts --fix --ignore-path .gitignore"
  }
}

运行

sh
pnpm lint

1.8 配置 husky 和 lint-staged

安装并初始化

sh
pnpm dlx husky-init		//安装初始化husky
pnpm i -D lint-staged	//安装lint-staged

配置 package.json

json
{
  "script": {
    // ... 省略 ...
    "lint-staged": "lint-staged"
  },
  "lint-staged": {
    "*.{vue,ts,js}": ["eslint --fix"]
  }
}

修改 husky 的 pre-commit 文件

sh
npm run lint-staged

2.拉取小兔鲜模板代码

模板已经内置了 4 个 tabbar,和 eslint/prettier/husky

sh
git clone -b template https://gitee.com/heima-fe/uniapp-shop-vue3-ts.git 项目名称
pnpm i
pnpm dev:mp-weixin

注意:需要自己添加 appid

3.引入 uni-ui

sh
pnpm i @dcloudio/uni-ui

配置自动导入组件

ts
// pages.json
{
  // 组件自动导入
  "easycom": {
    "autoscan": true,
    "custom": {
      // uni-ui 规则如下配置
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
    }
  },
  "pages": [
    // …省略
  ]
}

安装类型声明文件

现在的 uni-ui 已经自己发布了 TS 类型声明文件,所以不用配置了。

sh
pnpm i -D @uni-helper/uni-ui-types

配置类型声明文件

sh
// 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

sh
pnpm i pinia 						//安装pinia
pnpm i pinia-plugin-persistedstate  //pinia持久化

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;

// 模块统一导出
export * from "./modules/member";

main.ts

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

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

ts
// 网页端API
localStorage.setItem()
localStorage.getItem()

多端持久化 API
// 兼容多端API
uni.setStorageSync()
uni.getStorageSync()

5.配置请求工具

5.1 请求拦截器--配置 baseUrl 和 header

  1. 拼接基础地址
  2. 设置超时时间
  3. 添加请求头标识
  4. 添加 token

src/utils/http/request.ts

uni.addInterceptor(api,config) 可以拦截 uni 的 api。

ts
// 请求基地址
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

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

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.【拓展】代码规范

为什么需要代码规范

如果没有统一代码风格,团队协作不便于查看代码提交时所做的修改。

diff

6.1 统一代码风格 ESLint/Prettier

  • 安装 eslint + prettier
sh
pnpm i -D eslint prettier eslint-plugin-vue @vue/eslint-config-prettier @vue/eslint-config-typescript @rushstack/eslint-patch @vue/tsconfig
  • 新建 .eslintrc.cjs 文件,添加以下 eslint 配置
js
/* 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
json
{
  "script": {
    // ... 省略 ...
    "lint": "eslint . --ext .vue,.js,.ts --fix --ignore-path .gitignore"
  }
}
  • 运行
sh
pnpm lint

温馨提示

到此,你已完成 eslint + prettier 的配置。

6.2 Git 工作流规范--husky/lint-staged

  • 安装并初始化 husky
sh
pnpm dlx husky-init
sh
npx husky-init
  • 安装 lint-staged
sh
pnpm i -D lint-staged
  • 配置 package.json
json
{
  "script": {
    // ... 省略 ...
  },
  "lint-staged": {
    "*.{vue,ts,js}": ["eslint --fix"]
  }
}
  • 修改 .husky/pre-commit 文件
diff
npm test
npm run lint-staged

温馨提示

到此,你已完成 husky + lint-staged 的配置。

MIT License