震惊!小程序竟然可以这么简单!

6,188 阅读22分钟

手摸手实现一个电商-微信小程序(上)

前言

基于微信小程序原生框架,常用原生组件及官方API接口实现的小程序版电商,将一步一个脚印带大家从0到1真正实现一个微信小程序。 源码: https://github.com/panzekun/wx_procedure

1、创建小程序项目

打开微信开发者工具,点击新建项目,选择小程序,AppId 开发时候使用测试号,发布生产需要注册

2、初始化项目

3、实现标题以及tabBar底部导航

app.json

{
  "pages": [
    "pages/home/index",
    "pages/category/index",
    "pages/cart/index",
    "pages/user/index",
    "pages/search/index"
  ],
  "window": {
    "navigationBarTitleText""从0到1实战微信小程序",
    "navigationBarBackgroundColor""#eb4450",
    "navigationBarTextStyle""black",
    "backgroundColorTop""#ffffff",
    "backgroundColorBottom""#ffffff",
    "backgroundColor""#ffffff"
  },
  "tabBar": {
    "color""#333333",
    "selectedColor""#f06c7a",
    "borderStyle""black",
    "backgroundColor""#ffffff",
    "list": [{
        "pagePath""pages/home/index",
        "iconPath""static/tabBar/home.png",
        "selectedIconPath""static/tabBar/home-on.png",
        "text""首页"
      },
      {
        "pagePath""pages/category/index",
        "iconPath""static/tabBar/category.png",
        "selectedIconPath""static/tabBar/category-on.png",
        "text""分类"
      },
      {
        "pagePath""pages/cart/index",
        "iconPath""static/tabBar/cart.png",
        "selectedIconPath""static/tabBar/cart-on.png",
        "text""购物车"
      },
      {
        "pagePath""pages/user/index",
        "iconPath""static/tabBar/user.png",
        "selectedIconPath""static/tabBar/user-on.png",
        "text""我的"
      }
    ]
  },
  "style""v2",
  "sitemapLocation""sitemap.json"
}

便捷操作

在page写入路径保存,自动生成对应文件夹以及对应的 js json wxml wxss。

4、实现首页

效果图先上

4.1、首页功能分析

代码

<!--pages/home/index.wxml-->
<page class="home">
  <!-- 搜索 -->
  <view class="shop-input">
    <search-input></search-input>
  </view>

  <!-- 轮播图 -->
  <view class="shop-swiper">
    <swiper class="" indicator-dots autoplay interval="5000">
      <block wx:for="{{swiperList}}" wx:key="{{item}}">
        <swiper-item class="" item-id="">
          <image class="" src="{{item.url}}" mode="widthFix" />
        </swiper-item>
      </block>
    </swiper>
  </view>
  <!-- 分类数据 -->
  <view class="category-list">
    <block wx:for="{{categoryList}}" wx:key="{{item.name}}">
      <view class="category" bindtap="hancleClickItem" data-itemList="{{item}}">
        <view class="img">
          <image src="{{item.image_src}}" mode="widthFix" />
        </view>
        <view>
          <view class="text">{{item.name}}</view>
        </view>
      </view>
    </block>
  </view>

  <!-- 广告图 -->
  <view class="banner">
    <MarqueeUp />
    <view class="img-box">
      <image src="https://ae01.alicdn.com/kf/H5470c644e6454719a610620f1e6f67d9y.jpg" />
    </view>
  </view>

  <!-- 楼层数据 -->
  <view class="goods-list">
    <view class="title">
      <image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
      猜你喜欢
      <image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
    </view>
    <view class="product-list">
      <block wx:for="{{productList}}" wx:key="{{item}}">
        <view class="product">
          <image src="{{item.img_url}}" />
          <view class="name ellipsis">{{ item.name }}</view>
          <view class="info">
            <view class="price">{{ item.price }}</view>
            <view class="slogan">{{ item.slogan }}</view>
          </view>
        </view>
      </block>
    </view>
  </view>
</page>

4.2、实现搜索自定义组件

1、微信开发者工具创建自定义组件

根目录创建components/SearchInput文件,SearchInput右键选择新建Component,自定生成js json wxss wxml

2、代码实现

SearchInput.wxml

