小程序学习3 goods-card
pages/home/home
home.wxml
<goods-list
wr-class="goods-list-container"
goodsList="{{goodsList}}"
bind:click="goodListClickHandle"
bind:addcart="goodListAddCartHandle"
/>
<goods-list>是一个自定义组件,它具有以下属性和事件:
属性:
- wr-class:用于设置组件容器的样式类名。
- goodsList:用于传递商品列表数据给组件。
事件:
- click:当用户点击商品列表中的某个商品时触发该事件,可以通过绑定该事件来执行相应的处理函数。
- addcart:当用户点击商品列表中的添加购物车按钮时触发该事件,可以通过绑定该事件来执行相应的处理函数。
可以根据需要设置wr-class属性来自定义组件的样式,同时通过goodsList属性传递商品列表数据给组件。另外,你还可以绑定click事件和addcart事件来处理用户的点击操作。
home.json
"usingComponents": {
"goods-list": "/components/goods-list/index",
}
home.js
import { fetchHome } from '../../services/home/home';
import { fetchGoodsList } from '../../services/good/fetchGoods';
import Toast from 'tdesign-miniprogram/toast/index';
Page({
data: {
imgSrcs: [],
tabList: [],
goodsList: [],
goodsListLoadStatus: 0,
pageLoading: false,
current: 1,
autoplay: true,
duration: '500',
interval: 5000,
navigation: { type: 'dots' },
swiperImageProps: { mode: 'scaleToFill' },
},
goodListPagination: {
index: 0,
num: 10,
},
privateData: {
tabIndex: 0,
},
onShow() {
this.getTabBar().init();
},
onLoad() {
this.init();
},
onReachBottom() {
if (this.data.goodsListLoadStatus === 0) {
this.loadGoodsList();
}
},
onPullDownRefresh() {
this.init();
},
init() {
this.loadHomePage();
},
loadHomePage() {
wx.stopPullDownRefresh();
this.setData({
pageLoading: true,
});
fetchHome().then(({ swiper, tabList }) => {
this.setData({
tabList,
imgSrcs: swiper,
pageLoading: false,
});
this.loadGoodsList(true);
});
},
tabChangeHandle(e) {
this.privateData.tabIndex = e.detail;
this.loadGoodsList(true);
},
onReTry() {
this.loadGoodsList();
},
async loadGoodsList(fresh = false) {
if (fresh) {
wx.pageScrollTo({
scrollTop: 0,
});
}
this.setData({ goodsListLoadStatus: 1 });
const pageSize = this.goodListPagination.num;
let pageIndex = this.privateData.tabIndex * pageSize + this.goodListPagination.index + 1;
if (fresh) {
pageIndex = 0;
}
try {
const nextList = await fetchGoodsList(pageIndex, pageSize);
this.setData({
goodsList: fresh ? nextList : this.data.goodsList.concat(nextList),
goodsListLoadStatus: 0,
});
this.goodListPagination.index = pageIndex;
this.goodListPagination.num = pageSize;
} catch (err) {
this.setData({ goodsListLoadStatus: 3 });
}
},
goodListClickHandle(e) {
const { index } = e.detail;
const { spuId } = this.data.goodsList[index];
wx.navigateTo({
url: `/pages/goods/details/index?spuId=${spuId}`,
});
},
goodListAddCartHandle() {
Toast({
context: this,
selector: '#t-toast',
message: '点击加入购物车',
});
},
navToSearchPage() {
wx.navigateTo({ url: '/pages/goods/search/index' });
},
navToActivityDetail({ detail }) {
const { index: promotionID = 0 } = detail || {};
wx.navigateTo({
url: `/pages/promotion-detail/index?promotion_id=${promotionID}`,
});
},
});
解析:async loadGoodsList(fresh = false) {
说白了,这段儿代码就是鼠标滚轮往下拉,商品列表就刷刷刷的往外刷
这段代码是一个异步函数loadGoodsList
,它接受一个参数fresh
,默认为false
。函数的作用是加载商品列表。
首先,如果fresh
为true
,则调用wx.pageScrollTo
函数将页面滚动到顶部。
然后,通过调用setData
方法将goodsListLoadStatus
设置为1,表示正在加载商品列表。
接下来,根据当前的页码和每页的数量计算出要请求的页码。如果fresh
为true
,则将页码设置为0。
然后,使用fetchGoodsList
函数异步获取商品列表。获取到列表后,通过调用setData
方法将goodsList
更新为新的列表。如果是刷新操作,则直接使用新的列表;如果是加载更多操作,则将新的列表与原有列表合并。同时,将goodsListLoadStatus
设置为0,表示加载完成。
最后,更新分页信息,将页码和每页数量保存到goodListPagination
对象中。
如果在获取商品列表过程中发生错误,则通过调用setData
方法将goodsListLoadStatus
设置为3,表示加载失败。
goodListAddCartHandle() { }
goodListAddCartHandle() {
Toast({
context: this,
selector: '#t-toast',
message: '点击加入购物车',
});
},
TDesign Toast 轻提示
用于轻量级反馈或提示,不会打断用户操作。
goodListAddCartHandle()是一个函数,用于处理点击加入购物车的操作。在函数内部,它会调用Toast组件来显示一个提示消息,提示用户已成功将商品加入购物车。
在函数中,Toast组件的参数包括:
- context:表示上下文,即函数所在的环境或组件。
- selector:表示选择器,用于指定要显示提示消息的位置。
- message:表示要显示的提示消息内容,这里是"点击加入购物车"。
这样,当用户点击加入购物车时,函数会调用Toast组件来显示提示消息。
components/goods-list
index.wxml
<view class="goods-list-wrap wr-class" id="{{independentID}}">
<block wx:for="{{goodsList}}" wx:for-item="item" wx:key="index">
<goods-card
id="{{independentID}}-gd-{{index}}"
data="{{item}}"
currency="{{item.currency || '¥'}}"
thresholds="{{thresholds}}"
class="goods-card-inside"
data-index="{{index}}"
bind:thumb="onClickGoodsThumb"
bind:click="onClickGoods"
bind:add-cart="onAddCart"
/>
</block>
</view>
index.json
{
"component": true,
"usingComponents": {
"goods-card": "/components/goods-card/index"
}
}
components/goods-card
index.wxml
<view
id="{{independentID}}"
class="goods-card"
bind:tap="clickHandle"
data-goods="{{ goods }}"
>
<view class="goods-card__main">
<view class="goods-card__thumb" bind:tap="clickThumbHandle">
<t-image
wx:if="{{ !!goods.thumb }}"
t-class="goods-card__img"
src="{{ goods.thumb }}"
mode="aspectFill"
lazy-load
/>
</view>
<view class="goods-card__body">
<view class="goods-card__upper">
<view wx:if="{{ goods.title }}" class="goods-card__title">
{{ goods.title }}
</view>
<view wx:if="{{ goods.tags && !!goods.tags.length }}" class="goods-card__tags">
<view
wx:for="{{ goods.tags }}"
wx:key="index"
wx:for-item="tag"
class="goods-card__tag"
data-index="{{index}}"
>
{{tag}}
</view>
</view>
</view>
<view class="goods-card__down">
<price
wx:if="{{ goods.price }}"
wr-class="spec-for-price"
symbol-class="spec-for-symbol"
symbol="{{currency}}"
price="{{goods.price}}"
/>
<price
wx:if="{{ goods.originPrice && isValidityLinePrice }}"
wr-class="goods-card__origin-price"
symbol="{{currency}}"
price="{{goods.originPrice}}"
type="delthrough"
/>
<t-icon
class="goods-card__add-cart"
prefix="wr"
name="cartAdd"
id="{{independentID}}-cart"
data-id="{{independentID}}"
catchtap="addCartHandle"
size="48rpx"
color="#FA550F"
/>
</view>
</view>
</view>
</view>
<view class="goods-card__main">
TDesign mode为 裁切
lazy-load
懒加载(Lazy Load)是一种延迟加载的策略,它在编程中常用于优化系统性能和资源利用。懒加载的核心思想是将对象的创建或数据的加载推迟到真正需要的时候再进行,而不是在初始化阶段就立即进行。
懒加载的优点是可以减少系统启动时间和内存占用,特别适用于大型系统或者需要加载大量资源的场景。通过懒加载,可以避免不必要的资源浪费,提高系统的响应速度和效率。
在软件开发中,懒加载可以应用于多个方面,比如:
-
对象的懒加载:当一个对象在程序中被创建时,并不立即初始化其成员变量或关联对象,而是在真正需要使用时才进行初始化。这样可以避免不必要的对象创建和资源消耗。
-
数据库查询的懒加载:在使用ORM(对象关系映射)框架进行数据库操作时,可以延迟加载关联对象的数据。只有当访问关联对象时才会触发实际的数据库查询操作,从而减少数据库访问次数和提高查询效率。
-
图片或文件的懒加载:在网页或移动应用中,可以延迟加载图片或文件资源。当用户滚动到可见区域时,再进行实际的资源加载,避免一次性加载大量资源导致页面卡顿或流量浪费。
index.json
{
"component": true,
"usingComponents": {
"price": "/components/price/index",
"t-icon": "tdesign-miniprogram/icon/icon",
"t-image": "/components/webp-image/index"
}
}
index.js
Component({
options: {
addGlobalClass: true,
},
properties: {
id: {
type: String,
value: '',
observer(id) {
this.genIndependentID(id);
if (this.properties.thresholds?.length) {
this.createIntersectionObserverHandle();
}
},
},
data: {
type: Object,
observer(data) {
if (!data) {
return;
}
let isValidityLinePrice = true;
if (data.originPrice && data.price && data.originPrice < data.price) {
isValidityLinePrice = false;
}
this.setData({ goods: data, isValidityLinePrice });
},
},
currency: {
type: String,
value: '¥',
},
thresholds: {
type: Array,
value: [],
observer(thresholds) {
if (thresholds && thresholds.length) {
this.createIntersectionObserverHandle();
} else {
this.clearIntersectionObserverHandle();
}
},
},
},
data: {
independentID: '',
goods: { id: '' },
isValidityLinePrice: false,
},
lifetimes: {
ready() {
this.init();
},
detached() {
this.clear();
},
},
pageLifeTimes: {},
methods: {
clickHandle() {
this.triggerEvent('click', { goods: this.data.goods });
},
clickThumbHandle() {
this.triggerEvent('thumb', { goods: this.data.goods });
},
addCartHandle(e) {
const { id } = e.currentTarget;
const { id: cardID } = e.currentTarget.dataset;
this.triggerEvent('add-cart', {
...e.detail,
id,
cardID,
goods: this.data.goods,
});
},
genIndependentID(id) {
let independentID;
if (id) {
independentID = id;
} else {
independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`;
}
this.setData({ independentID });
},
init() {
const { thresholds, id } = this.properties;
this.genIndependentID(id);
if (thresholds && thresholds.length) {
this.createIntersectionObserverHandle();
}
},
clear() {
this.clearIntersectionObserverHandle();
},
intersectionObserverContext: null,
createIntersectionObserverHandle() {
if (this.intersectionObserverContext || !this.data.independentID) {
return;
}
this.intersectionObserverContext = this.createIntersectionObserver({
thresholds: this.properties.thresholds,
}).relativeToViewport();
this.intersectionObserverContext.observe(
`#${this.data.independentID}`,
(res) => {
this.intersectionObserverCB(res);
},
);
},
intersectionObserverCB() {
this.triggerEvent('ob', {
goods: this.data.goods,
context: this.intersectionObserverContext,
});
},
clearIntersectionObserverHandle() {
if (this.intersectionObserverContext) {
try {
this.intersectionObserverContext.disconnect();
} catch (e) {}
this.intersectionObserverContext = null;
}
},
},
});
properties
data
data: {
type: Object,
observer(data) {
if (!data) {
return;
}
let isValidityLinePrice = true;
if (data.originPrice && data.price && data.originPrice < data.price) {
isValidityLinePrice = false;
}
this.setData({ goods: data, isValidityLinePrice });
},
},
这段代码是一个小程序中的一个数据观察器(observer用于监听一个名为"data"的对象类型数据的变化。当"data"对象发生变化时,触发observer函数进行处理。
在observer函数中,首先判断"data"是否存在,如果不存在则直接返回。接着,通过比较"data"对象中的"originPrice"和"price"属性的值,判断是否满足某个条件(即"originPrice"小于"price"),并将结果保存在名为"isValidityLinePrice"的变量中。
最后,通过调用小程序的setData方法,将"data"对象以及"isValidityLinePrice"变量的值更新到小程序的数据中。
thresholds: {
thresholds是一个属性,它是一个数组类型,初始值为空数组。在该属性的观察器(observer)中,当thresholds发生变化时,会执行相应的操作。
在这段代码中,当thresholds存在且长度大于0时,会调用createIntersectionObserverHandle()方法;否则,会调用clearIntersectionObserverHandle()方法。
createIntersectionObserverHandle是一个用于创建IntersectionObserver实例的函数。IntersectionObserver一个用于观察目标元素与其祖先元素或视窗交叉状态的API。通过使用IntersectionObserver,我们可以监听目标元素进入或离开视窗,或者与其祖先元素交叉的情况。
createIntersectionObserverHandle函数的作用是创建一个IntersectionObserver实例,并返回一个用于控制观察行为的句柄。通过这个句柄,我们可以对观察行为进行控制,例如开始观察、停止观察、设置观察回调等。
createIntersectionObserverHandle
使用createIntersectionObserverHandle函数的一般步骤如下:
- 调用createIntersectionObserverHandle函数创建一个IntersectionObserver实例的句柄。
- 通过句柄调用observe方法,指定要观察的目标元素。
- 设置观察回调函数,当目标元素与其祖先元素或视窗交叉状态发生变化时,会触发该回调函数。
- 可选地,通过句柄调用unobserve方法停止对目标元素的观察。
IntersectionObserver
IntersectionObserver是一个用于监测元素是否进入或离开视口的API。它可以帮助我们实现懒加载、无限滚动、可见性检测等功能。
IntersectionObserver的基本原理是通过创建一个观察器(IntersectionObserver对象),然后指定要观察的目标元素和一些配置选项。当目标元素进入或离开视口时,观察器会触发回调函数,我们可以在回调函数中执行相应的操作。
以下是IntersectionObserver的一些常用配置选项:
- root:指定根元素,即观察器所在的容器,默认为浏览器视口。
- rootMargin:指定根元素的边界,可以用来扩大或缩小触发回调的范围。
- threshold:指定目标元素可见性的阈值,可以是一个或多个0到1之间的值。
使用IntersectionObserver的基本步骤如下:
- 创建一个IntersectionObserver对象,并传入一个回调函数。
- 使用observe()方法指定要观察的目标元素。
- 在回调函数中处理目标元素进入或离开视口的情况。
methods:{
clickHandle() {
clickHandle() {
this.triggerEvent('click', { goods: this.data.goods });
},
clickHandle()是一个函数,它的作用是触发一个名为’click’的事件,并传递一个包含商品信息的对象{ goods: this.data.goods }作为参数。这个函数通常用于处理点击事件,并将商品信息传递给其他组件或模块进行处理。