uniapp 跨端开发

一、创建uniapp项目

1. 项目架构

2. 初始化项目







3. pages.json 和 tabBar 案例



4. uni-app和原生小程序开发区别

5. 用命令行创建uni-app项目

官网链接:https://uniapp.dcloud.net.cn/quickstart-cli.html#创建uni-app


6. 用vscode开发uni-app




在uni-app中只有manifest.json和pages.json这两个json文件允许写注释,按如下添加后,这两个文件的注释就不报错了

7. 拉取项目模板

小兔鲜儿

这里填入appid

项目模板中已配置好了下面的第8、第9点

8. 额外:项目基础配置统一代码风格

  • 安装 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

::: tip 温馨提示

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

:::

9. 额外:Git 工作流规范

  • 安装并初始化 husky

::: code-group

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   // [!code --]
npm run lint-staged     // [!code ++]

::: tip 温馨提示

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

:::

二、基础架构

1. 引入uni-ui

ts类型如果之前已经配置过了则忽略

2. 小程序端 Pinia 持久化

配置示例

3. 请求封装

1. 添加请求和上传文件拦截器


2. 封装promise请求函数






http.ts 添加 http 请求函数及类型

javascript 复制代码
import { useMemberStore } from '@/stores'

const baseURL = 'https://pcapi-xiaotuxian-front-devtest.itheima.net'

// 拦截器
const httpInterceptor = {
  // 拦截前触发
  invoke(options: UniApp.RequestOptions) {
    // 1. 非 http 开头需拼接地址
    if (!options.url.startsWith('http')) {
      options.url = baseURL + options.url
    }
    // 2. 请求超时, 默认 60s
    options.timeout = 10000
    // 3. 添加小程序端请求头标识
    options.header = {
      ...options.header,
      'source-client': 'miniapp',
    }
    // 4. 添加 token 请求头标识
    const memberStore = useMemberStore()
    const token = memberStore.profile?.token
    if (token) {
      options.header.Authorization = token
    }
    console.log(24, options)
  },
}
uni.addInterceptor('request', httpInterceptor)
uni.addInterceptor('uploadFile', httpInterceptor)

/**
 * 请求函数
 * @param  UniApp.RequestOptions
 * @returns Promise
 *  1. 返回 Promise 对象
 *  2. 获取数据成功
 *    2.1 提取核心数据 res.data
 *    2.2 添加类型,支持泛型
 *  3. 获取数据失败
 *    3.1 401错误  -> 清理用户信息,跳转到登录页
 *    3.2 其他错误 -> 根据后端错误信息轻提示
 *    3.3 网络错误 -> 提示用户换网络
 */
type Data<T> = {
  code: string
  msg: string
  result: T
}
// 2.2 添加类型,支持泛型
export const http = <T>(options: UniApp.RequestOptions) => {
  // 1. 返回 Promise 对象
  return new Promise<Data<T>>((resolve, reject) => {
    uni.request({
      ...options,
      // 响应成功
      success(res) {
        // 状态码 2xx, axios 就是这样设计的
        if (res.statusCode >= 200 && res.statusCode < 300) {
          // 2.1 提取核心数据 res.data
          resolve(res.data as Data<T>)
        } else if (res.statusCode === 401) {
          // 401错误  -> 清理用户信息,跳转到登录页
          const memberStore = useMemberStore()
          memberStore.clearProfile()
          uni.navigateTo({ url: '/pages/login/login' })
          reject(res)
        } else {
          // 其他错误 -> 根据后端错误信息轻提示
          uni.showToast({
            icon: 'none',
            title: (res.data as Data<T>).msg || '请求错误',
          })
          reject(res)
        }
      },
      // 响应失败
      fail(err) {
        uni.showToast({
          icon: 'none',
          title: '网络错误,换个网络试试',
        })
        reject(err)
      },
    })
  })
}

三、项目首页

1. 自定义导航

样式适配-预留安全区域

