项目首页
项目首页
到目前为止,我们已经完成了小程序项目的初始化的操作,例如:创建小程序项目、组织目录结构、封装了通用模块和网络请求模块,从这一节开始,我们开始做小程序的功能开发,首先要完成的是小程序的首页功能。

01. 获取首页数据
思路分析:
我们先来分析下在项目的首页我们需要完成的一些功能
- 轮播图区域
- 商品导航区域
- 活动宣传区域
- 猜你喜欢区域
- 人气推荐区域
在实现这些功能之前,我们需要先获取数据,在获取数据以后,然后进行页面的渲染,同时完成进行功能的开发。
因为需要同时获取 5 个接口的数据,所以我们使用并发请求来实现。这样能够提升页面的渲染速度。
实现步骤:
- 封装接口请求函数,可以一个个封装,也可以直接使用
all方法进行封装 - 在页面 .js 文件中导入封装的接口
API函数 - 在
onLoad钩子函数中调用方法,获取首页轮播图数据
落地代码:
➡️ api/index.js: 准备接口 API
import http from '../utils/http'
/**
* 通过并发请求获取首页的数据
*/
export const reqIndexData = () => {
return instance.all(
http.get('/index/findBanner'),
http.get('/index/findCategory1'),
http.get('/index/advertisement'),
http.get('/index/findListGoods'),
http.get('/index/findRecommendGoods')
)
}
➡️ page/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.data[0],
categoryList: res.data[1],
activeList: res.data[2],
hotList: res.data[3],
guessList: res.data[4]
})
},
// 监听页面加载
onLoad() {
// 调用获取首页数据的回调
this.getIndexData()
}
})
02. 分析轮播图区域并渲染
分析轮播图结构:

轮播图区域采用组件化方式开发,我们在 index 目录下新建 banner 文件夹,里面存放轮播图组件。
在 index/index.json 文件中导入组件,然后将组件当成标签进行使用
{
"usingComponents": {
"banner": "./banner/banner"
}
}
<!-- 轮播图区域 -->
<banner />
swiper、swiper-item、navigator、image组件实现页面结构的搭建block渲染数组,实现列表渲染- 使用
flex布局实现了页面样式的绘制
另外需要注意的是:轮播图面板指示点不支持自定义,所以只能页面结构的方式,实现轮播图的面板指示点功能
<!--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>
渲染页面结构:
➡️ page/index/index.js
<!-- 轮播图区域 -->
+ <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>
03. 实现轮播图和指示点的联动
思路分析:

当轮播图进行切换时,面板指示点也要跟随着进行切换,如上图。
轮播图和指示点进行联动,当切换到第二张轮播图时,第二个面板指示点高亮,如果想实现这种一一对应的关系,需要借助索引
首先在 data 中初始化状态 activeIndex 默认为 0,代表第一个高亮,也是用来接收切换后的轮播图索引
然后使用 swiper 组件的 change 事件,监听轮播图是否发生改变,如果改变,则获取到轮播图的索引,赋值到data中,
通过 activeIndex 对小圆点进行动态的渲染
实现思路:
- 在
data中初始化状态activeIndex默认为 0 - 给
swiper绑定bindchange事件,监听轮播图是否切换,将切换后轮播图的索引赋值给activeIndex - 利用
activeIndex对小圆点进行动态的渲染
落地代码:
➡️ pages/index/banner/banner.js
// pages/index/banner/banner.js
Component({
/**
* 组件的属性列表
*/
properties: {
// 轮播图数据
bannerList: {
type: Array,
value: []
}
},
/**
* 组件的初始数据
*/
data: {
+ activeIndex: 0 // 被激活的轮播图索引,默认是 0
},
/**
* 组件的方法列表
*/
methods: {
+ // 获取被激活的轮播图索引
+ getSwiperIndex(event) {
+ // console.log(event)
+ const { current } = event.detail
+
+ this.setData({
+ activeIndex: current
+ })
+ }
}
})
➡️ 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)"
+ bindchange="getSwiperIndex"
>
<!-- 通过 block 标签对 轮播图数据 进行渲染 -->
<block wx:for="{{ bannerList }}" wx:key="index">
<!-- coding... -->
</block>
</swiper>
<!-- 轮播图的面板指示点,因为面板指示点不支持自定义,所以我们只能通过自定义结构的方式 -->
<view class="indicator">
<!-- active 类名:当前被激活的面板指示点颜色 -->
<!-- rectangle 类名:默认的面板指示点颜色 -->
<text
wx:for="{{bannerList.length}}"
wx:key="id"
+ class="{{ index === activeIndex ? 'active rectangle' : 'rectangle' }}"
></text>
</view>
</view>
04. 分析商品导航区域并渲染
商品导航结构分析:

