Skip to main content

项目首页

项目首页​

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

01. 获取首页数据​

思路分析:

我们先来分析下在项目的首页我们需要完成的一些功能

  1. 轮播图区域
  2. 商品导航区域
  3. 活动宣传区域
  4. 猜你喜欢区域
  5. 人气推荐区域

在实现这些功能之前,我们需要先获取数据,在获取数据以后,然后进行页面的渲染,同时完成进行功能的开发。

因为需要同时获取 5 个接口的数据,所以我们使用并发请求来实现。这样能够提升页面的渲染速度。

实现步骤:

  1. 封装接口请求函数,可以一个个封装,也可以直接使用 all 方法进行封装
  2. 在页面 .js 文件中导入封装的接口 API 函数
  3. 在 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 />
  1. swiper、swiper-item 、navigator、image 组件实现页面结构的搭建
  2. block 渲染数组,实现列表渲染
  3. 使用 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 对小圆点进行动态的渲染

实现思路:

  1. 在 data 中初始化状态 activeIndex 默认为 0
  2. 给 swiper 绑定 bindchange 事件,监听轮播图是否切换,将切换后轮播图的索引赋值给activeIndex
  3. 利用 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 文件夹,里面存放导航分类组件。

  1. 采用 view、navigator 、image 、text 组件实现了进行页面结构的搭建
  2. 使用 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. 分析猜你喜欢+人气推荐并渲染​

猜你喜欢和人气推荐区域结构分析:

猜你喜欢和人气推荐的布局是一致的,所以将两个模块放到一起实现

​

商品列表在项目中属于常见的结构,商品列表区域依然采用组件化方式开发,商品列表组件由两个组件进行构成:

  1. goods-list 商品列表组件,包含者商品卡片组件,用来展示商品列表
  2. 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 绘制一些灰色的区块,将页面内容大致勾勒出轮廓。

通常会在页面完全渲染之前,将骨架屏代码进行展示,待数据加载完成后,再替换成真实的内容。

骨架屏的设计旨在优化用户体验。

在进行项目开发时,我们需要手工维护骨架屏的代码,当业务变更时,同样需要对骨架屏代码进行调整。

为了方便开发者进行骨架屏的绘制,开发者工具提供了自动生成骨架屏代码的能力。

使用步骤:

  1. 使用微信开发者工具为当前正在预览的页面生成骨架屏代码,工具入口位于模拟器面板右下角三点处
  1. 点击生成骨架屏,将有弹窗提示是否允许插入骨架屏代码。

    确定后将在当前页面同级目录下生成 page.skeleton.wxml 和 page.skeleton.wxss 两个文件,分别为骨架屏代码的模板和样式。

  1. 骨架屏代码通过小程序模板(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';
  1. 在 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...
    })

  1. 查看页面效果