风袖电商小程序笔记第二周【LinUI组件库的引入与编程原则】

上一周概念和相关的配置讲的比较多,实际涉及到小程序业务的很少;接下来的这周是概念比较多,业务比较少,整体都是在铺垫一个坚实的基础,毕竟—-万丈高楼平地起!

优惠券的一些基本概念

前端:涉及到核算,不仅仅是满100减20,比如会有以下情况:

  • 优惠券类型:满减、折扣、使用条件(全场券,品类券)
  • 过期,比如领取之后多少天过期

两大概念:领取和使用

  • 领取:通过活动的方式领取,活动的领取时间,结束时间和在线时间;优惠券使用的范围也就是品类券,会和优惠券的Coupon绑定一起,而不是和商品的分类绑定一起

优惠券入口

新建优惠券模型:

model\activity.js

1
2
3
4
5
6
7
8
9
10
11
import { Http } from "../utils/http";
class Activity {
static LocationD = "a-2";
static getHomeLocationD() {
return Http.request({
url: `activity/name/${Activity.LocationD}`,
});
}
}
// 导出
export { Activity };

pages\home\home.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 页面的初始数据
*/
data: {
themeA: null,
bannerB: null,
grid: [],
activity: null,
},

/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
this.initAllData();
},
// 初始化数据
async initAllData() {
// await去接受promise;获取顶部图片
const themeA = await Theme.getHomeLocationA();
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
//六宫格
const grid = await Category.getHomeLocationC();
//优惠券入口
const activity = await Activity.getHomeLocationD();

this.setData({
themeA: themeA[0],
bannerB: bannerB,
grid: grid,
activity: activity,
});
},

pages\home\home.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

<view>
<image class="top-theme" src="{{themeA.entrance_img}}" />

<swiper
class="swiper"
indicator-dots
indicator-active-color="#157658"
autoplay
circular
>
<block wx:for="{{bannerB.items}}" wx:key="index">
<swiper-item>
<image class="swiper" src="{{item.img}}"></image>
</swiper-item>
</block>
</swiper>
<s-category-grid grid="{{grid}}"></s-category-grid>
<image class="activity" src="{{activity.entrance_img}}"></image>
</view>

pages\home\home.wxss

1
2
3
4
5
.activity{
margin-top: 20rpx;
width: 100%;
height: 310rpx;
}

背景颜色到底怎么设置?

根据设计图可以看到每个业务对象之间是有间隔的,并且是灰色背景,当然最简单的方式就是加入view,然后设置背景颜色,但是这样的代码很冗余,这节中将设置页面的背景颜色;

官方文档:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/page.html

设置窗口的背景色:backgroundColor:#ffffff;

pages\home\home.json

1
2
3
4
5
6
7
{
"usingComponents": {
"s-category-grid": "/components/category-grid/index"
},
"enablePullDownRefresh": true,
"backgroundColor": "#000000"
}

会发现添加了之后没用,这是因为配置的是窗体背景色,只会在下拉刷新或上拉加载时露出,而不是页面的常规背景色(enablePullDownRefresh是设置刷新)

真机预览的时候,调用的API需要是https的,如果不是,需要点击预览,扫描二维码,然后在真机小程序的三个点,打开调试模式,就可以访问http的API了

既然这种方式不行,试下直接的方式,直接在页面最外面的view设置backgroundColor,这种方式最直接简单,但是有以下问题需要解决:有两个缺点,一个是所有页面都需要这样写,另外背景颜色只作用于页面内容,我们需要实现的是全屏背景色

pages\home\home.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<view class="container">
<image class="top-theme" src="{{themeA.entrance_img}}" />

<swiper
class="swiper"
indicator-dots
indicator-active-color="#157658"
autoplay
circular
>
<block wx:for="{{bannerB.items}}" wx:key="index">
<swiper-item>
<image class="swiper" src="{{item.img}}"></image>
</swiper-item>
</block>
</swiper>
<s-category-grid grid="{{grid}}"></s-category-grid>
<image class="activity" src="{{activity.entrance_img}}"></image>
</view>

pages\home\home.wxss

1
2
3
.container{
background-color: #000000;
}
  • 顶部凸显一个黑块,这是因为在app.wxss中也有一个container类,把他删除即可;
  • 删除之后又会发现图片与图片之间有间距,这是图片自带的间距,可以在.top-theme中加入display: flex;
  • 六宫格没有设置背景色,默认是透明的,所以背景色是黑色就会变黑色,修改方式就是给六宫格设置一个白色背景色:components\category-grid\index.wxss文件中的.container加入 background-color: #ffffff;
  • 优惠券底部也有黑色背景,解决方式一样,在.activity中加入display: flex;

