微信小程序综合训练--花店商城小程序首页、分类笔记

项⽬初始化

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:消息提示模块封装

在进⾏项⽬开发的时候,我们经常的会频繁的使⽤到⼀些 API,例如: wx.showToast()

1:wx.showToast()基本使⽤

wx.showToast() 消息提示框是在项⽬中频繁使⽤的⼀个⼩程序 API ,常⽤来给⽤户进⾏消息提示反 馈。使⽤⽅式如下:

// 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:为什么要进⾏模块封装

如果每次使⽤的时候,都直接调⽤这些 API ,会导致代码很冗余,为了减少了代码冗余,我们需要将
这些 API 封装成公共⽅法。

3:封装思路

a:创建⼀个 toast ⽅法对 wx.showToast() ⽅法进⾏封装
b:调⽤该⽅法时,传递对象作为参数
     如果没有传递任何参数,设置⼀个空对象 {} 作为默认参数
     从对象中包含 title 、 icon 、 duration 、 mask 参数,并给参数设置默认值
const toast = ({ title = " 数据加载中 " , icon = "none" , duration = 2000 , mask = true } = {}) => {
  wx . showToast ({
  title ,
  icon ,
  duration ,
  mask
  })
  }
c:在需要显示弹出框的时候调⽤ toast ⽅法,并传⼊相关的参数,有两种参数⽅式:
     不传递参数,使⽤默认参值
     传⼊部分参数,覆盖默认的参数

4:调⽤⽅法

a:模块化的⽅式导⼊使⽤
  export { toast }
 
  import { toast } from './extendApi'
 
  toast ()
  toast ({ title : ' 数据加载失败 ....' , mask : true })
b:将封装的模块挂载到 wx 全局对象身上
wx . toast = toast
  import './utils/extendApi'
 
  wx . toast ()
  wx . toast ({ title : ' 数据加载失败 ....' , mask : true })

5:封装步骤

a:在utils⽂件夹内新建extendApi.js
b:完成extendApi.js⽂件
// 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:基本使⽤

wx.showModal() 模态对话框也是在项⽬中频繁使⽤的⼀个⼩程序 API ,通常⽤于向⽤户询问是否执
⾏⼀些操作,例如:询问⽤户是否真的需要退出、是否确认删除等等。
w x .showModa l(
 title : ' 提示 ' , // 提示的标题 3 content : ' 您确定执⾏该操作吗? ' , // 提示的内容
 confirmColor : '#f3514f' ,
  // 接⼝调⽤结束的回调函数(调⽤成功、失败都会执⾏)
  complete ({ confirm , cancel }) {
 confirm && console . log ( ' 点击了确定 ' )
 cancel && console . log ( ' 点击了取消 ' )
  }
  })

2:封装思路

a:对 wx.showModal() ⽅法进⾏封装, 封装后的新⽅法叫 modal。
b:调⽤该⽅法时,传递对象作为参数,对象的参数同 wx.showModal() 参数⼀致。
c:封装的 modal ⽅法的内部通过 Promise 返回⽤户执⾏的操作(确定和取消,都通过 resolve 返
回)。
d:在需要显示模态对话框的时候调⽤ modal ⽅法,并传⼊相关的参数,有三种调⽤⽅式:
     不传递参数,使⽤默认参数。
     传递参数,覆盖默认的参数。

3:调⽤⽅式

新封装的本地存储模块,我们依然希望有两种调⽤的⽅式:
a:模块化的⽅式导⼊使⽤
b:将封装的模块挂载到 wx 全局对象身上

4:实现步骤

a:在 extendApi.js ⽂件中新建 modal ⽅法,⽅法内部
b:modal ⽅法,⽅法内部⽤来处理封装的逻辑

5:代码实现