2. 通用轮播图组件

通用组件自动导入

配置自动导入

添加组件类型声明:目前添加组件声明后没效果

轮播图指示点:非空断言

轮播图接口数据类型

页面加载的生命周期onLoad需要从@dcloudio/uni-app中引入


3. 首页分类组件



4. 首页热门推荐组件



5. 首页猜你喜欢组件

多处会使用到猜你喜欢组件,所以样式及数据请求都放到组件内部,组件defineExpose暴露出方法供父组件调用

typeof XtxGuess 拿到的是组件的类型,InstanceType拿到的是组件的实例类型


由于类型PageParams中的page是可选的,下面pageParams.page会报错提示page可能未定义,解决办法是如下加上Required,将可选参数变成必填

6. 首页下拉刷新

refresher-enabled: 开启自定义下拉刷新,会出现对应的动画;

refresherrefresh 下拉刷新回调

下拉刷新开始,先重置猜你喜欢组件数据

7. 首页骨架屏


生成骨架屏,把html和样式抽到PageSkeleton组件,删除原生的注释部分、删除导航部分,只保留滚动部分

四、热门推荐

1. 动态设置热门推荐标题


2. 获取热门推荐数据

交叉类型: PageParams & { subType?: string }

3. 热门推荐类型定义



4. 热门推荐页面渲染和tab交互


5. 热门推荐分页加载

分页加载的条件判断

扩展字段;环境变量判断

五、商品分类

1. 商品分类轮播图

2. 渲染一级分类和Tab交互


3. 二级分类和商品渲染

4. 商品分类骨架屏

把骨架屏样式放到.vue文件的style中,把html和css中的注释删除,解决html中的报红

六、商品详情

1. 商品详情接口请求

defineProps接收页面传参

2. 详情页面渲染

3. 商品详情轮播图渲染

4. 商品详情弹出层

弹出层交互

联合类型;用typeof获取类型;子组件给父组件通信

七、登录模块

1. 小程序快捷登录

获取登录凭证code:https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.login.html

获取手机号:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html

调用后台登录接口成功示例

模拟快捷登录

2. 保存登录信息

如果这里不用延时,登录成功的提示就看不见了,因为switchTab()会自动关闭当前页面所有内容并跳转到 tabBar 页面


3. 会员信息渲染

4. 会员信息-猜你喜欢部分抽离成组合式函数

并复用到首页

5. 会员中心-设置页分包和预下载

6. 会员中心-退出登录

7. 会员中心-个人信息页

分包、自定义导航

个人信息页接口请求及数据类型

修改用户头像

uni.chooseMedia 调用拍照/本地相册

uni.uploadFile 上传文件,url为后端接口地址

修改用户昵称

v-model处去掉非空断言,定义profile时改用as 断言

更新Store信息

直接修改单个属性,不调用setProfile

code 数组的类型和长度都确定了(类型 [string, string, string],元组),这里定义fullLocationCode得定义成如下

修改性别

修改生日

八、地址模块

1. 动态设置标题

分包规则里置空

2. 新建地址

3. 地址列表

在onShow页面每次显示的钩子函数中调用(新增返回列表也要请求)

修改地址-回显
修改-保存地址

4. 地址表单校验


5. 删除地址

uni-swipe-action 滑动操作组件

九、SKU模块

1. SKU插件

SKU算法:https://juejin.cn/post/7002746459456176158


2. 渲染商品信息

打开弹窗交互-修改按钮模式

枚举按钮模式

选中值处理

从SKU组件内部获取选中值,绑定到商品信息页

3. 加入购物车

十、购物车模块

1. 购物车列表渲染

uni-swipe-action组件无法渲染,把@dcloudio/uni-ui从1.5.8降级到"@dcloudio/uni-ui": "1.4.23"

2. 删除购物车商品

3. 修改单品数量

4. 修改选中状态

5. 底部结算信息

6. 两个购物车页面

十一、订单模块