最终方式:

app.wxss

1
2
3
page{
background-color: #f5f5f5;
}

这样整个小程序页面的背景色都是设置为 #f5f5f5,也不用单独每个页面设置,而且不仅只作用于页面内容,而是作用与整个页面

页面是否合并HTTP请求

上面已经完成了优惠券入口,接下来完成每周上新这个主题;这里涉及到很多知识点,单这个每周上新的样式这周都没写完…..

首先来个最正常的写法:

model\theme.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//业务对象
// theme、banner、spu、sku、address、user
import { Http } from "../utils/http";
class Theme {
// 不用实例化可以直接调用;Http.reques已经返回是一个promise,在这里直接返回就可以
// 打折
static LocationA = "t-1";
static LocationE = "t-2";
static getHomeLocationA() {
return Http.request({
url: "theme/by/names",
data: {
names: Theme.LocationA,
},
});
}
// 每周上新
static getHomeLocationE() {
return Http.request({
url: "theme/by/names",
data: {
names: Theme.LocationE,
},
});
}
}
// 导出
export { Theme };

pages\home\home.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async onLoad(options) {
this.initAllData();
},
// 初始化数据
async initAllData() {
// await去接受promise;获取顶部图片
const themeA = await Theme.getHomeLocationA();
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
//六宫格
const grid = await Category.getHomeLocationC();
//优惠券入口
const activity = await Activity.getHomeLocationD();
// 每周上新
const newweek = await Theme.getHomeLocationE();
this.setData({
themeA: themeA[0],
bannerB: bannerB,
grid: grid,
activity: activity,
newweek,
});
},

改变的地方:创建getHomeLocationE的请求,在首页调用接口;

思考:在theme这个对象,getHomeLocationA和getHomeLocationE去请求的接口都是一样的,只是参数不一样,这里完全可以写成拼接的方式去合并请求数据,而不是分2次请求,多次请求会导致服务器性能下降;这里就引出了本节的重点,页面是否合并HTTP请求

页面是否合并HTTP请求可以从三个方面讨论:

衡量的依据:Http的请求数量;Http请求有多少次数据库查询;接口灵活性、维护性;

  1. 全部合并:Http的请求数量是最少的,这种方式Http请求的数据库查询次数适中(每一个http都会去查询数据库,哪怕是重复的查询,但是发送一次不会重复查询,比如我3个请求都是查询A和B的数据库,因为发送3此请求所以查询了3次,但是我一个请求查询A和B的数据库,实际上我只查询了2次,把A和B的查询结果一次性返回),灵活性和维护性最低;
  2. 每一个数据都发送一次请求:这种方式Http的请求数量最多,Http请求的数据库查询次数最多;灵活性和维护性高;
  3. 部分合并,部分单独:上面的2种方式都有极端的操作和严重的缺点,灵活性和维护性高亦或者低;接下来这种方式是汲2种方式之所长,有选择的合并接口,比如一个业务完全可以一次得到的,就一次接口得到,业务完全不同的,就分开,这样可以提升维护性,也可以提升性能。

说点另外的:每一次http请求并不是都会进行数据库查询,所以这部分请求影响服务器性能很小,但是会影响一定的带宽,比如成千上万的用户像该接口发起请求,有些没有权限的都会直接返回404等,没有查询数据库;影响性能的主要分为IO密集型和CPU密集型,比如数据库查询就是典型的IO操作,视频解码是CPU密集型,通常CPU密集型会放到另外服务器

优化:(这里添加了另外2个主题)

model\theme.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  static LocationA = "t-1";
static LocationE = "t-2";
static LocationF = "t-3";
static LocationH = "t-4";
// 合并接口,查询主题
static getThemes() {
const names = `${Theme.LocationA},${Theme.LocationE},${Theme.LocationF},${Theme.LocationH}`;
return Http.request({
url: "theme/by/names",
data: {
names: names,
},
});
}

