uniapp基础配置

149 阅读9分钟

vu3+pinia+uni-UI——Ts

工程结构解析

├── .husky                     # Git Hooks
├── .vscode                    # VS Code 插件 + 设置
├── dist                       # 打包文件夹(可删除重新打包)
├── src                        # 源代码
│   ├── components             # 全局组件
│   ├── composables            # 组合式函数
|   |———pagesMember            #分包页面
│   ├── pages                  # 主包页面
│       ├── index               # 首页
│       ├── category            # 分类页
│       ├── cart                # 购物车
│       ├── my                  # 我的
│       └── login               # 登录页
│   ├── services               # 所有请求
│   ├── static                 # 存放应用引用的本地静态资源的目录
│       ├── images              # 普通图片
│       └── tabs                # tabBar 图片
│   ├── stores                 # 全局 pinia store
│       ├── modules             # 模块
│       └── index.ts            # store 入口
│   ├── styles                 # 全局样式
│       └── fonts.scss          # 字体图标
│   ├── types                  # 类型声明文件
│       └── component.d.ts      # 全局组件类型声明
│   ├── utils                  # 全局方法(比如请求封装)
│   ├── App.vue                # 入口页面
│   ├── main.ts                # Vue初始化入口文件
│   ├── pages.json             # 配置页面路由等页面类信息
│   ├── manifest.json          # 配置appid等打包信息
│   └── uni.scss               # uni-app 内置的常用样式变量
├── .editorconfig              # editorconfig 配置
├── .eslintrc.cjs              # eslint 配置
├── .prettierrc.json           # prettier 配置
├── .gitignore                 # git 忽略文件
├── index.html                 # H5 端首页
├── package.json               # package.json 依赖
├── tsconfig.json              # typescript 配置
└── vite.config.ts             # vite 配置
  1. 运行程序
# 微信小程序端
npm run dev:mp-weixin

# H5端
npm run dev:h5

# App端
需 HbuilderX 工具,运行 - 运行到手机或模拟器

1.创建

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

uni-app官网 (dcloud.net.cn)

我们先来认识 uni-app 项目的目录结构。

├─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内置的常用样式变量

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)

2.命令行创建 uni-app 项目

优势

通过命令行创建 uni-app 项目,不必依赖 HBuilderX,TypeScript 类型支持友好。

创建其他版本可查看:uni-app 官网

常见问题

编译和运行 uni-app 项目

  1. 安装依赖 pnpm install
  2. 编译成微信小程序 pnpm dev:mp-weixin
  3. 导入微信开发者工具

温馨提示

编译成 H5 端可运行 pnpm dev:h5 通过浏览器预览项目。

3.用 VS Code 开发 uni-app 项目

为什么选择 VS Code?

  • VS Code 对 TS 类型支持友好,前端开发者主流的编辑器
  • HbuilderX 对 TS 类型支持暂不完善,期待官方完善 👀

用 VS Code 开发配置

项目采用 Vue3 + TS 开发 uni-app 项目,所以需要分别安装 Vue3 + TS 插件 和 uni-app 插件。

安装 Vue3 + TS 插件

注意事项

没开发过 Vue3 + TS 项目的小伙伴注意,需要先安装 Vue3 和 TS 的插件,并完成以下配置 👇

配合微信开发者工具使用

  1. 运行程序
# 微信小程序端
npm run dev:mp-weixin

# H5端
npm run dev:h5

# App端
需 HbuilderX 工具,运行 - 运行到手机或模拟器

微信开发者工具导入 /dist/dev/mp-weixin 打包目录,可以预览

2.配置uni-ui组件

1.安装组件库

pnpm i @dcloudio/uni-ui   或   yarn add @dcloudio/uni-ui

组件使用的入门教程 | uni-app官网 (dcloud.net.cn)

2.配置全局自动导入 配置easycom(组件自动导入),就不用自己导入了

使用 npm 安装好 uni-ui 之后,需要配置 easycom 规则,让 npm 安装的组件支持 easycom

打开项目根目录下的 pages.json 并添加 easycom 节点:

//组件自动引入规则
  "easycom": {
    "autoscan": true,
    "custom": {
      // uni-ui 规则如下配置 以 uni 开头的组件,在 components 目录中自动查找
      "^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
      // 以 Xtx 开头的组件,在 components 目录中自动查找自定义的组件
      "^Xtx(.*)": "@/components/Xtx$1.vue"
    }
  },
  "pages": [

配置pinia -持久化-请求封装-1.拦截器2.请求函数

为什么封装?

uni.request的success函数只表示 服务器请求成功,没处理响应状态码,业务中使用不方便

axios函数是状态码是2xx,才调用resolve函数,表示获取数据成功,业务中使用更精确

// src/utils/http.ts

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. 请求超时
    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)
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor)
//小程序中普通请求,和uploadFile 文件上传请求要分开

/**
 * 请求函数
 * @param  UniApp.RequestOptions
 * @returns Promise
 *  1. 返回 Promise 对象,用于处理返回值类型
 *  2. 获取数据成功
 *    2.1 提取核心数据 res.data
 *    2.2 添加类型,支持泛型
 *  3. 获取数据失败
 *    3.1 401错误  -> 清理用户信息,跳转到登录页
 *    3.2 其他错误 -> 根据后端错误信息轻提示
 *    3.3 网络错误 -> 提示用户换网络
 */

//封装以http取代uni.request,返回的是promise 方便async await
// 定义泛型
type 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) {
        // 状态码 2xx,参考 axios 的设计,uni没实现
        if (res.statusCode >= 200 && res.statusCode < 300) {
          //提取核心数据
          //401是有服务器响应,要在success判断
          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)
      },
    })
  })
}