1. 填写订单-基本信息渲染

2. 填写订单-收货地址

3. 填写订单-立即购买

查看页面参数

4. 填写订单-提交订单

添加编译模式

十二、订单详情

1. 自定义导航栏交互

获取当前页面栈集合 getCurrentPages()

小程序滚动驱动动画

在页面渲染完成钩子onReady中调用

2.订单状态渲染

3. 待付款倒计时

uni-countdown组件

控制台报错uni-countdown.js被无依赖过滤,导致无法显示该组件,如下设置可解决

4. 订单支付

发起微信支付wx.requestPayment

关闭当前页然后跳转 uni.redirectTo

5. 订单发货

6. 确认收货

7. 订单物流渲染

8. 删除订单

十三、订单列表

1. Tabs滑动切换

swiper的current绑定activeIndex

Tabs传参高亮

2. 订单列表渲染

订单列表抽成了一个组件,请求列表接口在组件挂载生命周期onMounted中调用

3. 订单列表订单支付

这里更新订单状态,没有刷新接口而是前端查找对应数据修改状态

十四、项目打包

1.打包上传

2.网页端 pnpm dev:h5 开发调试

首页轮播图、商品详情图宽高有问题,没有自适应:首页轮播图 :host选择器在h5端不生效,修改如下

商品详情图宽高有问题,因为图片上的mode="aspectFill"没生效,加上image类:宽高100%

调用微信自有能力的地方都要修改,比如以wx.开头的都加上条件判断 #ifdef MP-WEIXIN #endif

搜索包含open-type的按钮,凡是调用微信能力的地方都加条件判断,注意路由跳转在h5是支持的,不需要改

3. 网页端打包 pnpm build:h5

build/h5/index.html 预览报错

原因是这里的路径是根路径

修改如下,加如下配置重新执行pnpm build:h5打包


4. android app 端预览及调试

用Hbuilder X 打开项目,按如下操作

注意:华为mate 40 pro USB选项选择MIDI

项目中的依赖@dcloudio/uni-app 与 Hbuilder X 版本不一致

复制如下命令,改成与Hbuilder X 一致的版本:npx @dcloudio/uvm@latest 4.7.5

在终端执行

重新运行

5. Android app 打正式包(云打包)

在HbuilderX中,打开manifest,获取AppId

在dcloud开发中心生成证书 https://dev.dcloud.net.cn/pages/app/list

5. IOS端预览调试打包

十五、跨端兼容样式

1. 通配符选择器在小程序端不生效

2. 页面视口差异导致

去结算部分,如果定位的bottom写0,在小程序端没问题,在h5端就会被下面的tabbar覆盖住,所以这里用的uni-app提供的内置css变量--window-bottom

3. h5端单页面应用,默认开启了样式隔离scoped

导致首页骨架屏样式错乱,解决办法把首页各个部分的样式抽离出来,在各部分及骨架屏组件中单独引入

注意下面的XtxSwiper.css改成.scss,引入的时候也改成.scss,否则小程序端会报错


导致样式错乱

小程序端没有

解决办法:加render-link:false

5. app端没有渲染un-page-body

导致app端display:flex;没生效,scroll-view高度有问题,滚动的时候把顶部固定部分滑走了;

app端外层是id=app的容器

解决:

十六、跨端组件兼容

1. button type=open-type一些能力

只有小程序支持

解决:选中类名,再设置一次flex: 1

十七、跨端JS API兼容

上传图片api,小程序用uni.chooseMedia,app和h5端使用uni.chooseImage

十八、uniClound 云开发

1. 准备工作

2. 完成城市选择

picker mode=region在app和h5端不支持,需要换成uni-data-picker,城市数据由云端提供

解决如下

加条件判断,覆盖默认样式

解决:加上clear-icon="false"

另外:修改时无法回显,加上v-model这句

在h5端,地址列表点击修改先跳转修改页瞬间有返回上一页了,解决:加上@tap.prevent