pages\home\home.js(发送请求)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 初始化数据
async initAllData() {
// await去接受promise;获取顶部图片
// const themeA = await Theme.getHomeLocationA();
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
//六宫格
const grid = await Category.getHomeLocationC();
//优惠券入口
const activity = await Activity.getHomeLocationD();
// 每周上新
// const newweek = await Theme.getHomeLocationE();
// 获取主题数组
const themes = await Theme.getThemes();
this.setData({
// themeA: themeA[0],
bannerB: bannerB,
grid: grid,
activity: activity,
// newweek,
});
}

至此,接口合并完成,但是查看接口返回的数据可以发现,返回来是一个数组,显然这不能直接使用,下一小节中将解决这个问题;

函数式编程概述

在上一节中,接口返回的是一个数组,通过分析数组可以发现,每个数组都有一个names,names分别是t-1、t-2、t-3、t-4;这正好对应每一个主题,所以可以通过判断names来设置这个主题应该放在哪个位置上

通过find进行函数式编程

1
2
3
4
5
6
7
// 获取主题数组
const themes = await Theme.getThemes();
// 查询这个数组下name为t-1的数组;函数式编程
const themeA = themes.find((t) => t.name === "t-1");
const themeE = themes.find((t) => t.name === "t-2");
const themeF = themes.find((t) => t.name === "t-3");
const themeH = themes.find((t) => t.name === "t-4");

themes是一个数组,find去查询每一个数组,t这个参数代表数组项,判断这个数组项里面的name是不是t-1,是的话就返回;

当然,这里还不是最终态,这里写的t-1、t-2、t-3和t-4这些初看完全不知道用意,完全凭空出来的,这在后面难以维护,其实这些在theme这个类里面已经声明过了,可以直接在类里面取值,这个在类保存数据,对象保存状态这节会讲解到。

第三次作业(动态获取图片宽高)

最讨厌的作业….

图片有时候不是一尘不变的,比如在CMS传任意一张图片,都可以在前端保持比较好的展现

微信小程序媒体组件图片组件:https://developers.weixin.qq.com/miniprogram/dev/component/image.html

类可以保存数据,对象可以保存状态

思考过程:在函数式编程概述这节中,使用函数式编程的方式解决了for循环这种繁琐的写法,但是却又引出了新的问题,类似t-1、t-2、t-3和t-4这种硬编码,像是随意凭空出现的,而且这个硬编码已经在themes这个类已经定义了,所以直接取值就可以了,否则后期难以维护,那现在优化后的代码是这样的:

1
2
3
4
5
6
7
// 获取主题数组
const themes = await Theme.getThemes();
// 查询这个数组下name为t-1的数组;函数式编程
const themeA = themes.find((t) => t.name === Theme.LocationA);
const themeE = themes.find((t) => t.name === Theme.LocationE);
const themeF = themes.find((t) => t.name === Theme.LocationF);
const themeH = themes.find((t) => t.name === Theme.LocationH);

好的,硬编码的问题解决了,现在有新的问题:如果我page这个页面下有一个新的方法,也需要用的themes这个接口返回来的数组,怎么办?第一时间想到的代码是这样:

1
2
3
4
5
6
7
async test () {
const themes = await Theme.getThemes();
const themeA = themes.find((t) => t.name === Theme.LocationA);
const themeE = themes.find((t) => t.name === Theme.LocationE);
const themeF = themes.find((t) => t.name === Theme.LocationF);
const themeH = themes.find((t) => t.name === Theme.LocationH);
}

这样写第一个是代码冗余,第二个是这样多请求了一次接口;那直接把themes存到页面page下的data,不就可以节省发送请求又可以解决冗余问题,这时候代码是这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
async initAllData() {
// await去接受promise;获取顶部图片
// const themeA = await Theme.getHomeLocationA();
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
//六宫格
const grid = await Category.getHomeLocationC();
//优惠券入口
const activity = await Activity.getHomeLocationD();
// 每周上新
// const newweek = await Theme.getHomeLocationE();
// 获取主题数组
const themes = await Theme.getThemes();
this.data.themes= themes
// 查询这个数组下name为t-1的数组;函数式编程
const themeA = themes.find((t) => t.name === Theme.LocationA);
const themeE = themes.find((t) => t.name === Theme.LocationE);
const themeF = themes.find((t) => t.name === Theme.LocationF);
const themeH = themes.find((t) => t.name === Theme.LocationH);
this.setData({
themeA,
themeE,
themeF,
themeH,
bannerB,
grid,
activity,
});
},
async test() {
if (this.data.themes.lenght === 0) {
// 业务逻辑
const themeA = this.data.themes.find((t) => t.name === Theme.LocationA);
const themeE = this.data.themes.find((t) => t.name === Theme.LocationE);
const themeF = this.data.themes.find((t) => t.name === Theme.LocationF);
const themeH = this.data.themes.find((t) => t.name === Theme.LocationH);
}
},