```js
自定义导航配置

json

自定义导航配置

Snipaste_2023-11-05_21-56-39.png

// src/pages.json
{
  "path": "pages/index/index",
  "style": {
    "navigationStyle": "custom", // 隐藏默认导航
    "navigationBarTextStyle": "white",
    "navigationBarTitleText": "首页"
  }
}

动态设置安全距离

Snipaste_2023-11-05_21-53-26.png

<!-- src/pages/index/componets/CustomNavbar.vue -->
<script>
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>

<template>
  <!-- 顶部占位 -->
  <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
    <!-- ...省略 -->
  </view>
</template>

自定义导航栏,位置不随滚动而滚动

利用scroll-view flex

Snipaste_2023-11-06_15-59-34.png

在类型定义中,对应通用ts类型,可以用泛型进行复用,泛型相当于参数

微信图片_20231106163245.jpg

触底发起请求,获取数据

我们将请求定义在组件上,而不是定义在页面上?

因为我们还需要使用,就不用再次请求了,而是每当使用组件就发起请求

在滚动触底调用方法 @scrolltolower="onScrolltolower"

父调子中的方法,利用模板ref获取组件实例

子组件内部方法默认封闭,需要暴露

//暴露
defineExpose({
    getMore: getHomeGoodsGuessLikeData  //名字太长,给个别名
})

微信图片_20231106165304.jpg

分页加载

传递参数

步骤 微信图片_20231106171410.jpg 升级API函数 Snipaste_2023-11-06_17-16-55.png

Snipaste_2023-11-06_17-12-39.png

分页条件

加载页码超过页总数,后端没有数据是空数组,再发请求不合理,所以要加分页条件

优化

Snipaste_2023-11-06_17-32-16.png

微信图片_20231106173339.jpg

Snipaste_2023-11-06_17-36-34.png

下拉刷新

基于滚动容器的

下拉刷新实际上是在用户操作下拉交互时重新调用接口,然后将新获取的数据再次渲染到页面中。

操作步骤

基于 scroll-view 组件实现下拉刷新,需要通过以下方式来实现下拉刷新的功能。

  • 配置 refresher-enabled 属性,开启下拉刷新交互
  • 监听 @refresherrefresh 事件,监听用户是否执行了下拉操作
  • 配置 refresher-triggered 属性,关闭下拉状态(自己不会自动关闭)

微信图片_20231106174523.jpg

Snipaste_2023-11-06_17-54-49.png

Snipaste_2023-11-06_17-48-55.png

Snipaste_2023-11-06_18-06-36.png

使用async await 等待请求执行完,再往下执行,必须等第一个,然后第二个。。。。。

Snipaste_2023-11-06_18-09-53.png

性能优化

** Promise.all() 让这些请求同时走,都走完再往后走**

Snipaste_2023-11-06_18-17-49.png

Snipaste_2023-11-06_18-19-08.png

下拉刷新-猜你喜欢组件

对于组件自身维护数据,刷新要重置数据,重置的在子组件,要父调子

微信图片_20231106182329.jpg

Snipaste_2023-11-06_18-29-31.png

使用

Snipaste_2023-11-06_18-32-50.png

骨架屏

利用微信开发者工具生成

在vue中使用与原生微信小程序不同

微信图片_20231106192834.jpg

1.分离窗口

Snipaste_2023-11-06_19-34-10.png

Snipaste_2023-11-06_19-34-51.png

Snipaste_2023-11-06_19-43-00.png

Snipaste_2023-11-06_19-47-03.png

5.使用个变量判断加载状态

Snipaste_2023-11-06_20-00-28.png

Snipaste_2023-11-06_20-00-55.png 做tabbar改变样式 1. Snipaste_2023-11-07_14-32-08.png

Snipaste_2023-11-07_14-30-58.png

一级分类获取二级分类

Snipaste_2023-11-07_14-49-11.png

向组件传参数

Snipaste_2023-11-07_15-15-51.png

自定义事件

实现子调父,自定义事件可以实现子组件给父组件传递数据

父组件内部给子组件绑定一个自定义事件,在子组件内部触发这个自定义事件

Snipaste_2023-11-07_17-12-59.png

登录

真实开发

微信图片_20231107192900.jpg

Snipaste_2023-11-07_19-27-05.png

微信图片_20231107192818.jpg

设置分包

对不常使用的进行分包,例如设置

  1. 按模块管理页面,方便项目维护。
  2. 减少主包体积,用到的时候再加载分包,属于性能优化解决方案。

小程序分包:将小程序代码分割成多个部分,分别打包成多个小程序包,减少小程序加载时间。分包的默认不加载,使用时再加载,但使用时还需等待?

优化:

分包预下载:在进入小程序某个页面时,由框架自动预下载可能需要的分包,提高进入后续分包页面时的启动速度

步骤 1.新建分包页面 2.配置分包预下载

新建文件夹pagesMember用来放分包页面

不放在pages里,防止与主包混淆

Snipaste_2023-11-07_20-39-49.png

分包:按照项目业务模块划分,如会员模块分包,订单模块分包

Snipaste_2023-11-07_21-10-15.png

表单验证

利用uni-forms/uni-forms-item组件

Snipaste_2023-11-08_22-21-36.png

Snipaste_2023-11-09_10-18-02.png

插件市场

DCloud 插件市场

小程序tabbar页:小程序跳转tabbar页,会关闭其他非tabbar页页面,所以小程序tabbar页没有后退按钮,想实现跳转tabbar页,我们要再建个一模一样的普通页,