// exendApi.js
 
  // function toast () {}
 
  // 在调⽤ modal ⽅法时,可以传递参数,也可以不传递参数。 6 // 如果不传递参数,默认值就是空对象。
  // 如果传递参数,参数需要时⼀个对象,对象中的属性需要和 wx.showModal 参数保持⼀致
  const modal = ( options = {}) => {
  // 在⽅法内部需要通过 Promise 返回⽤户的操作
  // 如果⽤户点击了确认,需要通过 resolve 返回 true
// 如果⽤户点击了取消,需要通过 resolve 返回 false
 
  return 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 抛出 false
confirm && resolve ( true )
 cancel && resolve ( false )
  }
  })
  })
  }
 
  export { modal } 45
  wx . 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:思路分析

在⼩程序中,经常需要将⼀些数据存储到本地,⽅便多个⻚⾯的读取使⽤,例如:将⽤户的登录状
态、⽤户的个⼈信息存储到本地。
⼩程序提供了同步、异步两类 API 来实现本地存储操作。例如: wx.setStorageSync 、 wx.setStorage等⽅法。
try {
 wx . setStorageSync ( key , value )
  } catch ( err ) {
 console . error ( ` 存储指定 ${key} 数据发⽣错误 : ` , err )
  }
 
  wx . setStorage ({
 key : 'key' ,
 data : 'data' ,
  success ( res ) => {},
  fail ( err ) => {}
  })
如果直接使⽤这些 API,会⽐较麻烦,通常情况下,我们需要对本地存储的⽅法进⾏封装。

2:实现步骤

在 utils ⽬录下新建 storage.js ⽂件
在该⽂件中,封装对本地数据进⾏ 存储、获取、删除、清除的⽅法

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.js
 
  import { 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
b:request⽅法使⽤

<!--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
  })
在通过实例,调⽤ request 实例⽅法时也会传⼊相关的请求参数。
const res = await instance.request({
 url: '/index/findBanner',
 method: 'GET'
  })
从⽽得出结论:请求参数的设置有三种⽅式:
1. 默认参数:在 WxRequest 类中添加 defaults 实例属性来设置默认值
2. 实例化时参数:在对 WxRequest 类进⾏实例化时传⼊相关的参数,需要在 constructor 构造
函数形参进⾏接收
3. 调⽤实例⽅法时传⼊请求参数

b:具体实例

默认参数和⾃定义参数的合并操作,通常会在 constructor 中进⾏。
因此我们就在 constructor 中将开发者传⼊的相关参数和 defaults 默认值进⾏合并,需要传⼊的配置
项覆盖默认配置项。
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 实例时也会传⼊相关的参数,是发起请求真正的参数,
我们需要将调⽤ reqeust 实例⽅法时传⼊的参数,继续覆盖合并以后的参数,请求才能够发送成功。
// 创建 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:思路分析

⽬前已经完成了 request() 请求⽅法的封装,同时处理了请求参数。
每次发送请求时都使⽤ request() ⽅法即可,但是项⽬中的接⼝地址有很多,不是很简洁。

所以我们在 request() 基础上封装⼀些快捷⽅法,简化 request() 的调⽤。
需要封装 4 个快捷⽅法,分别是 get 、 delete 、 post 、 put ,他们的调⽤⽅式如下:

这 4 个请求⽅法,都是通过实例化的⽅式进⾏调⽤,所以需要 Request 类中暴露出来
get 、 delete 、 post 、 put ⽅法。每个⽅法接收三个参数,分别是:接⼝地址、请求参数以及其他
参数。
这 4 个快捷⽅法,本质上其实还是调⽤ request ⽅法,我们只要在⽅法内部组织好参数,调⽤
request 发送请求即可。

b:落地代码

c:添加参数

定义请求/响应拦截器

a:思路分析

为了⽅便统⼀处理请求参数以及服务器响应结果,为 WxRequest 添加拦截器功能,拦截器包括 请求
拦截器 和 响应拦截器
请求拦截器本质上是在请求之前调⽤的函数,⽤来对请求参数进⾏新增和修改
响应拦截器本质上是在响应之后调⽤的函数,⽤来对响应数据做点什么
注意:不管成功响应还是失败响应,都会执⾏响应拦截器
拦截器的使⽤⽅式:

 // 请求拦截器

  instance . interceptors . request = ( config ) => {
  // 在发送请求之前做些什么
  return config
  }
 
  // 响应拦截器
  instance . interceptors . response = ( response ) => {
  // 对响应数据做点什么 12 return response
  }