可以看到这种方式只是解决了http请求多次的问题,但是没有解决冗余问题,还是得把themeA、themeE等这些找出来,那除了这种方式外,还有其他办法吗,答案是有的,比如可以使用缓存的方式(复杂,并且缓存数据是永久的)、app全局变量(大材小用),这些方式都只是解决了一个减少http请求的问题,并没有解决代码冗余问题;接下来就会用到一个知识点:类可以保存数据,不可以保存状态,但是对象可以保存状态;

1
2
3
4
5
6
7
const t = new Theme();
t.a = 1;
const t2 = new Theme();
t2.a = 1;
Theme.a = 1;
Theme.a = 2;

通过这段代码可以知道,t和t2的a是不同的,但是类Theme第一次赋值1,后面又复制2,最终输出的a肯定是2;

重构Theme获取

先来最终代码:

model\theme.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//业务对象
// theme、banner、spu、sku、address、user
import { Http } from "../utils/http";
class Theme {
// 不用实例化可以直接调用;Http.reques已经返回是一个promise,在这里直接返回就可以
// 打折
static LocationA = "t-1";
static LocationE = "t-2";
static LocationF = "t-3";
static LocationH = "t-4";
// 对象,保存状态
themes = [];
// 合并接口,查询主题
async getThemes() {
const names = `${Theme.LocationA},${Theme.LocationE},${Theme.LocationF},${Theme.LocationH}`;
this.themes = await Http.request({
url: "theme/by/names",
data: {
names: names,
},
});
}
// 打折
getHomeLocationA() {
return this.themes.find((t) => t.name === Theme.LocationA);
}
// 每周上新
getHomeLocationE() {
return this.themes.find((t) => t.name === Theme.LocationE);
}
getHomeLocationF() {
return this.themes.find((t) => t.name === Theme.LocationF);
}
getHomeLocationH() {
return this.themes.find((t) => t.name === Theme.LocationH);
}
}
// 导出
export { Theme };

pages\home\home.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// pages/home/home.js

import { Activity } from "../../model/activity";
import { Banner } from "../../model/banner";
import { Category } from "../../model/category";
import { Theme } from "../../model/theme";

Page({
/**
* 页面的初始数据
*/
data: {
themeA: null,
themeE: null,
bannerB: null,
grid: [],
activity: null,
},

/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
this.initAllData();
// this.test();
},
// 初始化数据
async initAllData() {
// await去接受promise;获取顶部图片
// const themeA = await Theme.getHomeLocationA();
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
//六宫格
const grid = await Category.getHomeLocationC();
//优惠券入口
const activity = await Activity.getHomeLocationD();
// 每周上新
// const newweek = await Theme.getHomeLocationE();
// 获取主题数组
// const themes = await Theme.getThemes();
// 实例化
const themes = new Theme();
await themes.getThemes();
// this.data.themes = themes;
// const t = new Theme();
// t.a = 1;
// const t2 = new Theme();
// t2.a = 1;
// Theme.a = 1;
// Theme.a = 2;
// 查询这个数组下name为t-1的数组;函数式编程
const themeA = themes.getHomeLocationA();
const themeE = themes.getHomeLocationE();
console.log(themeA);

// const themeF = themes.getHomeLocationF;
// const themeH = themes.getHomeLocationH;
this.setData({
themeA,
themeE,
// themeF,
// themeH,
bannerB,
grid,
activity,
});
},
async test() {
// if (this.data.themes.lenght === 0) {
// // 业务逻辑
// const themeA = this.data.themes.find((t) => t.name === Theme.LocationA);
// const themeE = this.data.themes.find((t) => t.name === Theme.LocationE);
// const themeF = this.data.themes.find((t) => t.name === Theme.LocationF);
// const themeH = this.data.themes.find((t) => t.name === Theme.LocationH);
// }
},

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},

/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},

/**
* 用户点击右上角分享
*/
onShareAppMessage() {},
});

