上一教程,我们通过讲解组件监听事件,把组件的数组数据转换成对象,最后直接与wxml绑定,更方便快捷。
利用lin-ui的WaterFlow组件,使用小程序的抽象节点
抽象节点与插槽的区别
- 抽象节点:需要调用者把“自定义组件传入”;优点:灵活性高
- 插槽:只需要调用者把数据传入;优点:易用性极高
菜单
- 1、在app.json全局引入lin-ui的water-flow组件
- 2、在home.wxml中插入lin-ui组件
- 3、准备瀑布流数据,在utils中创建paging.js
- 4、在model中创建spu-paging.js
- 5、在home.js调用spu.js中的函数
- 6、创建自定义组件“spu-preview”
- 6.1因为瀑布流会使用到加载提示控件,所以需要引入lin-ui的“ loadmore ”组件
- 6.2 编写组件的主体框架,index.wxml
- 6.3 处理tags问题,把文本转换成对象,修改index.js
- 6.4、增加组件的样式,修改组件的index.wxss
- 6.5 修改home.wxml,引入loadmore组件
1、在app.json全局引入lin-ui的water-flow组件
{
"pages": [
"pages/home/home"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black"
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"usingComponents": {
"l-grid2":"/miniprogram_npm/lin-ui/grid/index",
"l-price":"/miniprogram_npm/lin-ui/price/index",
"l-water-flow":"/miniprogram_npm/lin-ui/water-flow/index"
}
}
2、在home.wxml中插入lin-ui组件
<!--pages/home/home.wxml-->
<span>{{WebTitle}}</span>
<view>
<image class="top-theme" src="{{topTheme.entrance_img}}" ></image>
<swiper class="swiper"
indicator-dots="{{true}}"
indicator-active-color="#157658"
autoplay="{{true}}"
circular="{{true}}"
>
<block wx:for="{{bannerB.items}}">
<swiper-item>
<image class="swiper" src="{{item.img}}"></image>>
</swiper-item>
</block>
</swiper>
<s-category-grid grid="{{grid}}"></s-category-grid>
<image class="activityD" src="{{activityD.entrance_img}}"></image>
<s-spu-scroll
m-scroll-class="spu-scroll"
theme="{{themeE}}"
spu-list="{{themeESpu}}"
wx:if="{{topTheme.online}}">
</s-spu-scroll>
<image src="{{themeF.entrance_img}}" class="quality"></image>
<s-hot-list banner="{{bannerG}}"></s-hot-list>
<image src="{{themeH.entrance_img}}" class="fashion"></image>
<view class="spu-bottom">
<!-- 抽象节点引入的自定义组件名 spu-preview -->
<l-water-flow generic:l-water-flow-itme="spu-preview"></l-water-flow>
</view>
</view>
抽象节点引入的自定义组件名:spu-preview
因为抽象节点需要调用者把“自定义组件:spu-preview”传入,所以也需要定义新的自定义组件“spu-preview”
3、准备瀑布流数据,在utils中创建paging.js
paging.js需要处理哪些逻辑呢?分页数据
- 没有任何数据,为空的情况
- 最后一页时,没有下一页;没有更多数据
- 累加。因为小程序的setData机制,所以需要从[1,N]
- 分页数据的状态:a.正在加载 b.加载完成 c.没有更多数据 d.空
- 上划页面触底加载时避免用户重复请求,对数据库压力,应对方法:a.redis 数据锁
- 例如按钮button 防抖和截流。
防抖和截流常用场景:
- button的禁用,点击后禁止继续点击
- 倒计时无法点击
- 模态,整页加入loading页
import {Http} from "./http";
class Paging{
strat
count
req
locaker=false
url
moreData=true
accumulator=[]
/** 定义参数 */
constructor(req,count=10,start=0){
this.start=start
this.count=count
this.req=req
this.url=req.url
}
/** 获取更新的数据 */
async getMoreDate(){
//获取数据的逻辑:1、判断是否有锁,没锁的话 加锁然后request查询数据,最后解锁
//getLocker
//request
//releaseLocker
if(!this.moreData){
return
}
if(!this._getLocker){
return
}
const data = await this._actualGetData()
this._releaseLocker();
return data
}
/** 跑接口 根据不同的结果,返回不同对象
*
*/
async _actualGetData(){
const req = this._getCurrentReq()
let paging = await Http.request(req)
if(!paging){
return null
}
/** 没有数据,返回空 */
if(paging.total===0){
return {
empty:true,
items:[],
moreData:false,
accumulator:[]
}
}
/** 判断是否存在更多数据 */
this.moreData = Paging._moreData(paging.total_page,paging.page)
if(this.moreData){
this.start +=this.count
}
this._accumulate(paging.items)
return{
empty:false,
items:paging.items,
moreData:this.moreData,
accumulator:this.accumulator
}
}
/** 合并item */
_accumulate(items){
this.accumulator = this.accumulator.concat(items)
}
/** 获取是否有更多内容 */
static _moreData(totalPage,pageNum){
return pageNum<totalPage-1
}
/** 获取当前的url */
_getCurrentReq(){
let url =this.url;
const params = `start=${this.start}&count=${this.count}`;
if(url.includes('?')){
url +='&'+params
}else{
url +='?'+params
}
this.req.url=url
return this.req
}
/* 获取当前锁的状态 */
_getLocker(){
if(this.locker){
return false;
}
this.locker=true
return true
}
/** 解锁 */
_releaseLocker(){
this.locker=false
}
}
export{
Paging
}
4、在model中创建spu-paging.js
在spu-paging.js中实例化并返回,在home.js中调用该类的对象
import {Http} from "../../utils/http"
import { Paging } from "../../utils/paging"
class SpuPaging{
static getLatestPaging(){
/** 实例化,传入url, */
return new Paging(
{url:`spu/latest`},3
)
}
}
export{
SpuPaging
}
5、在home.js调用spu.js中的函数
瀑布流使用到触发底部事件:onReachBottom
// pages/home/home.js
import { Banner } from "../model/banner.js";
import { category } from "../model/category.js";
import { Theme } from "../model/theme.js"
import { Activity } from "../model/activity.js"
import { SpuPaging } from "../model/spu-paging.js";
Page({
/**
* 页面的初始数据
*/
data: {
WebTitle:"Fox_Test",
topTheme: null,
themeE:null,
bannerB:null,
grid:[],
activity:null,
themeESpu:null,
paging:null,
loadtype:"loading",
loadObj:{
"loadingType":"loading",
"endtext":null
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad : async function(){
this.initAllDate();
this.initBottomSpuList();
},
/**定义获取瀑布流元素事件 */
initBottomSpuList:async function(){
/** SouPaging已经在spu-paging.js实例化了 直接可以调用对象 */
const paging = SpuPaging.getLatestPaging()
this.data.paging = paging;
const data = await paging.getMoreDate()
console.log(data);
if(!data){
return
}
wx.lin.renderWaterFlow(data.items)
},
initAllDate: async function(){
const themes = new Theme();
await themes.GetThemes();
const themeA = await themes.getHomelocationA();
const themeE = await themes.getHomelocationE();
const themeF = await themes.getHomelocationF();
let themeESpu = [];
if(themeE.online){
const data = await Theme.getHomeLocationESpu();
if(data){
/*截取数组0~8个 */
themeESpu = data.spu_list.slice(0,8);
}
}
const bannerB = await Banner.getHomeLocationB();
const grid = await category.getGridCategory();
const activityD =await Activity.getHomeLocationD();
const bannerG = await Banner.getHomeLocationG();
const themeH = await themes.getHomelocationH();
this.setData({
activityD:activityD,
topTheme:themeA,
themeE:themeE,
bannerB:bannerB,
grid:grid,
themeESpu:themeESpu,
themeF:themeF,
bannerG:bannerG,
themeH:themeH
})
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: async function () {
/* getMoreDate*/
const data = await this.data.paging.getMoreDate();
if(!data){
return
}
wx.lin.renderWaterFlow(data.items)
if(!data.moreData){
this.setData({
loadObj:{
"loadingType":'end',
"end-text":"没更多内容了,亲"
},
loadtype:'end'
// loadingType:loadingType
})
}
},
})
6、创建自定义组件“spu-preview”
6.1因为瀑布流会使用到加载提示控件,所以需要引入lin-ui的“ loadmore ”组件
在app.json中引入lin-ui的loadmore组件和自定义spu-preview组件
{
"pages": [
"pages/home/home"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black",
"onReachBottomDistance": 50
},
"style": "v2",
"sitemapLocation": "sitemap.json",
"usingComponents": {
"l-grid2":"/miniprogram_npm/lin-ui/grid/index",
"l-price":"/miniprogram_npm/lin-ui/price/index",
"l-water-flow":"/miniprogram_npm/lin-ui/water-flow/index",
"l-tag":"/miniprogram_npm/lin-ui/tag/index",
"l-loadmore":"/miniprogram_npm/lin-ui/loadmore/index",
"m-spu-preview":"/components/spu-preview/index"
}
}
6.2 编写组件的主体框架,index.wxml
<!--考虑到原价和折扣价的复用性,引入处理价格的wxs-->
<wxs src="../../wxs/price.wxs" module="p"></wxs>
<view class="container">
<!-- 设置高度自适应方法1 使用小程序官方的image方法-->
<!--
<image mode="widthFix" class="img" src="{{data.img}}" ></image>
-->
<!--方法二:动态计算宽高比 onImgLoad在组件的index.js的methods定义,切勿忘记后面的rpx -->
<image bing:load="onImgLoad" style="width:{{w}}rpx;height:{{h}}rpx" class="img" src="{{data.img}}"></image>
<view class="content-container">
<text class="tittle">{{data.tittle}}</text>
<view class="tags">
<!--因为tags不是每个都有,所以需要在index.js处理-->
<block wx:for="{{tags}}">
<!-- 注意此处使用了lin-ui的l-tag所以需要在APP.json或home.json引入组件-->
<l-tag type="reading" class="m-tag" size="super-mini">{{item}}</l-tag>
</block>
</view>
<view class="price-row">
<!--原价和折扣价 考虑到复用性,所以使用wxs对价格进行处理-->
<l-price
color:='#157658'
value-size='28'
unit-size='20'
autofix
value="{{p.mainPrice(data.price,data.discount_price)}}"></l-price>
<l-price l-class="discount-price" wx:if="{{data.discount_price?true:false}}"
deleted
color='#999999'
size="26"
value="{{p.slashedPrice(data.price,data.discount_price)}}"></l-price>
</view>
<text class="subtitle">{{data.subtitle}}</text>
</view>
</view>
// components/spu-preview/index.js
Component({
/**
* 组件的属性列表
*/
externalClasses:['m-tag'],
properties: {
data:Object
},
/**
* 组件的初始数据
*/
data: {
tags:Array
},
/** 数据处理,在tag转换成数据 */
observers:{
data:function(data){
if(!data){
return
}
if(!data.tags){
return
}
const tags = data.tags.split('$')
this.setData({
tags:tags
})
}
},
/**
* 组件的方法列表
*/
methods: {
/** 瀑布流重算高度 */
onImgLoad(event){
const {width,hegiht}=event.detail
this.setData({
w:340,
h:340*height/width
})
}
}
})
6.4、增加组件的样式,修改组件的index.wxss
/* components/spupreview/index.wxss */
.container{
width: 340rpx;
display: flex;
flex-direction: column;
box-shadow: 0px 0px 8px 0px rgba(119,163,149,0.2);
margin-bottom: 30rpx;
background-color: #ffffff;
}
.content-container{
display:flex;
flex-direction: column;
}
.tittle{
font-size: 28rpx;
columns: #333333;
}
.img{
width: 100%;
height:360rpx;
}
.tags{
display: flex;
flex-direction: row;
margin-bottom: 8rpx;
margin-top: 6rpx;
flex-wrap:wrap;
}
.subtitle{
font-size: 24rpx;
font-weight: 300;
margin-top:6rpx;
color:#888
}
.price-row{
display:flex;
flex-direction: row;
}
.discount-price{
margin-left:20rpx;
}
.m-tag{
background-color:#DCEBE6 !important;
color:#157658 !important;
padding-left:30rpx !important;
padding-right: 30rpx !important;
height: 80rpx !important;
margin-right: 5rpx !important;
}
6.5 修改home.wxml,引入loadmore组件
<!--考虑到原价和折扣价的复用性,引入处理价格的wxs-->
<wxs src="../../wxs/price.wxs" module="p"></wxs>
<view class="container">
<!-- 设置高度自适应方法1 使用小程序官方的image方法-->
<!--
<image mode="widthFix" class="img" src="{{data.img}}" ></image>
-->
<!--方法二:动态计算宽高比 onImgLoad在组件的index.js的methods定义,切勿忘记后面的rpx -->
<image bing:load="onImgLoad" style="width:{{w}}rpx;height:{{h}}rpx" class="img" src="{{data.img}}"></image>
<view class="content-container">
<text class="tittle">{{data.tittle}}</text>
<view class="tags">
<!--因为tags不是每个都有,所以需要在index.js处理-->
<block wx:for="{{tags}}">
<!-- 注意此处使用了lin-ui的l-tag所以需要在APP.json或home.json引入组件-->
<l-tag type="reading" class="m-tag" size="super-mini">{{item}}</l-tag>
</block>
</view>
<view class="price-row">
<!--原价和折扣价 考虑到复用性,所以使用wxs对价格进行处理-->
<l-price
color:='#157658'
value-size='28'
unit-size='20'
autofix
value="{{p.mainPrice(data.price,data.discount_price)}}"></l-price>
<l-price l-class="discount-price" wx:if="{{data.discount_price?true:false}}"
deleted
color='#999999'
size="26"
value="{{p.slashedPrice(data.price,data.discount_price)}}"></l-price>
</view>
<text class="subtitle">{{data.subtitle}}</text>
</view>
</view>
功能大功告成!!!!
下一节,我们将讲解关于组件的传参教程。