一、创建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,否则小程序端会报错
4. navigator组件在h5端渲染包裹了一层a标签
导致样式错乱
小程序端没有
解决办法:加render-link:false
5. app端没有渲染un-page-body
导致app端display:flex;没生效,scroll-view高度有问题,滚动的时候把顶部固定部分滑走了;
app端外层是id=app的容器
解决:
十六、跨端组件兼容
1. button type=open-type一些能力

只有小程序支持
2. navigator open-type=navigate,在h5及app端会多渲染一层a标签


解决:选中类名,再设置一次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