项⽬初始化
1.1创建项⽬与项⽬初始化
a:创建项⽬
在微信开发者⼯具的开始界⾯左侧检查项⽬类型,需要为 [⼩程序]。 然后在右侧点击 [+] 开始新建项⽬。 最后在弹出的界⾯中输⼊项⽬相关的信息,点击确定即可。
填写项目名称,目录中引入文件夹
填写自己的AppID
开发模式选择小程序,后端服务我们选择不使用云服务,模板选择JS-基础模板
b:项⽬初始化 重置
app.js 中的代码。
// app.js:小程序的项目入口文件
App({
})
删除 app.json 中 pages 下的 "rendererOptions" 以及 "componentFramework" 字段。
重置 app.wxss 中的代码。
删除 app.json 中 pages 下的 "pages/logs/logs" 路径,同时删除 pages/logs ⽂件夹。
删除 components 中的⾃定义组件。
重置 pages/index ⽂件夹下的 index.js 、 index.wxss 、 index.html 以及 index.json ⽂件。
更新 utils 下 util.js 的⽂件名为 formatTime.js。
1.2:⾃定义构建 npm + 集成Sass
随着项⽬的功能越来越多、项⽬越来越复杂,⽂件⽬录也变的很繁琐,为了⽅便进⾏项⽬的开发,开发 ⼈员通常会对⽬录结构进⾏调整优化,在慕尚花坊项⽬中, 我们就需要将⼩程序源码放到 miniprogram ⽬录下 。
a:⾃定义构建
"miniprogramRoot": "miniprogram/"
然后配置 project.config.json 的 setting.packNpmManually 为 true。
"setting": {
"packNpmManually":true,
...
},
初始化项⽬。
npm init -y
//⽣成package.json⽂件
最后配置 project.config.json 的 setting.packNpmRelationList 项,指定 packageJsonPath 和 miniprogramNpmDistDir 的位置。
setting": {
"packNpmManually":true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram"
}
],
},
// packageJsonPath 表示 node_modules 源对应的 package.json
// miniprogramNpmDistDir 表示 node_modules 的构建结果⽬标位置
安装 vant ,然后进⾏ npm 构建 ,测试是否能够正常 vant 构建成功。
npm i @vant/weapp
“⼯具”-“构建npm”。
b:集成 Sass
在 project.config.json ⽂件中,修改 setting 下的 useCompilerPlugins 字段为 ["sass"] ,即可开 启⼯具内置的 sass 编译插件。
"setting": {
"packNpmManually": true,
"packNpmRelationList": [
{
"packageJsonPath": "./package.json",
"miniprogramNpmDistDir": "./miniprogram"
}
],
"useCompilerPlugins":[
"sass"
],
...
},
更改wxss后缀名为sass
关闭并重新打开项⽬
构建项⽬⻚⾯
assets⽂件导⼊ 配置app.json⽂件
⾸⻚
1.1:⾸⻚结构
1.2:⾸⻚背景图
/**index.wxss**/
.index-container {
// ⾸⻚背景图
.window-bgc {
height: 200rpx;
width: 100%;
background-color: #f3514f;
border-radius:0 0 50% 50%;
}
}
1.3:banner组件
a:新建banner组件
b:构建banner组件结构
// index.html
<!-- 轮播图区域 -->
<banner />
c:完成轮播图组件
1.4:entrance组件
a:新建entrance组件
b:构建entrance组件
c:完成entrance组件
1.5:完成⼴告区域
1.6:注册goods-card和goods-list全局组件
a:完成goods-card组件
// components/goods-card/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
// 每⼀项商品的数据
goodItem: {
type: Object,
value: {}
}
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的⽅法列表
*/
methods: {}
})
b:完成goods-list组件
// components/goods-list/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
// 列表标题
title: {
type: String,
value: ''
},
// 传递的列表数据
list: {
type: Array,
value: [{}, {}, {}]
}
},
/**
* 组件的初始数据
*/
data: {},
/**
* 组件的⽅法列表
*/
methods: {}
})
1.7:完成⾸⻚⻚⾯
<!-- 商品列表 -->
<goods-list title="猜你喜欢"></goods-list>
<goods-list title="⼈⽓推荐"></goods-list>
{
"usingComponents": {
"goods-list": "/components/goods-list/goods-list",
"entrance": "./entrance/entrance",
"banner": "./banner/banner"
},
"navigationBarTitleText": "慕尚花坊"
}
分类⻚⾯
我的⻚⾯
1:阿⾥图标库引⼊
2:代码部分完成
配置my.js
// pages/my/my.js
Page({
/**
* 页面的初始数据
*/
data: {
// 初始化第二个面板数据
orderItem: [
{
url: '/pages/orders/orders',
title: '商品订单',
iconfont: 'icon-dingdan'
},
{
url: '/pages/order/list/list',
title: '礼品卡订单',
iconfont: 'icon-lipinka'
},
{
url: '/pages/order/list/list',
title: '退款/售后',
iconfont: 'icon-tuikuan'
}
]
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
}
})
配置my.wxml
<view class="container">
<!-- 顶部展示图 -->
<view class="top-show">
<image src="../../assets/images/banner.jpg" mode="widthFix" class="top-show-img" />
</view>
<!-- 白色背景面板 -->
<view class="bottom-show">
<!-- 未登录面板 -->
<view class="user-container">
<!-- 左边头像区域 -->
<view class="avatar-container">
<image src="../../assets/images/avatar.png" class="avatar" mode="" />
</view>
<!-- 右边文字区域 -->
<view class="no-login">
<text>未登录</text>
<text>点击授权登录</text>
</view>
</view>
<!-- 已登陆面板 -->
</view>
<!-- 订单面板 -->
<view class="order">
<!-- 订单面板标题部分 -->
<view class="order-title">
<text class="title">我的订单</text>
<text class="more">查看更多></text>
</view>
<!-- 订单面板内容部分 -->
<view class="order-content">
<view class="order-content-item" wx:for="{{orderItem}}" wx:key="index">
<!-- item指数组内的项 -->
<navigator url="{{item.url}}">
<view class="iconfont {{item.iconfont}}"></view>
<text>{{ item.title }}</text>
</navigator>
</view>
</view>
</view>
<!-- 关于售前售后服务面板 -->
<view class="after-scale">
<view class="order-title-wrap">
<text class="title">关于售前售后服务</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-kefufenxiermaikefu"></view>
<text>可与小程序客服实时聊天或电话咨询</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-shijian"></view>
<text>小程序客服工作时间为: 8:30 ~ 20:30</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-dizhiguanli"></view>
<text>鲜花制作完毕情况下暂不支持退款</text>
</view>
<view class="after-scale-item">
<view class="iconfont icon-zhangben"></view>
<text>鲜花可以提前7-15天预订重大节假日不支持定时配送</text>
</view>
</view>
<!-- 底部面板 -->
<view class="info-footer"> 智谷星图技术支持 </view>
</view>
配置my.scss
.container {
background-color: whitesmoke;
height: 100vh;
.top-show {
width: 100%;
height: 360rpx;
.top-show-img {
width: 100%;
height: 100%;
}
}
.bottom-show {
position: relative;
top:-100rpx;
border-radius: 20rpx;
margin: 0 auto;
width: 96%;
background-color: white;
.user-container {
display: flex;
align-items: center;
.avatar-container{
margin: 20rpx;
.avatar {
height: 120rpx;
width: 120rpx;
}
}
.no-login {
display: flex;
flex-direction: column;
font-size: 24rpx;
color: gray;
text:first-child {
font-size: 28rpx;
}
}
}
}
.order {
border-radius: 20rpx;
margin: 0 auto;
width: 96%;
background-color:white;
margin-top: -80rpx;
.order-title {
display: flex;
// justify-content: 设置主轴上的排列方式;
justify-content: space-between;
padding: 40rpx;
.more {
font-size: 30rpx;
color: #ccc;
}
}
.order-content {
display: flex;
justify-content: space-evenly;
text-align: center;
padding-bottom: 24rpx;
.iconfont {
font-size: 60rpx;
}
text {
font-size: 26rpx;
}
}
}
.after-scale {
border-radius: 20rpx;
margin: 0 auto;
width: 96%;
background-color:white;
margin: 20rpx;
padding-bottom: 25rpx;
.order-title-wrap {
padding: 20rpx 0 0 20rpx;
}
.after-scale-item {
display: flex;
margin: 30rpx 25rpx;
text {
font-size: 25rpx;
color: #999;
margin-left: 20rpx;
}
.iconfont {
color: #a2b364;
}
}
}
.info-footer {
text-align: center;
font-size: 24rpx;
color: #999;
margin-top: 30rpx;
}
}
“购物⻋⻚⾯”
1:vant组件库引⼊
2:代码部分完成
配置cart.js
// pages/cart/cart.js
Page({
/**
* 页面的初始数据
*/
data:{
carList:[1,2,3]
},
})
配置cart.wxml
<view class="container">
<!-- 购物车列表区域 -->
<view class="carList-container" wx:if="{{carList.length}}">
<view class="carList-container-cell" wx:for="{{ carList }}" wx:key="index">
<van-swipe-cell right-width="{{ 65 }}" left-width="{{ 65 }}">
<view slot="left" class="van-swipe-cell__left">选择</view>
<van-cell-group>
<view class="goods-info">
<view class="left">
<van-checkbox value="{{ false }}" checked-color="#e60017" bind:change="onChange">
</van-checkbox>
</view>
<view class="mid">
<image src="../../assets/images/floor-img.jpg" mode="" />
</view>
<view class="right">
<view class="title">[11支红玫瑰]买花就送女友送爱人送老婆</view>
<view class="buy">
<view class="price">¥99.99</view>
<view class="buy-btn">
<van-stepper value="{{ 1 }}" bind:change="onChange" />
</view>
</view>
</view>
</view>
</van-cell-group>
<view slot="right" class="van-swipe-cell__right">删除</view>
</van-swipe-cell>
</view>
</view>
<!-- 购物车列表为空的情况 -->
<van-empty description="还没有商品,快去添加吧" wx:else="" >
<navigator url="">
<van-button type="danger" round>去购物</van-button>
</navigator>
<navigator url="">
<van-button type="danger" round>去登录</van-button>
</navigator>
</van-empty>
<!-- 提交订单栏区域 -->
<van-submit-bar price="{{ 3050 }}" button-text="提交订单" bind:submit="onClickButton" tip="{{ true }}">
<van-checkbox value="{{ true }}" checked-color="#e60017" bind:change="onChange">
全选
</van-checkbox>
</van-submit-bar>
</view>
配置cart.scss
.container {
background-color: whitesmoke;
height: 100vh;
}
.carList-container {
.carList-container-cell {
.goods-info {
display: flex;
background-color: white;
width: 95%;
margin: 20rpx 20rpx 10rpx 20rpx;
border-radius: 16rpx;
padding: 24rpx 16rpx;
.left {
display: flex;
align-items: center;
}
.mid {
width: 300rpx;
height: 300rpx;
image {
width: 100%;
height: 100%;
}
}
.right {
display: flex;
flex-direction: column;
height: 300rpx;
padding-left: 20rpx;
// 钱99.99下滑
justify-content: space-between;
.title {
font-size: 26rpx;
}
.buy {
display: flex;
justify-content: space-between;
.price {
font-size: 30rpx;
color: red;
}
}
}
}
}
}
.van-empty__bottom{
height: 250rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
"商品列表⻚⾯"
1:新建Page
2:完成商品列表⻚⾯
<!-- 商品列表组件 -->
<view class="goods_container">
<!-- 标题 -->
<view class="goods_title">{{title}}</view>
<!-- 列表区域 -->
<view class="goods_card_list">
<block wx:for="{{ 4 }}" wx:key="index">
<goods-card />
</block>
</view>
<!-- 查看更多 -->
<view class="goods_more">
<navigator url="" class="goods_more_btn">
查看更多
</navigator>
</view>
</view>
.goods_container {
width: 95%;
margin: 0 auto;
.goods_title {
// 设置文本居中
text-align: center;
font-size: 48rpx;
font-weight: bold;
padding: 20rpx 0 0 0;
}
.goods_card_list {
display: flex;
// 设置换行
flex-wrap: wrap;
// 设置元素在主轴上的排列方式
justify-content: space-between;
}
.goods_more_btn {
height: 40px;
width: 95%;
background-color: white;
border-radius: 20px;
margin:0 auto;
// 以下三句话设置元素垂直居中显示
display: flex;
justify-content: center;
align-items: center;
}
.goods_more {
margin: 10px 0 0 0;
}
}
Component({
// 组件的属性列表
properties: {
// 声明需要从父组件传title值。
title:{
type:String,
value:'',
}
}
})
{
"usingComponents": {
"goods-card":"../goods-card/goods-card"
},
"component": true
}
"商品细节⻚⾯"
1:代码部分完成
<!--pages/detail/detail.wxml-->
<view class="container">
<!-- 商品大图 -->
<view class="banner-img">
<image src="../../assets/images/floor-img.jpg" mode="" />
</view>
<!-- 商品基本信息 -->
<view class="content">
<!-- 商品的价钱 -->
<view class="price">
<view class="price-num">¥299</view>
<view class="price-origin-unm">¥399</view>
</view>
<!-- 商品的标题 -->
<view class="title">99支红玫瑰/99支红玫瑰/99支红玫瑰</view>
<!-- 详细信息 -->
<view class="info">爱她,就送她99只玫瑰</view>
</view>
<!-- 商品详细信息 -->
<view class="detail">
<image src="../../assets/images/floor-img.jpg" mode="" />
<image src="../../assets/images/floor-img.jpg" mode="" />
<image src="../../assets/images/floor-img.jpg" mode="" />
</view>
<!-- 商品底部导航栏 -->
<van-goods-action>
<!-- 如果navigator标签跳转到tabber栏 则需要加open-type -->
<navigator url="/pages/index/index" open-type="switchTab">
<van-goods-action-icon icon="wap-home-o" text="主页" info="" />
</navigator>
<navigator url="/pages/cart/cart" open-type="switchTab">
<van-goods-action-icon icon="cart-o" text="购物车" info="" />
</navigator>
<navigator url="/pages/my/my" open-type="switchTab">
<van-goods-action-icon icon="contact-o" text="我的" />
</navigator>
<van-goods-action-button text="加入购物车" type="warning" bindtap="handelSheet" />
<!-- handelSheet为点击按钮事件 -->
<van-goods-action-button text="立即购买" bindtap="handelSheet" />
</van-goods-action>
<!-- 自定义盒子 -->
<!-- bind:close为面板关闭事件 -->
<van-action-sheet show="{{ show }}" title="" bind:close="onClose">
<view class="sheet-container">
<!-- 图片区域 -->
<view class="img">
<image src="../../assets/images/floor-img.jpg" mode="" />
</view>
<!-- <view class="zi">祝福语</view> -->
<!-- 商品基本信息区域 -->
<view class="baseInfo">
<!-- 商品名称 -->
<view class="title">亲爱的/情人节网红款/19枝</view>
<!-- 商品价钱 -->
<view class="buy">
<view class="price">
<view class="symbol">¥</view>
<view class="num">99.99</view>
</view>
<view class="buyBtn">
<van-stepper value="{{ 1 }}" bind:change="onChange" />
</view>
</view>
</view>
</view>
<!-- 商品祝福语区域 -->
<view class="textArea">
<view class="title">祝福语</view>
<!-- 占位符 -->
<textarea value="" placeholder="请输入您的祝福" class="box" />
</view>
<view class="btn">
<!-- round设置圆角弧度 -->
<van-button type="primary" round size="large">确定</van-button>
</view>
<!-- <view class="zhongjian">
<view class="qwe">祝福你</view>
</view>
<view class="queding">
<view class="abc">确定</view>
</view> -->
</van-action-sheet>
</view>
.container {
background-color: whitesmoke;
height: 100vh;
.banner-img {
// 高度
height: 800rpx;
image {
height: 100%;
}
}
// 商品基本信息
.content {
background-color: white;
// margin 设置框外边距
margin: 0 46rpx;
// padding 设置内边距
padding: 40rpx; //框内
// 设置圆角弧度
border-radius: 20rpx;
// 相对于原本的位置发生改变是相对定位
// 相对于父盒子位置发生改变是绝对定位
position: relative; //相对定位
top: -200rpx; //上下平移
.price {
// 将块级元素一行显示
display: flex;
// 299
.price-num {
color: red;
// 字体加粗
font-weight: bolder;
// 字体大小
font-size: 18px;
}
// 399
.price-origin-unm {
color: #b4babf;
text-decoration: line-through; //399数字有横线
// 字体大小
font-size: 12px;
margin-left: 15px; //399离299距离
margin-top: 5px; // 399数字上下
}
}
.title {
// 溢出隐藏
overflow: hidden;
// 超过一行就算溢出
white-space: nowrap;
// 溢出之后的文本
text-overflow: ellipsis;
font-size: 16px; // 字体大小
font-weight: bolder; // 字体加粗
}
.info {
color: #b4babf;
// 溢出隐藏
overflow: hidden;
// 超过一行就算溢出
white-space: nowrap;
// 溢出之后的文本
text-overflow: ellipsis;
font-size: 12px; // 字体大小
}
}
// 商品详细信息
.detail {
background-color: white;
padding: 20rpx; // padding 设置内边距
margin: -150rpx 20rpx 0 20rpx;
border-radius: 20rpx; // 设置圆角弧度
image {
width: 100%; // 框
height: 700rpx; //高
}
}
.sheet-container {
margin: 20rpx;
padding: 20rpx;
border-radius: 20rpx;
display: flex;
.img {
height: 400rpx;
width: 350rpx;
image {
width: 100%;
height: 100%;
}
}
.buy {
display: flex;
.price {
display: flex;
color: red;
}
}
.baseInfo {
display: flex;
// 设置主轴方向
flex-direction: column;
// 设置主轴上的排序列方式
justify-content: space-between;
margin-left: 20rpx;
.title {
font-size: 14px;
}
}
}
.textArea {
background-color: white;
margin: 20rpx 20rpx;
.title {
margin-left: 20rpx;
margin-bottom: 20rpx;
font-weight: bolder; // 字体加粗
}
.box {
background-color: whitesmoke;
width: 92%;
margin: 0 auto;
border-radius: 20rpx;
padding: 20rpx;
}
}
.btn {
width: 90%;
margin: 20rpx auto;
}
}
// pages/detail/detail.js
Page({
/**
* 页面的初始数据
*/
data: {
},
// 点击按钮事件,控制面板显示
handelSheet(){
this.setData({
show:true
})
},
onClose(){
this.setData({
show:false
})
}
})
集成项⽬⻚⾯
4.7:订单⻚⾯
<!--pages/orders/orders.wxml-->
<view class="order-container container">
<view class="order-list">
<view class="order-item" wx:for="{{3}}" wx:key="index">
<view class="order-item-header list-flex">
<view class="orderno">订单号2222222222<text></text></view>
<view class="order-status"> 已支付</view>
</view>
<view class="goods-item list-flex">
<view class="left">
<image src="../../assets/images/floor-img.jpg" mode="widthFix" class="img" />
</view>
<view class="mid">
<view class="goods-name">不变的承诺</view>
<view class="goods-blessing">不变的承诺</view>
</view>
<view class="right">
<view class="goods-price">¥299</view>
<view class="goods-count">x1</view>
</view>
</view>
<view class="order-item-footer">
<view class="total-amount list-flex">
<text class="text">实付:</text>
<text class="price"><text>¥</text>399</text>
</view>
</view>
</view>
</view>
</view>
/* pages/order/list/index.wxss */
.order-container{
background-color: whitesmoke;
.list-flex {
display: flex;
background-color: white;
}
.order-item {
margin-top: 16rpx;
padding: 24rpx 32rpx 24rpx;
background-color:white;
border-radius: 14rpx;
&:first-child {
margin: 0rpx;
}
.order-item-header {
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
}
}
.orderno {
font-size: 28rpx;
font-weight: normal;
color: #333333;
display: flex;
align-items: center;
line-height: 40rpx;
font-weight: bolder; // 字体加粗
.no {
margin-left: 6px;
}
}
.order-status {
font-size: 30rpx;
line-height: 40rpx;
font-weight: bolder; // 字体加粗
}
.order-active {
color: #fa4126;
}
.goods-item {
padding: 16rpx 0;
.left {
width: 200rpx;
height: 200rpx;
.img{
width: 210rpx;
height: rpx;
}
}
.mid {
flex: 1;
margin: 0 12px;
.goods-name {
font-size: 28rpx;
color: #333;
line-height: 40rpx;
font-weight: 400;
}
.goods-blessing {
font-size: 24rpx;
height: 32rpx;
line-height: 32rpx;
color: #999999;
margin: 8rpx 0;
}
}
.right {
.goods-price {
white-space: nowrap;
color: #fa4126;
font-size: 24rpx;
line-height: 48rpx;
}
.goods-count {
white-space: nowrap;
order: 4;
text-align: right;
font-size: 24rpx;
color: #999;
margin: 20rpx 0 0 auto;
}
}
}
.order-item-footer {
.total-amount {
justify-content: flex-end;
align-items: center;
}
.text {
font-size: 28rpx;
line-height: 40rpx;
color: #333333;
margin-right: 4px;
font-weight: bolder; // 字体加粗
}
.price {
font-size: 32rpx;
color: #fa4126;
font-weight: bold;
text {
font-weight: normal;
}
}
}
}
// pages/orders/orders.js
Page({
/**
* 页面的初始数据
*/
data: {
}
})
通⽤模块封装
5.1:消息提示模块封装
1:wx.showToast()基本使⽤
// app.js
App({
// ⻚⾯显示⽣命周期函数
onShow(){
wx.showToast({
title: '消息提示框', // 提示的内容
icon: 'success', // 提示的图标,success(成功)、error(失败)、loading(加
载)、none(不显示图标)
duration: 2000, // 提示的延迟时间
mask: true // 是否显示透明蒙层,防⽌触摸穿透
})
}// ⽣命周期函数
// 在使⽤Page()构造⻚⾯时,需要使⽤⽣命周期函数。
// onLoad:⻚⾯加载时⽣命周期函数。
// onShow:⻚⾯显示⽣命周期函数。每次打开⻚⾯时都会调⽤⼀次。⻚⾯显示/切⼊前台前触发。
// onReady:⻚⾯初次渲染完成⽣命周期函数。⻚⾯初次渲染完成时触发。
// onHide:⻚⾯隐藏⽣命周期函数。如⻚⾯之间跳转或通过底部Tab切换到其他⻚⾯,⼩程序切⼊后
台。
// onUnload:⻚⾯卸载⽣命周期函数。⻚⾯卸载时触发,如⻚⾯跳转或者返回到之前的⻚⾯时。})
2:为什么要进⾏模块封装
3:封装思路
const toast = ({ title = " 数据加载中 " , icon = "none" , duration = 2000 , mask = true } = {}) => {wx . showToast ({title ,icon ,duration ,mask})}
4:调⽤⽅法
export { toast }import { toast } from './extendApi'toast ()toast ({ title : ' 数据加载失败 ....' , mask : true })
wx . toast = toastimport './utils/extendApi'wx . toast ()wx . toast ({ title : ' 数据加载失败 ....' , mask : true })
5:封装步骤
// function toast () {}// 在使⽤ toast ⽅法时,可以传⼊参数,也可以不传⼊参数。// 如果需要传⼊参数,要传⼊对象作为参数。// const toast = (option = {}) => { }// 在形参数、位置通过解构的⽅式获取⽤户传⼊的参数,同时设置默认const toast = ({ title = " 数据加载中 " , icon = "none" , duration = 2000 , mask = true } = {}) =>{wx . showToast ({title ,icon ,duration ,mask})}// 局部暴露 toast ⽅法export { toast }// 如果其他 .js ⽂件,需要使⽤ toast ⽅法,需要导⼊ toast ,然后进⾏调⽤才可以。// 如果有很多的 .js ⽂件,都需要调⽤ toast ⽅法// 每次使⽤都需要导⼊ toast 再调⽤,太麻烦了// 可以将 toast ⽅法挂载到 wx 全局对象上 25 // 全局挂载⽅法wx . toast = toast
c:调⽤步骤
// app.js// import { toast } from './utils/extendApi'// import './utils/extendApi'App ({// ⻚⾯显示⽣命周期函数onShow () {// 局部导⼊// 不传参数// toast()// 传⼊参数,传⼊的参数会覆盖默认的参数。// toast({title:' 数据加载完毕 ', icon:'success'})// 全局导⼊// wx.toast()// wx.toast({ title: ' 数据加载失败 ....', mask: true })}})
5.2:模块对话框封装
1:基本使⽤
w x .showModa l(title : ' 提示 ' , // 提示的标题 3 content : ' 您确定执⾏该操作吗? ' , // 提示的内容confirmColor : '#f3514f' ,// 接⼝调⽤结束的回调函数(调⽤成功、失败都会执⾏)complete ({ confirm , cancel }) {confirm && console . log ( ' 点击了确定 ' )cancel && console . log ( ' 点击了取消 ' )}})
2:封装思路
3:调⽤⽅式
4:实现步骤
5:代码实现
// exendApi.js// function toast () {}// 在调⽤ modal ⽅法时,可以传递参数,也可以不传递参数。 6 // 如果不传递参数,默认值就是空对象。// 如果传递参数,参数需要时⼀个对象,对象中的属性需要和 wx.showModal 参数保持⼀致const modal = ( options = {}) => {// 在⽅法内部需要通过 Promise 返回⽤户的操作// 如果⽤户点击了确认,需要通过 resolve 返回 true// 如果⽤户点击了取消,需要通过 resolve 返回 falsereturn new Promise (( resolve ) => {// 默认的参数const defaultOpt = {title : ' 提示 ' , // 提示的标题content : ' 您确定执⾏该操作吗? ' , // 提示的内容confirmColor : '#f3514f' ,// 接⼝调⽤结束的回调函数(调⽤成功、失败都会执⾏)complete ({ confirm , cancel }) {confirm && console . log ( ' 点击了确定 ' )cancel && console . log ( ' 点击了取消 ' )}}// 通过 object.assgin ⽅法将参数进⾏合并// 需要使⽤传⼊的参数覆盖默认的参数// 为了不影响默认的参数,需要将合并以后的参数赋值给⼀个空对象const opts = Object . assign ({}, defaultOpt , options )wx . showModal ({// 将合并以后的参数通过展开运算符赋值给 wx.showModal 对象... opts ,complete ({ confirm , cancel }) {// 如果⽤户点击了确定,通过 resolve 抛出 true// 如果⽤户点击了取消,通过 resolve 抛出 falseconfirm && resolve ( true )cancel && resolve ( false )}})})}export { modal } 45wx . modal = modal
// app.js// app.js// import { toast } from './utils/extendApi'import './utils/extendApi'App ({// ⻚⾯显示⽣命周期函数async onShow () {// wx.showModal({// title: ' 提示 ', // 提示的标题// content: ' 您确定执⾏该操作吗? ', // 提示的内容// confirmColor: '#f3514f',// // 接⼝调⽤结束的回调函数(调⽤成功、失败都会执⾏)// complete({ confirm, cancel }) {// confirm && console.log(' 点击了确定 ')// cancel && console.log(' 点击了取消 ')// }// })// 不传⼊参数// const res = await wx.modal()// 传⼊参数const res = await wx . modal ({title : ' 新的提示 ' ,showCancel : false})console . log ( res );}})
封装本地存储 API
1:思路分析
try {wx . setStorageSync ( key , value )} catch ( err ) {console . error ( ` 存储指定 ${key} 数据发⽣错误 : ` , err )}wx . setStorage ({key : 'key' ,data : 'data' ,success ( res ) => {},fail ( err ) => {}})
2:实现步骤
3:落地代码
// utils/storage.js/*** @description 存储数据* @param {*} key 本地缓存中指定的 key* @param {*} value 需要缓存的数据*/export const setStorage = ( key , value ) => {try {wx . setStorageSync ( key , value )} catch ( e ) {console . error ( ` 存储指定 ${key} 数据发⽣错误 : ` , e )}}/*** @description 从本地读取对应 key 的数据* @param {*} key*/export const getStorage = ( key ) => {try {const value = wx . getStorageSync ( key )if ( value ) {return value}} catch ( e ) {console . error ( ` 获取指定 ${key} 数据发⽣错误 : ` , e ) 28 }}/*** @description 从本地移除指定 key 数据* @param {*} key*/export const removeStorage = ( key ) => {try {wx . removeStorageSync ( key )} catch ( err ) {console . error ( ` 移除指定 ${key} 数据发⽣错误 : ` , e )}}/*** @description 从本地清空全部的数据*/export const clearStorage = () => {try {wx . clearStorageSync ()} catch ( e ) {console . error ( " 清空本地存储时发⽣错误 :" , e );}}
// app.jsimport { setStorage , getStorage , removeStorage , clearStorage } from './utils/storage'App ({// ⻚⾯显示⽣命周期函数async onShow () {setStorage ( 'name' , 'tom' ), 10 setStorage ( 'age' , 10 )const name = getStorage ( 'name' )console . log ( name );removeStorage ( 'name' )clearStorage ()}})
// utils/request.js// 创建 WxRequest 类// 通过类的⽅式来进⾏封装,会让代码更加具有复⽤性// 也可以⽅便添加新的属性和⽅法class WxRequest {// ⽤于创建和初始化类的属性以及⽅法constructor(){}// request 实例⽅法接受⼀个对象类型的参数// 属性值和 wx.request ⽅法调⽤时传递的参数保持⼀致request(options){// 需要通过 Promise 封装 wx.requset, 处理异步请求return new Promise((resolve,reject) => {wx.request({...options,// 当接⼝调⽤成功时会触发 success 回调函数success:(res) => {resolve(res)},// 当接⼝调⽤失败时会触发 fail 回调函数fail:(err) => {reject(err)}})})}}// ⽬前写到同⼀个⽂件中,是为了⽅便进⾏测试,以后会提取成多个⽂件// 对 wxRequest 进⾏实例化const instance = new WxRequest()// 将 Wxquest 实例进⾏暴露出去,⽅便在其他⽂件中进⾏使⽤export default instance
<!--pages/test/test.wxml-->
<!-- <text>pages/test/test.wxml</text> -->
<view class="box">
<!-- 设置一个按钮 并给按钮设置一个名为handler的点击事件,点击按钮则会发送请求 -->
<button type="warn" size="mini" plain bindtap="handler">测试发送请求</button>
<button type="primary" size="mini" plain bindtap="handler1">测试发送请求</button>
<button type="primary" size="mini" plain bindtap="AllHandler">测试并发请求</button>
</view>
/* pages/test/test.wxss */
.box{
display: flex;
justify-content: center; //主轴方向居中
align-items: center; //副轴方向居中
height: 100vh; // 将盒子高度设置为页面高度
}
// 导入在request页面中封装并暴露的instance实例
import instance from '../../utils/http'
Page({
// 具体阐述按钮点击事件要做什么事情
async handler() {
// 第一种调用方法:通过.then和.catch接受返回的值
instance
.request({
// 请求地址
url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
// 请求方式
method: "GET"
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
})
// 第二种调用方法:通过await和async接受返回的值
const res = await instance.request({
url: '/index/findBanner',
metthod: "GET",
})
console.log(res);
},
})
⽹络请求封装
请求封装-设置请求参数
a:思路分析
// 默认参数对象defaults = {baseURL : '' , // 请求基准地址url : '' , // 开发者服务器接⼝地址data : null , // 请求参数method : 'GET' , // 默认请求⽅法// 请求头header : {'Content-type' : 'application/json' // 设置数据的交互格式},timeout : 60000 // ⼩程序默认超时时间是 60000 ,⼀分钟// 其他参数 ...}
// 对 WxRequest 进⾏实例化const instance = new WxRequest ({baseURL : 'https://gmall-prod.atguigu.cn/mall-api' , // 请求基准地址timeout : 10000 // 微信⼩程序 timeout 默认值为 60000})
const res = await instance.request({url: '/index/findBanner',method: 'GET'})
b:具体实例
class WxRequest {// 默认参数对象defaults = {baseURL : '' , // 请求基准地址url : '' , // 开发者服务器接⼝地址data : null , // 请求参数method : 'GET' , // 默认请求⽅法// 请求头header : {'Content-type' : 'application/json' // 设置数据的交互格式},timeout : 60000 // ⼩程序默认超时时间是 60000 ,⼀分钟}/*** @description 定义 constructor 构造函数,⽤于创建和初始化类的属性和⽅法* @param {*} params ⽤户传⼊的请求配置项 19 */constructor ( params = {}) {// 在实例化时传⼊的参数能够被 constructor 进⾏接收console . log ( params )// 使⽤ Object.assign 合并默认参数以及传递的请求参数this . defaults = Object . assign ({}, this . defaults , params )}// coding....}// 对 WxRequest 进⾏实例化const instance = new WxRequest ({baseURL : 'https://gmall-prod.atguigu.cn/mall-api' ,timeout : 15000})// 将 WxRequest 的实例通过模块化的⽅式暴露出去export default instance
// 创建 request 请求⽅法request ( options ) {// 拼接完整的请求地址options . url = this . defaults . baseURL + options . url// 合并请求参数options = { ... this . defaults , ... options }return new Promise (( resolve , reject ) => {// coding...})12 }
c:落地代码
utils/request.js// 创建 Request 类,⽤于封装 wx.request() ⽅法class WxRequest {// 默认参数对象defaults = {baseURL: '', // 请求基准地址url: '', // 开发者服务器接⼝地址data: null, // 请求参数method: 'GET',// 默认请求⽅法// 请求头header: {'Content-type': 'application/json' // 设置数据的交互格式},timeout: 60000 // ⼩程序默认超时时间是 60000 ,⼀分钟}constructor(params = {}) {// 在实例化时传⼊的参数能够被 constructor 进⾏接收console.log(params)// 使⽤ Object.assign 合并默认参数以及传递的请求参数// 注意:需要传⼊的参数,覆盖默认的参数,因此传⼊的参数需要放到最后this.defaults = Object.assign({}, this.defaults, params)}request(options) {// 拼接完整的请求地址options.url = this.defaults.baseURL + options.url// 合并请求参数options = { ...this.defaults, ...options }// ⽅法返回⼀个 Promise 对象return new Promise((resolve, reject) => {// coding...})}}// 对 WxRequest 进⾏实例化const instance = new WxRequest({+baseURL: 'https://gmall-prod.atguigu.cn/mall-api',+timeout: 15000})// 将 WxRequest 的实例通过模块化的⽅式暴露出去export default instance
⽹络请求封装
封装请求快捷⽅法
a:思路分析
b:落地代码
c:添加参数
定义请求/响应拦截器
a:思路分析
// 请求拦截器
instance . interceptors . request = ( config ) => {// 在发送请求之前做些什么return config}// 响应拦截器instance . interceptors . response = ( response ) => {// 对响应数据做点什么 12 return response}
class WxRequest {
// coding...
// 定义拦截器对象,包含请求拦截器和响应拦截器⽅法,⽅便在请求或响应之前进⾏处理。
interceptors = {
// 请求拦截器
request: (config) => config,
// 响应拦截器
response: (response) => response
}
// ⽤于创建和初始化类的属性以及⽅法
// 在实例化时传⼊的参数,会被 constructor 形参进⾏接收
constructor(options = {}) {
// coding...
}
}
// ----------------- 以下是实例化的代码 --------------------
// ⽬前写到同⼀个⽂件中,是为了⽅便进⾏测试,以后会提取成多个⽂件
// 对 WxRequest 进⾏实例化
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000
})
// 配置请求拦截器
instance.interceptors.request = (config) => {
// 在发送请求之前做些什么
return config
}
// 响应拦截器
instance.interceptors.response = (response) => {
// 对响应数据做点什么
return response
}
// 将 WxRequest 实例进⾏暴露出去,⽅便在其他⽂件中进⾏使⽤
export default instance
class WxRequest {
// coding...
// request 实例⽅法接收⼀个对象类型的参数
// 属性值和 wx.request ⽅法调⽤时传递的参数保持⼀致
request(options) {
// 注意:需要先合并完整的请求地址 (baseURL + url)
// https://gmall-prod.atguigu.cn/mall-api/index/findBanner
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
+ // 在发送请求之前调⽤请求拦截器
+ options = this.interceptors.request(options)
// 需要使⽤ Promise 封装 wx.request,处理异步请求
return new Promise((resolve, reject) => {
wx.request({
...options,
// 当接⼝调⽤成功时会触发 success 回调函数
success: (res) => {
// 不管接⼝成功还是失败,都需要调⽤响应拦截器
// 第⼀个参数:需要合并的⽬标对象
// 第⼆个参数:服务器响应的数据
// 第三个参数:请求配置以及⾃定义的属性
const mergetRes = Object.assign({}, res, { config: options })
resolve(this.interceptors.response(mergetRes))
},
// 当接⼝调⽤失败时会触发 fail 回调函数
fail: (err) => {
// 不管接⼝成功还是失败,都需要调⽤响应拦截器
const mergetErr = Object.assign({}, err, { config: options })
reject(this.interceptors.response(mergetErr))
}
})
})
}
// coding...
}
b:落地代码
//utils/request.js
// 创建 WxRequest 类
// 通过类的⽅式来进⾏封装,会让代码更加具有复⽤性
// 也可以⽅便添加新的属性和⽅法
class WxRequest {
// 定义实例属性,⽤来设置默认请求参数
defaults = {
baseURL: '', // 请求基准地址
url: '', // 接⼝的请求路径
data: null, // 请求参数
method: 'GET', // 默认的请求⽅法
// 请求头
header: {
'Content-type': 'application/json' // 设置数据的交互格式
},
timeout: 60000 // 默认的超时时⻓,⼩程序默认的超时时⻓是 1 分钟
}
// 定义拦截器对象,包含请求拦截器和响应拦截器⽅法,⽅便在请求或响应之前进⾏处理。
interceptors = {
// 请求拦截器
request: (config) => config,
// 响应拦截器
response: (response) => response
}
// ⽤于创建和初始化类的属性以及⽅法
// 在实例化时传⼊的参数,会被 constructor 形参进⾏接收
constructor(params = {}) {
// 通过 Object.assign ⽅法合并请求参数
// 注意:需要传⼊的参数,覆盖默认的参数,因此传⼊的参数需要放到最后
this.defaults = Object.assign({}, this.defaults, params)
}
// request 实例⽅法接收⼀个对象类型的参数
// 属性值和 wx.request ⽅法调⽤时传递的参数保持⼀致
request(options) {
// 注意:需要先合并完整的请求地址 (baseURL + url)
// https://gmall-prod.atguigu.cn/mall-api/index/findBanner
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// 在发送请求之前调⽤请求拦截器,新增或修改请求参数
options = this.interceptors.request(options)
// 需要使⽤ Promise 封装 wx.request,处理异步请求
return new Promise((resolve, reject) => {
wx.request({
...options,
// 当接⼝调⽤成功时会触发 success 回调函数
success: (res) => {
// 不管接⼝成功还是失败,都需要调⽤响应拦截器
// 响应拦截器需要接受服务器响应的数据,然后对数据进⾏逻辑处理,处理好以后进⾏返回
// 然后再通过resolve将返回的数据抛出去
// 在给响应拦截器传递参数时,需要将请求参数也⼀起传递
// ⽅便进⾏代码的调试或者进⾏其他逻辑处理,需要先合并参数
// 然后将合并的参数传递给响应拦截器
// 第⼀个参数:需要合并的⽬标对象
// 第⼆个参数:服务器响应的数据
// 第三个参数:请求配置以及⾃定义的属性
const mergeRes = Object.assign({}, res, { config: options })
resolve(this.interceptors.response(mergeRes))
},
// 当接⼝调⽤失败时会触发 fail 回调函数
fail: (err) => {
// 不管接⼝成功还是失败,都需要调⽤响应拦截器
const mergeErr = Object.assign({}, err, { iconfig: options })
// 不管接⼝成功还是失败,都需要调⽤响应拦截器
err = this.interceptors.response(mergeErr)
reject(err)
}
})
})
}
// 封装 GET 实例⽅法
get(url, data = {}, config = {}) {
// 需要调⽤ request 请求⽅法发送请求,只需要组织好参数,传递给 request 请求⽅法即可
// 当调⽤ get ⽅法时,需要将 request ⽅法的返回值 return 出去
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
// 封装 DELETE 实例⽅法
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
}
// 封装 POST 实例⽅法
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' }, config))
}
// 封装 PUT 实例⽅法
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
}
// 对 WxRequest 进⾏实例化
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000
})
// 配置请求拦截器
instance.interceptors.request = (config) => {
// 在发送请求之前做些什么
return config
}
// 响应拦截器
instance.interceptors.response = (response) => {
// 对响应数据做点什么
return response.data
}
// 将 WxRequest 实例进⾏暴露出去,⽅便在其他⽂件中进⾏使⽤
export default instance
⽹络请求封装
完善请求/响应拦截器
在使⽤ wx.request 发送⽹络请求时。
<!--pages/test/test.wxml-->
<view class="box">
<button type="primary" plain size="mini" bindtap="handler1">测试发送请求
</button>
</view>
// test.js
Page({
handler1(){
wx.request({
url: 'https://gmall-prod.atguigu.cn/mall-api/index/findBanner',
method:'GET',
// timeout:100,
success:(res) => {
// 在使⽤wx.request发送请求时
// 只要成功接收到服务器返回的结果
// ⽆论statusCode、状态码是多少,都会执⾏success
// 开发者就需要根据业务逻辑,⾃⼰来进⾏相关的判断处理
console.log(res);
console.log('虽然接⼝错了,但是依然会⾛success');
},
fail:(err) => {
console.log(err);
console.log('⽹络超时了,这时候⽹络出现了异常,就会执⾏fail');
}
})
}
})
b:完善请求/响应拦截器
// utils/request.js
class WxRequest {
// coding....
request(options) {
// coding....
// 使⽤ Promise 封装异步请求
return new Promise((resolve, reject) => {
// 使⽤ wx.request 发起请求
wx.request({
...options,
// 接⼝调⽤成功的回调函数
success: (res) => {
// 响应成功以后触发响应拦截器
if (this.interceptors.response) {
// 调⽤响应拦截器⽅法,获取到响应拦截器内部返回数据
// success: true 表示服务器成功响应了结果,我们需要对业务状态码进⾏判断
res = this.interceptors.response({ response: res, isSuccess: true
)
}
// 将数据通过 resolve 进⾏返回即可
resolve(res)
},
// 接⼝调⽤失败的回调函数
fail: (err) => {
// 响应失败以后也要执⾏响应拦截器
if (this.interceptors.response) {
+ // isSuccess: false 表示是⽹络超时或其他问题
+ err = this.interceptors.response({ response: err, isSuccess: false
)
}
// 当请求失败以后,通过 reject 返回错误原因
reject(err)
}
})
})
}
// coding......
}
// 对 WxRequest 进⾏实例化
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api'
})
// 设置请求拦截器
instance.setRequestInterceptor((config) => {
console.log('执⾏请求拦截器')
return config
})
// 响应拦截
instance.interceptors.response = (response) => {
console.log(response);
// 从response中结构isSuccess
// const { isSuccess } = response
const { isSuccess, data } = response
// 如果isSuccess为false,说明执⾏了fail回调函数
// 这时候说明⽹络异常,需要给⽤户提示⽹络异常
if (!isSuccess) {
wx.showToast({
title: '⽹络异常请重试',
icon: 'error'
})
return response
}
// 对响应数据做点什么
// return response
return data
}
// 将 WxRequest 的实例通过模块化的⽅式暴露出去
export default instance
⽹络请求封装
拆分request.js⽂件
// request.js
// 创建 Request 类,⽤于封装 wx.request() ⽅法
class WxRequest {
// 默认参数对象
defaults = {
baseURL: '', // 请求基准地址
url: '', // 开发者服务器接⼝地址
data: null, // 请求参数
method: 'GET',// 默认请求⽅法
// 请求头
header: {
'Content-type': 'application/json' // 设置数据的交互格式
},
timeout: 60000 // ⼩程序默认超时时间是 60000,⼀分钟
}
// 定义拦截器对象,包含请求拦截器和响应拦截器⽅法,⽅便在请求或响应之前进⾏处理。
interceptors = {
// 请求拦截器
request: (config) => config,
// 响应拦截器
response: (response) => response
}
/**
* @param {*} params ⽤户传⼊的请求配置项
*/
constructor(params = {}) {
// 在实例化时传⼊的参数能够被 constructor 进⾏接收
console.log(params)
// 使⽤ Object.assign 合并默认参数以及传递的请求参数
// 注意:需要传⼊的参数,覆盖默认的参数,因此传⼊的参数需要放到最后
this.defaults = Object.assign({}, this.defaults, params)
}
/**
* @description 发起请求的⽅法
* @param { Object} options 请求配置选项,同 wx.request 请求配置选项
* @returns Promise
*/
request(options) {
// 拼接完整的请求地址
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// 在发送请求之前调⽤请求拦截器,新增或修改请求参数
options = this.interceptors.request(options)
console.log(options);
return new Promise((resolve, reject) => {
wx.request({
// 使⽤拓展运算符将request函数传来的对象参数展开
...options,
// 当接⼝调⽤成功就会触发success回调函数
success: (res) => {
// 不管是请求失败还是请求成功,都已经将响应的数据传递给了响应拦截器
// 这时候再合并参数的时候,追加⼀个属性:isSuccess
// 如果属性值为true,说明执⾏了success回调函数
// 如果属性值为false,说明执⾏了fail回调函数
const mergeRes = Object.assign({}, res, { config:ptions,isSuccess:true })
resolve(this.interceptors.response(mergeRes))
},
// 当接⼝调⽤失败时会触发fail回调函数
fail: (err) => {
const mergeErr = Object.assign({}, err, { iconfig:tions,isSuccess:false })
// 不管接⼝成功还是失败,都需要调⽤响应拦截器
err = this.interceptors.response(mergeErr)
reject(err)
}
})
})
}
// 封装GET实例⽅法
get(url, data = {}, config = {}) {
// 需要调⽤request请求⽅法发送请求,只需要组织好参数,传递给request请求⽅法即可。
// 当调⽤get⽅法时,需要将request⽅法的返回值return出去。
return this.request(Object.assign({ url, data, method: 'GET' }, config))
}
// 封装DELETE实例⽅法
delete(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'DELETE' },onfig))
}
// 封装POST实例⽅法
post(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'POST' },
fig))
}
// 封装PUT实例⽅法
put(url, data = {}, config = {}) {
return this.request(Object.assign({ url, data, method: 'PUT' }, config))
}
}
export default WxRequest
import WxRequest from './request'
// 对 WxRequest 进⾏实例化
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000
})
// 添加请求拦截器 (在请求发送之前对请求参数进⾏新增或者修改)
instance.interceptors.request = (config) => {
// 在发送请求之前做些什么
return config
}
// 响应拦截器
instance.interceptors.response = (response) => {
console.log(response);
// 从response中解构isSuccess
// const { isSuccess } = response
const { isSuccess, data } = response
// 如果isSuccess为false,说明执⾏了fail回调函数
// 这时候说明⽹络异常,需要给⽤户提示⽹络异常
if (!isSuccess) {
wx.showToast({
title: '⽹络异常请重试',
icon: 'error'
})
return response
}
// 对响应数据做点什么
// return response
return data
}
// 将 WxRequest 的实例通过模块化的⽅式暴露出去
export default instance
使⽤请求/响应拦截器
a:思路分析
b:代码流程
1:使⽤请求拦截器
import {getStorage} from './storage'
// 添加请求拦截器 (在请求发送之前对请求参数进⾏新增或者修改)
instance.interceptors.request = (config) => {
// 在实际开发中,有⼀些接⼝需要使⽤访问令牌 token
// 访问令牌 token 通常是存储到本地
// 需要先从本地获取到存储的 token
const token = getStorage('token')
// 如果本地存在 token,这时候就需要在请求头中添加 token 字段
if (token) {
config.header['token'] = token
}
// 在发送请求之前做些什么
return config
}
请求封装-添加并发请求
a:思路分析
b:代码演示
<!--pages/test/test.wxml-->
<view class="box">
<button type="warn" size="mini" plain bindtap="AllHandler">测试并发请求
</button>
</view>
// test/test.js
page({
// 演示通过 async 和 await ⽅式同步发起多个请求
// async 和 await 能够控制异步任务以同步的流程来执⾏// async和awiat⽅式发起多个请求
// 当第⼀个请求结束以后,才能够发起第⼆个请求
// 当前⼀个请求结束以后,才能够发起下⼀个请求
// 会造成请求的阻塞,从⽽影响⻚⾯的渲染速度
async AllHandler(){
await instance.get('/index/findBanner')
await instance.get('/index/findCategory1')
await instance.get('/index/findBanner')
await instance.get('/index/findCategory1')
// 演示通过 Promise.all 同时发送多个请求
// Promise.all 能够将多个请求同时进⾏发送
// Promise.all 能够将多个异步请求同时进⾏发售,也就是并⾏发送
// 并不会造成请求的阻塞,从⽽不会影响⻚⾯的渲染速度
await
Promise.all([instance.get('/index/findBanner'),instance.get('/index/findCategory'),
instance.get('/index/findBanner'),instance.get('/index/findCategory1')])
}
})
c:实例运⽤
// request.js
class WxRequest {
code...
// ⽤来处理并发请求
all(...promise){
console.log(promise);
// 通过展开运算符接受传递的参数
// 那么展开运算符会将传⼊的参数转成数组
return Promise.all(promise)
}
}
// test/test.js
await
instance.all(instance.get('/index/findBanner'),instance.get('/index/findCategory'),
instance.get('/index/findBanner'),instance.get('/index/findCategory1'))
console.log(res)
⽹络请求封装
添加loading
a:思路分析
b:代码实现
// request.js
class WxRequest {
request(options) {
// 拼接完整的请求地址
options.url = this.defaults.baseURL + options.url
// 合并请求参数
options = { ...this.defaults, ...options }
// 发送请求之前添加loading
wx.showLoading()
// 在发送请求之前调⽤请求拦截器,新增或修改请求参数
options = this.interceptors.request(options)
console.log(options);
return new Promise((resolve, reject) => {
wx.request({
// 使⽤拓展运算符将request函数传来的对象参数展开
...options,
// 当接⼝调⽤成功就会触发success回调函数
success: (res) => {
// 不管是请求失败还是请求成功,都已经将响应的数据传递给了响应拦截器
// 这时候再合并参数的时候,追加⼀个属性:isSuccess
// 如果属性值为true,说明执⾏了success回调函数
// 如果属性值为false,说明执⾏了fail回调函数
const mergeRes = Object.assign({}, res, { config:ptions,isSuccess:true })
resolve(this.interceptors.response(mergeRes))
},
// 当接⼝调⽤失败时会触发fail回调函数
fail: (err) => {
const mergeErr = Object.assign({}, err, { iconfig:
tions,isSuccess:false })
// 不管接⼝成功还是失败,都需要调⽤响应拦截器
err = this.interceptors.response(mergeErr)
reject(err)
},
complete:() => {
// 接⼝调⽤完成后隐藏loading
wx.hideLoading()
}
})
})
}
}
使⽤npm包发送请求
a:下载mina-request包:
npm install mina-request
// http.jsimport WxRequest from 'mina-request'
c:http.js代码
// 导⼊模块、包提供的类
import WxRequest from 'mina-request'
// 导⼊封装的本地存储操作模块
import { getStorage, clearStorage } from './storage'
// 导⼊封装的增强 API
import { toast, modal } from './extendApi'
// 对类进⾏实例化
const instance = new WxRequest({
baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
timeout: 15000
})
// 添加请求拦截器 (在请求发送之前对请求参数进⾏新增或者修改)
instance.interceptors.request = (config) => {
// 在实际开发中,有⼀些接⼝需要使⽤访问令牌 token
// 访问令牌 token 通常是存储到本地
// 需要先从本地获取到存储的 token
const token = getStorage('token')
// 如果本地存在 token,这时候就需要在请求头中添加 token 字段
if (token) {
config.header['token'] = token
}
// 在发送请求之前做些什么
return config
}
// 添加响应拦截器 (在服务器响应数据以后,对返回的数据进⾏逻辑处理)
instance.interceptors.response = async (response) => {
// 从 response 对象中解构两个数据
const { isSuccess, data } = response
// response 服务器响应的数据,只不过数据被 wx.request 进⾏了⼀层包装
// console.log(response)
// response.config 封装的包⾥⾯提供的 config 属性,是请求的参数信息
// 可以使⽤请求参数进⾏代码的调试
// response.data 服务器真正响应的数据
// response.isSuccess 判断代码执⾏了哪⼀个回调函数
// isSuccess = true,说明代码执⾏了 wx.request ⽅法的 success 回调函数
// isSuccess = false,说明代码执⾏了 wx.request ⽅法的 fail 回调函数
// 如果 isSuccess = false,说明⽹络出现了问题
if (!isSuccess) {
toast({
title: '⽹络异常请重试',
icon: 'error'
})
return Promise.reject(response)
}
// 如果 isSuccess = true,说明代码执⾏到了 success 回调函数
// 需要开发者对返回的参数进⾏逻辑判断
// 需要对后端返回的业务状态码进⾏判断
// 业务状态码 === 200,接⼝调⽤成功,服务器成功返回了数据
// 业务状态码 === 208,没有 token 或者 token 失效,需要让⽤户重新进⾏登录
// 业务状态码既不等于 200,也不等于 208,说明出现了其他异常,需要给⽤户统⼀进⾏提示
switch (data.code) {
case 200:
// 接⼝调⽤成功,服务器成功返回了数据,只需要将数据简化以后返回即可
return data
case 208:
const res = await modal({
content: '鉴权失败,请重新登录',
showCancel: false
})
if (res) {
// 既然⽤户需要重新进⾏登录,就需要把之前⽤户存储的信息(过期的 token) 进⾏清除
clearStorage()
wx.navigateTo({
url: '/pages/login/login'
})
}
return Promise.reject(response)
default:
toast({
title: '程序出现异常,请联系客服或稍后重试!'
})
return Promise.reject(response)
}
// return response
}
// 导出实例
export default instance
接⼝调⽤⽅式说明
a:思路分析
b:请求封装
// index.js
// 导⼊封装的⽹络请求模块实例
import http from '../utils/http'
// export const reqSwiperData = () => {
// return http.get('/index/findBanner')
// }
export const reqSwiperData = () => http.get('/index/findBanner')
// test/test.js
// 导⼊接⼝API函数
import {reqSwiperData} from '../../api/index'
Page({
// 点击按钮触发 handler ⽅法
async handler() {
const res = await reqSwiperData()
console.log(res);
}
})
项⽬⾸⻚
获取⾸⻚数据
a:思路分析
b:代码实现
// api.index.js
// 导⼊封装的 ⽹络请求模块实例
import http from '../utils/http'
export const reqIndexData = () => {
// 通过并发请求获取⾸⻚的数据,提升⻚⾯的渲染速度
// 通过 Promise.all 进⾏并发请求
// return Promise.all([
// http.get('/index/findBanner'),
// http.get('/index/findCategory1'),
// http.get('/index/advertisement'),
// http.get('/index/findListGoods'),
// http.get('/index/findRecommendGoods')
// ])
// 是使⽤封装的 all ⽅法发送请求
// 这两种⽅式都可以
return http.all(
http.get('/index/findBanner'),
http.get('/index/findCategory1'),
http.get('/index/advertisement'),
http.get('/index/findListGoods'),
http.get('/index/findRecommendGoods')
)
}
// index/index.js
// 导⼊接⼝ API
import { reqIndexData } from '../../api/index'
Page({
// 初始化数据
data: {
bannerList: [], // 轮播图数据
categoryList: [], // 分类数据
activeList: [], // 活动⼴告
hotList: [], // ⼈⽓推荐
guessList: [] // 猜你喜欢
},
// 获取⾸⻚数据
async getIndexData() {
// 调⽤接⼝,获取⾸⻚数据
// 数组每⼀项是 Promise 产⽣的结果,并且是按照顺序返回。
const res = await reqIndexData()
// 在获取数据以后,对数据进⾏赋值
this.setData({
bannerList: res[0].data,
categoryList: res[1].data,
activeList: res[2].data,
guessList: res[3].data,
hotList: res[4].data,
})
},
// 监听⻚⾯加载
onLoad() {
// 调⽤获取⾸⻚数据的回调
this.getIndexData()
}
})
分析轮播图区域并渲染
a:分析轮播图结构
{
"usingComponents": {
"banner": "./banner/banner"
}
}
<!-- 轮播图区域 -->
<banner />
<!--pages/index/banner/banner.wxml-->
<!-- 轮播图 -->
<view class="swiper-box">
<!-- swiper 滑块视图容器 -->
<swiper
autoplay
class="swiper"
indicator-active-color="#FF734C"
interval="2000"
duration="1000"
indicator-color="rgba(0, 0, 0, .3)"
>
<!-- 使⽤ block 标签实现通过数组进⾏列表渲染 -->
<block wx:for="{{ bannerList }}" wx:key="index">
<!-- swiper-item 单个滑块视图容器 -->
<swiper-item class="swiper-item">
<!-- 通过 navigator 组件跳转的链接 -->
<navigator
class="navigator"
url="/pages/goods/detail/detail?goodsId=id"
>
<image class="img" src="{{ item }}"></image>
</navigator>
</swiper-item>
</block>
</swiper>
<!-- 轮播图的⾯板指示点,因为⾯板指示点不⽀持,所以我们只能通过⾃定义结构的⽅式 -->
<view class="indicator">
<!-- active 类名:当前被激活的⾯板指示点颜⾊ -->
<!-- circle 类名:默认的⾯板指示点颜⾊ -->
<text
wx:for="{{bannerList.length}}"
wx:key="id"
class="{{ 'active rectangle' }}"
></text>
</view>
</view>
b:渲染⻚⾯结构
// page/index/index.html
<!-- 轮播图区域 -->
<banner bannerList="{{ bannerList }}" />
// page/index/banner/banner.wxml
<!-- 使⽤ block 标签实现通过数组进⾏列表渲染 -->
<block wx:for="{{ bannerList }}" wx:key="index">
<!-- swiper-item 单个滑块视图容器 -->
<swiper-item class="swiper-item">
<!-- 通过 navigator 组件跳转的链接 -->
<navigator
class="navigator" + url="/pages/goods/detail/detail?goodsId={{item.id}}">
<image class="img" src="{{ item.imageUrl }}"></image>
</navigator>
</swiper-item>
</block>
实现轮播图和指示点的联动
a:思路分析
b:实现思路
c:代码实现
<!--pages/index/banner/banner.wxml-->
<!-- 轮播图 -->
<view class="swiper-box">
<swiper
autoplay
class="swiper"
indicator-active-color="#FF734C"
interval="2000"
duration="1000"
indicator-color="rgba(0, 0, 0, .3)" + bindchange="getSwiperIndex">
<block wx:for="{{ bannerList }}" wx:key="index">
<swiper-item class="swiper-item">
<navigator
class="navigator"
url="/pages/goods/detail/detail?goodsId={{ item.id }}">
<image class="img" src="{{ item.imageUrl }}"></image>
</navigator>
</swiper-item>
</block>
</swiper>
<!-- 轮播图的⾯板指示点,因为⾯板指示点不⽀持,所以我们只能通过⾃定义结构的⽅式 -->
<view class="indicator">
<!-- active 类名:当前被激活的⾯板指示点颜⾊ -->
<!-- rectangle 类名:默认的⾯板指示点颜⾊ -->
<text
wx:for="{{bannerList.length}}"
wx:key="id"+ class="{{ index === activeIndex ? 'active rectangle' : 'rectangle' }}"></text>
</view>
</view>
// pages/index/banner/banner.js
Component({
/**
* 组件的属性列表
*/
properties: {
// 轮播图数据
bannerList: {
type: Array,
value: [
'../../../assets/banner/banner-1.jpg',
'../../../assets/banner/banner-2.jpg',
'../../../assets/banner/banner-3.jpg'
]
}
},
/**
* 组件的初始数据
*/
data: {
activeIndex:0 // 被激活的轮播图索引,默认是0
},
/**
* 组件的⽅法列表
*/
methods: {
// 获取被激活的轮播图索引
getSwiperIndex(event){
// console.log(event);
const { current } = event.detail
this.setData({
activeIndex:current
})
}
}
})
渲染分类导航
a:渲染导航分类结构
<!-- 导航分类 -->
<entrance cateList="{{ categoryList }}"/>
// pages/index/entrance/entrance.html
<view class="nav-list">
<!-- ⼀级分类导航容器 -->
<view wx:for="{{ cateList }}"
wx:key="index"
class="nav-item {{ index >= 5 ? 'small' : '' }}"
>
<!-- 导航链接 -->
<navigator
class="navigator-nav" + url="/pages/goods/list/list?category1Id={{item.id}}"
>
<image class="nav-img" src="{{ item.imageUrl }}" />
<text class="nav-text">{{ item.name }}</text>
</navigator>
</view>
</view>
b:导航分类结构样式调整
/* pages/index/entrance/entrance.wxss */
/* 导航分类样式 */
.nav-list {
display: flex;
align-items: center;
flex-wrap: wrap;
margin: 20rpx 0rpx;
border-radius: 18rpx;
padding: 10px 0;
background-color: #fff;
.nav-item {
flex: 1;
min-width: 20%;
max-width: 20%;
&.small {
margin-top: 36rpx;
.nav-img {
width: 50rpx !important;
height: 50rpx !important;
}
}
}
}
渲染活动区域
// pages/index/index.html
<!-- ⼴告区域 -->
<view class="adver">
<view class="adver-left">
<navigator url="/pages/goods/list/list?category2Id={{activeList[0].category2Id }}">
<image src="{{ activeList[0].imageUrl }}" mode="widthFix" />
</navigator>
</view>
<view class="adver-right">
<view>
<navigator url="/pages/goods/list/list?category2Id={{activeList[1].category2Id }}">
<image src="{{ activeList[1].imageUrl }}" mode="widthFix" />
</navigator>
</view>
<view>
<navigator url="/pages/goods/list/list?category2Id={{activeList[2].category2Id }}">
<image src="{{ activeList[2].imageUrl }}" mode="widthFix" />
</navigator>
</view>
</view>
</view>
猜你喜欢+⼈⽓推荐区域渲染
a:将数据传递给 goods-list 组件
// page/index/index.wxml 2<!-- 商品列表 -->< goods-list title =" 猜你喜欢 " list =" {{ guessList }} "></ goods-list >< goods-list title =" ⼈⽓推荐 " list =" {{ hotList }} "></ goods-list >
b:接收⾸⻚传递的 list 数据
// components/goods-list/good-list.js
Component({
// 组件的属性列表
properties: {
// 列表标题
title: {
type: String,
value: '',
},
// 传递的列表数据
list: {
type: Array,
value: []
}
}
// coding...
})
c:遍历 goods-item 组件,并将数据传递给 goods-item
// components/goods-list/goods-list.wxml
<!-- 商品列表组件 -->
<view class="goods_container" wx:if="{{ list.length }}">
<!-- 标题 -->
<view class="goods_title">{{title}}</view>
<!-- 列表区域 -->
<view class="goods_card_list">
<goods-card wx:for="{{ list }}" wx:key="id" goodItem="{{ item }}"></goodscard>
</view>
<!-- 查看更多 -->
<!-- coding -->
</view>
d:将数据传递给 goods-item 组件
// components/goods-card/goods-card.js
Component({
// 组件的属性列表
properties: {
// 每⼀项商品的数据
goodItem: {
type: Object,
value: {}
}
}
// coding...
})
e:将数据传递给 goods-item 组件
// components/goods-list/goods-item.wxml
<!-- 列表分类卡⽚ -->
<view class="goods_cart_container">
<navigator class="navigator_nav" url="/pages/goods/detail/detail?goodsId={{goodItem.id}}">
<!-- 商品图⽚ -->
<image class="good_img" src="{{ goodItem.imageUrl }}" mode="widthFix" />
<!-- 商品详细信息 -->
<view class="goods_item_info">
<!-- 商品名称 -->
<text class="goods_item_info_name">{{ goodItem.name }}</text>
<!-- 商品描述 -->
<text class="goods_item_info_promo">{{ goodItem.floralLanguage }}</text>
<!-- 商品价格 -->
<view class="goods_item_info_bottom">
<view class="goods_item_info_price">
<text class="text">¥</text>{{ goodItem.price }}
</view>
<view class="goods_item_info_origin_price">
<text class="text">¥</text> {{goodItem.marketPrice}}
</view>
<!-- 加⼊购物⻋图⽚ -->
<view class="goods_item_info_btn">
<image class="goods_image" src="/static/images/buybtn.png" mode="" />
</view>
</view>
</view>
</navigator>
</view>