通过使⽤⽅式,我们可以得出结论:
可以在 WxRequest 类内部定义 interceptors 实例属性,属性中需要包含 request 以及 response
⽅法
需要注意:在发送请求时,还需要区分是否通过实例调⽤了拦截器:
1. 没有通过实例调⽤拦截器,需要定义默认拦截器,在默认拦截器中,需要将请求参数进⾏返回
2. 通过实例调⽤拦截器,那么实例调⽤的拦截器会覆盖默认的拦截器⽅法,然后将新增或修改的请求
参数进⾏返回
实现拦截器的思路:
1. 在 WxRequest 类内部定义 interceptors 实例属性,属性中需要包含 request 以及 response ⽅
2. 是否通过实例调⽤了拦截器
是:定义默认拦截器
否:实例调⽤的拦截器覆盖默认拦截器
3. 在发送请求之前,调⽤请求拦截器
4. 在服务器响应以后,调⽤响应拦截器
不管成功、失败响应,都需要调⽤响应拦截器
在 WxRequest 类内部定义 interceptors 实例属性,属性中需要包含 request 以及 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

 

⽹络请求封装

完善请求/响应拦截器

a:wx.request 注意事项
知识点:

在使⽤ wx.request 发送⽹络请求时。

只要成功接收到服务器返回,⽆论 statusCode 是多少,都会进⼊ success 回调。

 

落地代码:

<!--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:完善请求/响应拦截器

思路分析:
在响应拦截器,我们需要判断是请求成功,还是请求失败,然后进⾏不同的业务逻辑处理。
例如:请求成功以后将数据简化返回,⽹络出现异常则给⽤户进⾏⽹络异常提示。
⽬前不管请求成功 (success),还是请求失败(fail),都会执⾏响应拦截器
那么怎么判断是请求成功,还是请求失败呢 ?
封装需求:
1. 如果请求成功,将响应成功的数据传递给响应拦截器,同时在传递的数据中新增 isSuccess: true 字段,表示请求成功
2. 如果请求失败,将响应失败的数据传递给响应拦截器,同时在传递的数据中新增 isSuccess: false 字段,表示请求失败
在实例调⽤的响应拦截中,根据传递的数据进⾏以下的处理:
   如果 isSuccess: true 表示服务器响应了结果,我们可以将服务器响应的数据简化以后进⾏返回
   如果 isSuccess: false 表示是⽹络超时或其他⽹络问题,提示 ⽹络异常 ,同时将返回即可
落地代码:

// 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:思路分析

使⽤请求拦截器:
在发送请求时,购物⻋列表、收货地址、更新头像等接⼝,都需要进⾏权限验证,因此我们需要在请求拦截器中判断本地是否存在访问令牌 token ,如果存在就需要在请求头中添加 token 字段。

 

使⽤响应拦截器:
在使⽤ wx.request 发送⽹络请求时。只要成功接收到服务器返回,⽆论 statusCode 是多少,都会进⼊ success 回调。
因此开发者根据业务逻辑对返回值进⾏判断。
后端返回的业务状态码如下:
1. 业务状态码 === 200, 说明接⼝请求成功,服务器成功返回了数据
2. 业务状态码 === 208, 说明没有 token 或者 token 过期失效,需要登录或者重新登录
3. 业务状态码 === 其他,说明请求或者响应出现了异常

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:思路分析

前端并发请求是指在前端⻚⾯同时向后端发起多个请求的情况。当⼀个⻚⾯需要请求多个接⼝获取数
据时,为了提⾼⻚⾯的加载速度和⽤户体验,可以同时发起多个请求,这些请求之间就是并发的关
系。
我们通过两种⽅式演示发起多个请求:
1. 使⽤ async 和 await ⽅式
2. 使⽤ Promise.all() ⽅式