商品导航区域采用组件化方式开发,我们在 index 目录下新建 entrance 文件夹,里面存放导航分类组件。
- 采用
view、navigator、image、text组件实现了进行页面结构的搭建 - 使用
flex布局实现了页面样式的绘制
<!-- 导航分类 -->
<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=1"
>
<image class="nav-img" src="导航路径" />
<text class="nav-text">导航名字</text>
</navigator>
</view>
</view>
在 index/index.json 文件中导入组件,然后将组件当成标签进行使用
{
"usingComponents": {
// ...
"entrance": "./entrance/entrance"
},
"navigationBarTitleText": "慕尚花坊"
}
<!-- 导航分类 -->
<entrance />
渲染导航分类结构:
➡️ page/index/index.wxml
<!-- 导航分类 -->
<entrance cateList="{{ categoryList }}" />
➡️ pages/index/entrance/entrance.js
Component({
// 组件的属性列表
properties: {
cateList: {
type: Array,
value: []
}
},
// coding...
}
➡️ 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>
➡️ 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>
05. 分析猜你喜欢+人气推荐并渲染
猜你喜欢和人气推荐区域结构分析:
猜你喜欢和人气推荐的布局是一致的,所以将两个模块放到一起实现
商品列表在项目中属于常见的结构,商品列表区域依然采用组件化方式开发,商品列表组件由两个组件进行构成:
goods-list商品列表组件,包含者商品卡片组件,用来展示商品列表goods-card商品卡片组件,承载某个商品的结构,用来展示单个商品
goods-list 商品列表组件包含三个结构:标题(允许用户自定义)、商品列表(商品卡片)、查看更多
<!--components/goods-list/goods-list.wxml-->
<!-- 商品列表组件 -->
<view class="goods_container">
<!-- 标题 -->
<view class="goods_title">{{title}}</view>
<!-- 列表区域 -->
<view class="goods_card_list">
<goods-card></goods-card>
<goods-card></goods-card>
<goods-card></goods-card>
</view>
<!-- 查看更多 -->
<view class="goods_more">
<navigator
class="goods_more_btn"
url="/pages/goods/list/index"
hover-class="navigator-hover" open-type="navigate"
>
查看更多
</navigator>
</view>
</view>
goods-card 商品卡片组件由以下几部分组成
<!-- 列表分类卡片 -->
<view class="goods_cart_container">
<navigator class="navigator_nav" url="/pages/goods/detail/detail?goodsId={{goodItem.id}}">
<!-- 商品图片 -->
<image class="good_img" src="/static/images/floor.jpg" mode="widthFix" />
<!-- 商品详细信息 -->
<view class="goods_item_info">
<!-- 商品名称 -->
<text class="goods_item_info_name">亲爱的/情人节网红款/19枝</text>
<!-- 商品描述 -->
<text class="goods_item_info_promo">情人节新品情人节新品情人节新品情人节新品</text>
<!-- 商品价格 -->
<view class="goods_item_info_bottom">
<view class="goods_item_info_price">
<text class="text">¥</text>399
</view>
<view class="goods_item_info_origin_price">
<text class="text">¥</text> 1{{goodItem.marketPrice}}
</view>
<!-- 加入购物车图片 -->
<view class="goods_item_info_btn">
<image class="goods_image" src="/static/images/buybtn.png" mode="" />
</view>
</view>
</view>
</navigator>
</view>
渲染导航分类结构:
只不过在渲染数据的时候需要将数据传递给 goods-list 商品列表组件, goods-list组件内部传递给 goods-card 商品卡片组件,goods-card 组件内部渲染数据。
➡️ page/index/index.wxml:将数据传递给 goods-list 组件
<!-- 商品列表 -->
<goods-list title="猜你喜欢" list="{{ guessList }}"></goods-list>
<goods-list title="人气推荐" list="{{ hotList }}"></goods-list>
➡️ components/goods-list/goods-list.js:接收首页传递的 list 数据
// components/goods-list/index.js
Component({
// 组件的属性列表
properties: {
// 列表标题
title: {
type: String,
value: '',
},
// 传递的列表数据
list: {
type: Array,
value: []
}
}
// coding...
}
➡️ components/goods-list/goods-list.wxml:遍历 goods-item 组件,并将数据传递给 goods-item
<!-- 商品列表组件 -->
+ <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 }}"></goods-card>
</view>
<!-- 查看更多 -->
<!-- coding -->
</view>
➡️ components/goods-list/goods-item.js:将数据传递给 goods-item 组件
// components/goods-card/goods-card.js
Component({
// 组件的属性列表
properties: {
// 每一项商品的数据
goodItem: {
type: Object,
value: {}
}
}
// coding...
}
➡️ components/goods-list/goods-item.wxml:将数据传递给 goods-item 组件
<!-- 列表分类卡片 -->
<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>
06. 首页骨架屏组件
思路分析:
骨架屏是页面的一个空白版本,开发者会使用 CSS 绘制一些灰色的区块,将页面内容大致勾勒出轮廓。
通常会在页面完全渲染之前,将骨架屏代码进行展示,待数据加载完成后,再替换成真实的内容。
骨架屏的设计旨在优化用户体验。
在进行项目开发时,我们需要手工维护骨架屏的代码,当业务变更时,同样需要对骨架屏代码进行调整。
为了方便开发者进行骨架屏的绘制,开发者工具提供了自动生成骨架屏代码的能力。
使用步骤:
- 使用微信开发者工具为当前正在预览的页面生成骨架屏代码,工具入口位于模拟器面板右下角三点处

点击生成骨架屏,将有弹窗提示是否允许插入骨架屏代码。
确定后将在当前页面同级目录下生成
page.skeleton.wxml和page.skeleton.wxss两个文件,分别为骨架屏代码的模板和样式。

骨架屏代码通过小程序模板(template)的方式引入 以
pages/index/index页面为例,引入方式如下<!-- pages/index/index.wxml 引入模板 -->
<!-- 引入骨架屏 -->
<import src="index.skeleton.wxml"/>
<!-- 使用骨架屏 -->
<template is="skeleton" wx:if="{{loading}}" data="{{}}"/>
<!-- 页面模板 -->
<view class="container">
<!-- coding... -->
</view>/* pages/index/index.wxss 中引入样式 */
@import './skeleton/index.skeleton.wxss';
在
data中声明loading的状态为true当数据请求完以后将loading的状态为false// coding...
Page({
data: {
bannerList: [], // 轮播图数据
categoryList: [], // 一级分类数据
activeIndex: 0, // 被激活的轮播图索引
topList: [], // 人气推荐
guessList: [], // 猜你喜欢
+ loading: true // 数据是否在加载完毕
},
// 页面加载时执行
async onLoad() {
// coding...
},
// 获取页面的数据
async getPageData() {
// coding...
// 将返回的数据赋值给 data 中的状态
this.setData({
bannerList,
categoryList,
topList,
guessList,
+ loading: false
})
},
// coding...
})
查看页面效果