思路:

  1. 在theme这个类中创建themes来存放主题数组,也属于对象值,存放状态,
  2. 然后getThemes不直接返回,而是存放在这个themes数组中;
  3. 这个时候getHomeLocationA和getHomeLocationE就可以直接在themes数组寻找对应的数组项并返回调用方;
  4. 在home.js中就需要实例化theme,并且调用getThemes()发送请求,
  5. 这个时候themes数组已经有数据了,那使用这个实例化的对象去调用getHomeLocationA和getHomeLocationE就能得到最终数据;

这里再去调用getHomeLocationA和getHomeLocationE就不会去发送请求了,这两个方法会自动去themes数组拿数据

但是这个也不是最终的方式,这只解决代码冗余这个问题,把实例化写到initAllData方法,在其他方法是不能使用的,所以需要把实例化直接写到onLoad这个生命周期函数上,这样其他的方法也可以一起调用;

pages\home\home.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// pages/home/home.js

import { Activity } from "../../model/activity";
import { Banner } from "../../model/banner";
import { Category } from "../../model/category";
import { Theme } from "../../model/theme";

Page({
/**
* 页面的初始数据
*/
data: {
themeA: null,
themeE: null,
themeF: null,
themeH: null,
bannerB: null,
grid: [],
activity: null,
},

/**
* 生命周期函数--监听页面加载
*/
async onLoad(options) {
// 页面加载的时候就初始化并请求一次数据获取主题的数组
this.theme = new Theme();
await this.theme.getThemes();
this.initAllData();
this.test();
},
// 初始化数据
async initAllData() {
// 获取banner数据
const bannerB = await Banner.getHomeLocationB();
//六宫格
const grid = await Category.getHomeLocationC();
//优惠券入口
const activity = await Activity.getHomeLocationD();
// 顶部图片,打折
const themeA = this.theme.getHomeLocationA();
// 每周上新
const themeE = this.theme.getHomeLocationE();
const themeF = this.theme.getHomeLocationF();
const themeH = this.theme.getHomeLocationH();
this.setData({
themeA,
themeE,
themeF,
themeH,
bannerB,
grid,
activity,
});
},
async test() {
// 顶部图片,打折
const themeA1 = this.theme.getHomeLocationA();
console.log(themeA1);
},

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {},

/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {},

/**
* 用户点击右上角分享
*/
onShareAppMessage() {},
});

主要改动:

  1. 统一Theme实例管理:在onLoad中只创建一次Theme实例并获取数据
  2. 消除冗余实例化initAllDatatest方法都使用同一个this.theme实例
  3. 避免重复HTTP请求getThemes()只在onLoad中调用一次
  4. 保持原有逻辑:其他功能保持不变

这样修改后,Theme实例只会创建一次,getThemes()方法也只会调用一次,完全消除了原来的代码冗余和重复HTTP请求问题。

第四次作业(SKU的概念和实现)

又是作业…..

  • SPU:Standard Product Unit,标准产品单元,可以理解为一个产品型号,比如上面图片看到的iPhone 14 (A2884) 就是一个标准的产品单元,它属于生产制造过程的一个标准品,标准品在缺乏具体规格信息的时候是不能直接售卖的(除非这个产品系列只有一个规格)。
  • SKU:Stock Keeping Unit,最小库存单元,也就是对应仓库中的一件商品,这个商品的规格信息在入库的时候就已经确定了的,因此是可以直接售卖的。
  • SPU 和 SKU 的关系:SPU 是一个相对抽象的概念,而SKU 是具象化的 SPU,也就是在 SPU 基础上添加了一个可售卖完整的规格信息,从而能够让顾客明确知道拿到手的商品是什么样。以服装为例,服装的一个款式是一个 SPU,只有加上了尺码、颜色后才能成为一个 SKU

scroll-view组件灵活使用

接下来继续完善每周上新模块,在上一节中,接口只是获取了每周上新的封面数据,接下来需要增加一个获取带有spu数据的每周上新,因为这个模块需要展示部分的spu,对于每周上新这个模块,我们单独抽象出一个组件。

  1. 新建spu-scroll组件
  2. 新建获取带有SPU的theme数据的接口

model\theme.js

1
2
3
4
5
6
7
8
9
10
// 获取带有SPU的theme数据
static async getThemesSpuByName(name) {
return await Http.request({
url: `theme/name/${name}/with_spu`,
});
}
// 获取带有SPU的theme数据;外部调用
static async getHomeLocationESpu() {
return Theme.getThemesSpuByName(Theme.LocationE);
}

pages\home\home.js

1
const themeESpuList = await Theme.getHomeLocationESpu();

是不是方法都需要加async和await