b:代码演示

⾸先使⽤ async 和 await ⽅式发送请求,使⽤ async 和 await 能够控制异步任务以同步的流程执
⾏,代码如下,这时候就会产⽣⼀个问题,当第⼀个请求执⾏完以后,才能执⾏第⼆个请求,这样就会造成请求的阻塞,影响渲染的速度,如下:

<!--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:思路分析

在封装时添加 loading 效果,从⽽提⾼⽤户使⽤体验
1. 在请求发送之前,需要通过 wx.showLoading 展示 loading 效果
2. 当服务器响应数据以后,需要调⽤ wx.hideLoading 隐藏 loading 效果

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
npm install mina-request
b:构建 npm
安装包后,需要在微信开发者⼯具中进⾏ npm 构建,点击 ⼯具 ➡️ 构建 npm。
https://www.npmjs.com/package/mina-request
// http.js
  import 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:思路分析

在开发中,我们会将所有的⽹络请求⽅法放置在 api ⽬录下统⼀管理,然后按照模块功能来划分成对应 的⽂件,在⽂件中将接⼝封装成⼀个个⽅法单独导出,例如:

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);

 }

})

这样做的有以下⼏点好处:
1. 易于维护:⼀个⽂件就是⼀个模块,⼀个⽅法就是⼀个功能,清晰明了,查找⽅便
2. 便于复⽤:哪⾥使⽤,哪⾥导⼊,可以在任何⼀个业务组件中导⼊需要的⽅法
3. 团队合作:分⼯合作

项⽬⾸⻚

获取⾸⻚数据

a:思路分析

1. 轮播图区域
2. 商品导航区域
3. 活动宣传区域
4. 猜你喜欢区域
5. ⼈⽓推荐区域
在实现这些功能之前,我们需要先获取数据,在获取数据以后,然后进⾏⻚⾯的渲染,同时完成进⾏
功能的开发。
因为需要同时获取 5 个接⼝的数据,所以我们使⽤并发请求来实现。这样能够提升⻚⾯的渲染速度。

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:分析轮播图结构

轮播图区域采⽤组件化⽅式开发,我们在 index ⽬录下新建 banner ⽂件夹,⾥⾯存放轮播图组件。在 index/index.json ⽂件中导⼊组件,然后将组件当成标签进⾏使⽤。

{

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

1. swiper 、 swiper-item 、 navigator 、 image 组件实现⻚⾯结构的搭建
2. block 渲染数组,实现列表渲染
3. 使⽤ flex 布局实现了⻚⾯样式的绘制
另外需要注意的是:轮播图⾯板指示点不⽀持⾃定义,所以只能⻚⾯结构的⽅式,实现轮播图的⾯板
指示点功能

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:思路分析

< img src = " http://8.131.91.46:6677/mina/floor/02- 轮播图.gif" style = "zoom:80%; border: 1px solid
#ccc" />
当轮播图进⾏切换时,⾯板指示点也要跟随着进⾏切换,如上图。
轮播图和指示点进⾏联动,当切换到第⼆张轮播图时,第⼆个⾯板指示点⾼亮,如果想实现这种⼀
⼀对应的关系,需要借助 索引 。
⾸先在 data 中初始化状态 activeIndex 默认为 0,代表第⼀个⾼亮,也是⽤来接收切换后的轮播
图索引然后使⽤ swiper 组件的 change 事件,监听轮播图是否发⽣改变,如果改变,则获取到轮播图的 索引,赋值到 data 中,
通过 activeIndex 对⼩圆点进⾏动态的渲染。

b:实现思路

在 data 中初始化状态 activeIndex 默认为 0。
给 swiper 绑定 bindchange 事件,监听轮播图是否切换,将切换后轮播图的索引赋值
给 activeIndex 利⽤ activeIndex 对⼩圆点进⾏动态的渲染

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>

效果图

 

 

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值