<view class="search_row">
  <navigator class="search_input" target="" url="../search/index" hover-class="navigator-hover" open-type="navigate">
    搜索
  </navigator>
</view>

SearchInput.wxss

.search_row {
  height90rpx;
  padding15rpx;
  background-color#eb4450;
}
.search_row .search_input {
  border-radius10rpx;
  background-color#fff;
  height100%;
  display: flex;
  justify-content: center;
  align-items: center;
}

引入自定义组件

首先要在页面的 json 文件中进行引用声明。还要提供对应的组件名和组件路径

{
  "usingComponents": {
    "search-input":"/components/SearchInput/SearchInput"
  }
}
  • usingComponents 引用声明
  • search-input 组件名称(SearchInput
  • "/components/SearchInput/SearchInput" 组件路径

页面中使用

<!--pages/home/index.wxml-->
<page class="home">
  <!-- 搜索 -->
  <view class="shop-input">
    <search-input></search-input>
  </view>
</page>

navigator

导航组件 类似超链接标签

属性名 类型 默认值 说明
target String self 在哪个目标上发生跳转,默认当前小程序,可选值self(当前小程序)/miniProgram(其他小程序)
url String 当前小程序内的跳转链接
open-type String navigate 跳转方式

open-type 合法值:

说明
navigate 保留当前页面,跳转到应用内的某个页面,但是不能跳到 tabbar 页面
redirect 关闭当前页面,跳转到应用内的某个页面,但是不允许跳转到 tabbar 页面。
switchTab 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
reLaunch 关闭所有页面,打开到应用内的某个页面
navigateBack 关闭当前页面,返回上一页面或多级页面。可通过 getCurrentPages 获取当前的页面栈,决定需要返回几层
exit 退出小程序,target="miniProgram"时生效

4.3、实现轮播图(swiper)

使用微信内置swiper组件实现

swiper

  • 滑块视图容器,
  • 默认宽度100%,高度150px;

常用属性

属性名 类型 默认值 说明
indicator-dots Boolean false 是否显示面板指示点
indicator-color Color rgba(0, 0, 0, .3) 指示点颜色
indicator-active-color Color #000000 当前选中的指示点颜色
autoplay Boolean false 是否自动切换
interval Number 5000 自动切换时间间隔
circular Boolean false s是否循环轮播

swiper-item

  • 仅可放置在swiper组件中,宽高自动设置为100%。

image

微信内置图片组件,默认宽度320px、高度240px,支持懒加载

常用属性:

属性名 类型 默认值 说明
src String 图片资源地址
mode String 'scaleToFill' 图片裁剪、缩放的模式
lazy-load Boolean false 图片懒加载

mode 的合法值

一共有13种模式,9种裁剪模式,4种缩放模式,常用widthFix

说明
scaleToFill 缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素
aspectFit 缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。
aspectFill 缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。
widthFix 缩放模式,宽度不变,高度自动变化,保持原图宽高比不变
heightFix 缩放模式,高度不变,宽度自动变化,保持原图宽高比不变
top 裁剪模式,不缩放图片,只显示图片的顶部区域
bottom 裁剪模式,不缩放图片,只显示图片的底部区域
center 裁剪模式,不缩放图片,只显示图片的中间区域
left 裁剪模式,不缩放图片,只显示图片的左边区域
right 裁剪模式,不缩放图片,只显示图片的右边区域
top left 裁剪模式,不缩放图片,只显示图片的左上边区域
top right 裁剪模式,不缩放图片,只显示图片的右上边区域
bottom left 裁剪模式,不缩放图片,只显示图片的左下边区域
bottom right 裁剪模式,不缩放图片,只显示图片的右下边区域

代码实现

  <!-- 轮播图 -->
  <view class="shop-swiper">
    <swiper class="" indicator-dots autoplay interval="5000">
      <block wx:for="{{swiperList}}" wx:key="{{item}}">
        <swiper-item class="" item-id="">
          <image  src="{{item.url}}" mode="widthFix" />
        </swiper-item>
      </block>
    </swiper>
  </view>

4.4、实现分类/广告图/实现楼层

手动代码实现,直接贴代码了,主要讲下楼层使用了请求获取数据

<!--pages/home/index.wxml-->
<page class="home">
  <!-- 搜索 -->
  <view class="shop-input">
    <search-input></search-input>
  </view>

  <!-- 轮播图 -->
  <view class="shop-swiper">
    <swiper class="" indicator-dots autoplay interval="5000">
      <block wx:for="{{swiperList}}" wx:key="{{item}}">
        <swiper-item  item-id="">
          <image class="" src="{{item.url}}" mode="widthFix" />
        </swiper-item>
      </block>
    </swiper>
  </view>
  <!-- 分类数据 -->
  <view class="category-list">
    <block wx:for="{{categoryList}}" wx:key="{{item.name}}">
      <view class="category" bindtap="hancleClickItem" data-itemList="{{item}}">
        <view class="img">
          <image src="{{item.image_src}}" mode="widthFix" />
        </view>
        <view>
          <view class="text">{{item.name}}</view>
        </view>
      </view>
    </block>
  </view>

  <!-- 广告图 -->
  <view class="banner">
    <MarqueeUp />
    <view class="img-box">
      <image src="https://ae01.alicdn.com/kf/H5470c644e6454719a610620f1e6f67d9y.jpg" />
    </view>
  </view>

  <!-- 楼层数据 -->
  <view class="goods-list">
    <view class="title">
      <image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
      猜你喜欢
      <image src="https://ae01.alicdn.com/kf/H551edcdd154a4936a5762971504fb3e2r.jpg"></image>
    </view>
    <view class="product-list">
      <block wx:for="{{productList}}" wx:key="{{item}}">
        <view class="product">
          <image src="{{item.img_url}}" />
          <view class="name ellipsis">{{ item.name }}</view>
          <view class="info">
            <view class="price">{{ item.price }}</view>
            <view class="slogan">{{ item.slogan }}</view>
          </view>
        </view>
      </block>
    </view>
  </view>
</page>
/* pages/home/index.wxss */
swiper {
  height260rpx;
}

/* 分类 */
.category-list {
  width92%;
  margin0 4%;
  padding0 0 30rpx 0;
  border-bottom: solid 2rpx #f6f6f6;
  display: -webkit-box;
  display: -webkit-flex;
  display: -ms-flexbox;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;

}

.category-list .category {
  width25%;
  margin-top50rpx;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.category-list .category .img {
  width100%;
  display: flex;
  justify-content: center;
}

.category-list .category .img image {
  width9vw;
  height9vw;
}


.category-list .category .text {
  margin-top16rpx;
  width100%;
  display: flex;
  justify-content: center;
  font-size24rpx;
  color#3c3c3c;
}

/* 广告图 */
.banner {
  width92%;
  margin40rpx 4%;
}

.banner .img-box {
  position: relative;
  width100%;
  height150rpx;
  margin-top20rpx;
  border-radius80rpx;
}

.banner .img-box::before {
  content"";
  position: absolute;
  widthcalc(100% + 6%);
  height180rpx;
  border-radius100rpx;
  top: -15rpx;
  left: -3%;
  z-index: -1;
  background-imagelinear-gradient(60deg, red, cornflowerblue, yellow, hotpink, salmon, lightgreen, sandybrown, violet);
  background-size300%;
  animation: animate_bg 5s infinite;
}

@keyframes animate_bg {

  0%,
  100% {
    background-position0%50%;
  }

  50% {
    background-position100%50%;
  }
}

.banner image {
  width100%;
  height20vw;
  border-radius10vw;
  box-shadow0rpx 5rpx 25rpx rgba(0, 0, 0, 0.3);
}

/* 楼层 */
.goods-list{
  width100%;
}
.goods-list .title {
  width100%;
  margin-top10rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  height80rpx;
  color#f47825;
  font-size30rpx;
}

.goods-list .title image {
  width30rpx;
  height30rpx;
}

.goods-list .product-list {
  width100%;
  padding0 4% 3vw 4%;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;

}

.product {
  width48%;
  border-radius20rpx;
  background-color#fff;
  margin0 0 15rpx 0;
  box-shadow0rpx 5rpx 25rpx rgba(0, 0, 0, 0.1);

}

.product image {
  width100%;
  height332rpx;
  border-radius20rpx 20rpx 0 0;
}

.product .name {
  width92%;
  padding10rpx 4%;
  font-size30rpx;
}

.product .info {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  width92%;
  padding10rpx 4% 10rpx 4%;
}

.product .info .price {
  color#e65339;
  font-size30rpx;
  font-weight600;
}

.product .info .slogan {
  color#807c87;
  font-size24rpx;
}

wx.request获取后端数据

发起 HTTPS 网络请求,具体请参阅微信文档

request + promise封装
class request {
  constructor() {
    this._baseUrl = 'https://www.fastmock.site/mock/31875da47f27128e357e3d3e6ac0cc37/api/';
    this._token = wx.getStorageSync('token');
    // this._header = {'Authorization': 'Bearer ' + token}
  }

  /**
   * GET类型的网络请求
   */

  getRequest(url, data, header = this._header) {
    return this.requestAll(url, data, header, 'GET')
  }

  /**
   * DELETE类型的网络请求
   */

  deleteRequest(url, data, header = this._header) {
    return this.requestAll(url, data, header, 'DELETE')
  }

  /**
   * PUT类型的网络请求
   */

  putRequest(url, data, header = this._header) {
    return this.requestAll(url, data, header, 'PUT')
  }

  /**
   * POST类型的网络请求
   */

  postRequest(url, data, header = this._header) {
    return this.requestAll(url, data, header, 'POST')
  }

  /**
   * 网络请求
   */

  requestAll(url, data, header, method) {
    return new Promise((resolve, reject) => {
      // 显示正在等待
      wx.showLoading({
        title"正在加载中",
        maskfalse
      });
      wx.request({
        urlthis._baseUrl + url,
        data: data,
        header: header,
        method: method,
        success: (res => {
          let {
            data
          } = res;
          // console.log(res);
          if (data.success && data.code == 200) {
            //200: 服务端业务处理正常结束
            resolve(data.data)
          } else {
            //其它错误,提示用户错误信息
            reject(data.data)
          }
        }),
        fail: (res => {
          reject(res)
        }),
        complete() => {
          // 隐藏正在等待图标
          wx.hideLoading();
        }
      })
    })
  }
}

export default request
页面使用request
import request from '../../utils/request';  //引入
const api = new request(); //创建实例
使用方法
  • Get请求----------------- api.getRequest(接口路径,入参(对象))
  • Post请求---------------- api.postRequest( 'home/getProductList',this.QueryParams )
  • .....

注意: 开发过程中需要勾选不校验合法域名、web-view业务域名等,生产上线登录小程序官方配置对应域名,不然无法发起请求。

4.5 动态获取楼层数据


import request from '../../utils/request';
const api = new request();
Page({

  /**
   * 页面的初始数据
   */

  data: {
    swiperList: [{
        id1,
        url'https://ae01.alicdn.com/kf/H9450011bbd2e480882aa986c8ceaa312i.jpg'
      },
      {
        id2,
        url'https://ae01.alicdn.com/kf/H1eeb66ae770b44ad9147c1123fb63a32P.jpg'
      },
      {
        id3,
        url'https://ae01.alicdn.com/kf/H0fdf0f299c2a49d5995582f085d24522G.jpg'
      },
    ],
    // 分类菜单
    categoryList: [{
        id1,
        "name""办公",
        image_src'https://ae01.alicdn.com/kf/H974e4de8124b4783b7768d06a2b847ab0.jpg'
      },
      {
        id2,
        "name""家电",
        image_src'https://ae01.alicdn.com/kf/H53dede49e8bd4037b702eeb1f83f1b55M.jpg'
      },
      {
        id3,
        "name""服饰",
        image_src'https://ae01.alicdn.com/kf/H575960420d6b4990960160fba79f443bw.jpg'
      },
      {
        id4,
        "name""日用",
        image_src'https://ae01.alicdn.com/kf/H30b911d8668d46deb9cfed0fd68da1fex.jpg'
      },
      {
        id5,
        "name""蔬果",
        image_src'https://ae01.alicdn.com/kf/Hd3edad8b6c834a23aa214fa83ecca4ddm.jpg'
      },
      {
        id6,
        "name""运动",
        image_src'https://ae01.alicdn.com/kf/Hdb7bb002f13c4b79bc876c28555ca7ceV.jpg'
      },
      {
        id7,
        "name""书籍",
        image_src'https://ae01.alicdn.com/kf/Hc1a477b73bc147728ef29ab6c74af0f0O.jpg'
      },
      {
        id8,
        "name""文具",
        image_src'https://ae01.alicdn.com/kf/H23940f3a1f3145418b439c32521e263b4.jpg'
      }
    ],
    //猜你喜欢列表
    productList: [],
  },
  // 定义接口参数对象 
  QueryParams: {
    // 页码
    pagenum: 1,
    // 页容量
    pagesize: 10
  },
  // 总页码
  totalPage: 1,

  // 获取商品列表
  getProductList() {
    // 使用微信小程序内置发送请求的代码来获取数据
    api.postRequest(
      'home/getProductList',
      this.QueryParams
    ).then(res => {
      console.log(res);
      // 执行加载下一页的时候 productList 应该是 叠加 
      // 拼接数组 
      let productList = [...this.data.productList, ...res.productList];
      this.setData({
        productList
      });

      // 计算总页码
      this.totalPage = Math.ceil(res.total / this.QueryParams.pagesize);
    })
  },
  // 点击分类事件
  hancleClickItem(e) {
    let data = e.currentTarget.dataset.itemlist;
    wx.showToast({
      title'点击了' + data.name,
      icon'none'
    })
  },
  /**
   * 生命周期函数--监听页面加载
   */

  onLoad: function (options{

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */

  onReady: function ({

    this.getProductList()
  },

  /**
   * 生命周期函数--监听页面显示
   */

  onShow: function ({

  },

  /**
   * 生命周期函数--监听页面隐藏
   */

  onHide: function ({

  },

  /**
   * 生命周期函数--监听页面卸载
   */

  onUnload: function ({

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */

  onPullDownRefresh: function ({
    // wx.showNavigationBarLoading() //在标题栏中显示加载
    this.QueryParams.pagenum = 1;
    this.setData({
      productList: []
    });
    this.getProductList();
    //  结束下拉刷新组件的显示
    wx.stopPullDownRefresh();
  },

  /**
   * 页面上拉触底事件的处理函数
   */

  onReachBottom: function ({
    // 判断有没有下一页数据
    if (this.QueryParams.pagenum >= this.totalPage) {
      // 没有数据
      wx.showToast({
        title'没有更多数据了',
        icon'none',
        duration1500,
        // 蒙版  遮罩层 
        mask: false
      });

    } else {
      this.QueryParams.pagenum++;
      this.getProductList();
    }
  },

  /**
   * 用户点击右上角分享
   */

  onShareAppMessage: function ({

  }
})

4.6 实现下拉刷新上啦加载更多

首页开启下拉刷新

(1)、在index.json 中开启下拉刷新,需要设置backgroundColor,或者是backgroundTextStyle ,因为加载的动画可能会是白色背景,会看不清。

{
  "enablePullDownRefresh"true,
  "onReachBottomDistance"50,
  "usingComponents": {
    "search-input":"/components/SearchInput/SearchInput"
  }
}

(2)、在onPullDownRefresh 函数中监听下拉事件,执行刷新方法

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */

  onPullDownRefresh: function ({
    // wx.showNavigationBarLoading() //在标题栏中显示加载
    this.QueryParams.pagenum = 1;
    this.setData({
      productList: []
    });
    this.getProductList();
    //  结束下拉刷新组件的显示
    wx.stopPullDownRefresh();
  },

开启上拉加载更多

直接监听 onReachBottom事件,实现加载更多

/**
   * 页面上拉触底事件的处理函数
   */

  onReachBottom: function ({
    // 判断有没有下一页数据
    if (this.QueryParams.pagenum >= this.totalPage) {
      // 没有数据
      wx.showToast({
        title'没有更多数据了',
        icon'none',
        duration1500,
        // 蒙版  遮罩层 
        mask: false
      });

    } else {
      this.QueryParams.pagenum++;
      this.getProductList();
    }
  },

今天内容就到此,下一篇继续带撸商品分类以及商品详情的内容,感谢您的阅读以及支持,欢迎在下方留言一起共同学习进步!

💕看完三件事:

  • 点赞 | 你可以点击——>收藏——>退出一气呵成,但别忘了点赞🤭
  • 关注 | 点个关注,下次不迷路😘
最简单!小程序实战篇(中)