总结一句话:需要等待数据返回的时候就写async和await,不需要等待的时候就不加;

大白话:当你请求了接口数据,你是直接return回去的,没有后面需要写业务逻辑的,可以不写,后面有业务逻辑需要用到的,就需要写;

例子:像上面的获取带有SPU的theme数据的接口就可以优化成:

1
2
3
4
5
6
7
8
9
10
// 获取带有SPU的theme数据
static getThemesSpuByName(name) {
return Http.request({
url: `theme/name/${name}/with_spu`,
});
}
// 获取带有SPU的theme数据;外部调用
static getHomeLocationESpu() {
return Theme.getThemesSpuByName(Theme.LocationE);
}

把async和await去掉,因为他们本质上都是直接return回去数据,但是在homejs中就需要async和await接受数据,因为后面还需要setData;

同样的theme类中的getThemes方法为什么需要添加async和await,因为他 其实没有返回数据,还是赋值给了themes这个数组,所以需要加上。

接下来完善home的接口调用:

pages\home\home.js

1
2
3
4
5
6
7
8
9
10
// 每周上新
const themeE = this.theme.getHomeLocationE();
// 判断有没有每周上新下的商品,没有的话可以减少一次请求
let themeESpu = [];
if (themeE.online) {
const data = await Theme.getHomeLocationESpu();
if (data) {
themeESpu = data.spu_list.slice(0, 8);
}
}

这里先判断每周上新这个模块有没有商品数据,没有的话不请求接口数据,有的话,就只提取spu_list数组中8个数据

pages\home\home.wxml

1
2
3
4
5
<s-scroll
theme="{{themeE}}"
spuList="{{themeESpu}}"
wx:if="{{themeE.online}}"
></s-scroll>

第五次作业(SKU和SPU的实现思路)

又又是作业…..

一句玩笑话:我花钱了是来学习的,不是来写作业的,我懂了干嘛还需要学习…..

spu-scroll自定义组件1

上面已经请求到带有SPU的theme数据的接口的数据,接下来完成spu-scroll这个自定义组件的骨架:

首先需要子组件接受数据:

components\spu-scroll\index.js

1
2
3
4
properties: {
theme: Object,
spuList: Array,
},

components\spu-scroll\index.wxml

1
2
3
4
5
6
7
8
9
10
11
12
<view class="container">
<image src="{{theme.title_img}}" class="title"></image>
<scroll-view>
<block wx:for="{{spuList}}" wx:key="index">
<view>
<image src="{{item.img}}"></image>
<text>{{item.title}}</text>
</view>
</block>
</scroll-view>
</view>

components\spu-scroll\index.wxss

1
2
3
4
5
/* components/spu-scroll/index.wxss */
.title{
width: 694rpx;
height: 90rpx;
}

至此,后端数据可以正常显示到前端了;但是这里少了价格,由于价格这里涉及到很多种情况,所以这里直接使用LinUI的Price组件,下节课介绍使用

LinUI Price价格组件应用

引入l-price组件:app.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"pages": [
"pages/home/home"
],
"window": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "Weixin",
"navigationBarBackgroundColor": "#ffffff"
},
"style": "v2",
"componentFramework": "glass-easel",
"sitemapLocation": "sitemap.json",
"usingComponents": {
"l-grid": "/miniprogram_npm/lin-ui/grid/index",
"l-grid-item": "/miniprogram_npm/lin-ui/grid-item/index",
"l-price": "/miniprogram_npm/lin-ui/price/index"
},
"lazyCodeLoading": "requiredComponents"
}

使用:components\spu-scroll\index.wxml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<view class="container">
<image src="{{theme.title_img}}" class="title"></image>
<scroll-view>
<block wx:for="{{spuList}}" wx:key="index">
<view>
<image src="{{item.img}}"></image>
<l-price
color="#157658"
value="{{item.price}}"
l-unit-class="price-unit"
l-value-class="price-value"
></l-price>
<text>{{item.title}}</text>
</view>
</block>
</scroll-view>
</view>

外部样式类:components\spu-scroll\index.wxss

1
2
3
4
5
6
7
8
9
10
11
12
/* components/spu-scroll/index.wxss */
.title{
width: 694rpx;
height: 90rpx;
}
.price-unit{
font-size: 24rpx !important;
}
.price-value{
font-size: 48rpx !important;
font-weight: 800 !important;
}

小符号大价格的特点

周总结

无…..