总篇66篇 2019年 第40篇
众所周知,小程序是在腾讯云端存放,用户无需加载安装,通过扫一扫或者搜一下即可打开应用,能做出很多H5做不到的功能,开发成本和H5差不多,相对于APP开发成本要低,已经成为超级APP们角逐的焦点,汽车之家在这样的背景下推出了自己“小程序”,之家小程序采用自由组装组件的方式,为用户提供全面的「内容+数据+服务」的封装。言归正传本文将以之家车系列表为例,聊一聊如何之家小程序如何实现常见仿通讯录A-Z排序功能,获取数据首字母,仿造联系人实现A-Z字母排序,实现字母索引定位功 能,监听字母滚动以及连续触摸滑动到指定字母位置。 一、效果演示 二、自定义组件封装提取
为了该功能在项目中重复使用方便,我们将改功能封装成自己的组件,在根目录上建立一个components的文件夹,在里面自定义一个文件如:letterSortPicker
配置文件index.json
{
"component": true// 是否使用组件
}
页面布局index.wxml
<viewclass='brand_box'>
<scroll-viewclass='brand_left'scroll-y="true"style='width:100%;height:{{cHeight}}px;'bindscroll="scrollLeft"enable-back-to-topscroll-with-animationscroll-into-view='{{toView}}'>
<blockwx:for='{{cityList}}'wx:key='this'wx:for-item='letterItem'wx:for-index='letterIndex'>
<viewclass='brand_letter_box'id="{{'index'+letterItem.first}}">
<viewclass='brand_first_letter'>{{letterItem.first}}view>
<viewclass="car-item"wx:for='{{letterItem.data}}'wx:key='this'data-types='list'catchtap='brandTap'data-index='{{index}}'data-id='{{item.id}}'>
<viewclass="icon-box">
...
<imageclass='icon'wx:if='{{item.logo}}'src='{{item.logo}}'>image>
view>
<viewclass='brand_name'>{{item.name}}view>
view>
view>
block>
scroll-view>
<viewclass='brand_right'catchtouchstart="clickRight"catchtouchmove="handlerMove"catchtouchend="clearLetterShow">
<viewwx:for='{{letterList}}'wx:key="{{index}}"class="letter_item{{activeId==index?'on':''}}"id='{{item}}'data-id="{{index}}">{{item}}view>
view>
<viewclass="show-letter"wx:if="{{isLetterShow}}">{{letter}}view>
view>
页面为动态渲染,后台提供api(前后端约定好接口格式),服务器返回的数据格式为:
这样,我们遍历数据list就能动态渲染出车品牌列表,同时遍历letter也能将右侧字母列表动态渲染出来。布局后的页面为:
父组件引入letterSortPicker子组件
{
"navigationBarBackgroundColor": "#bf2727",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "选择品牌",
"usingComponents": {
"letterSortPicker": "/component/letterSortPicker/index"
}
}
Pages/index文件下的index.wxml
标签为json中自定义的属性名
bind:brandTap的点击事件为letterSortPicker传递过来的点击事件名(brandTap)绑定的brandTap要和子组件一样
<viewclass="container">
<letterSortPicker
brand_box="brand_box"
id='letterSortPicker'
cityList="{{cityList}}"
letterList="{{letterList}}"
bind:error="_error"
bind:success="_success">
letterSortPicker>
<imageclass="page-bg"src="../../images/sbg.jpg"mode="scaleToFill">image>
view>
Pages/index文件下的index.js
import*as Api from'../../utils/server'
//获取应用实例
const app =getApp()
Page({
data: {
cityList: {},
letterList: {},
},
onLoad: function (options) {
this.queryBrand()
},
onReady: function () {
//获得letterSortPicker组件
this.letterSortPicker = this.selectComponent("#letterSortPicker");
},
queryBrand() {
let that = this
wx.showLoading({
title: '智能匹配中...',
mask: true
})
Api.queryBrand((res)=>{
if(res.data.code == 0) {
that.setData({
cityList: res.data.data.list,
letterList: res.data.data.letter
})
wx.hideLoading()
}
})
},
})
三、选择及连续触摸选择滚动到指定字母位置
小程序提供了可滚动视图区域组件scroll-view,可以很轻松快速的实现定位效果。首先只需在父层scroll-view组件设置一下几个属性:
Scroll-y:设置为true,允许纵向滚动;
Scroll-into-view:值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素;
Scroll-with-animation:设置为true,在设置滚动条位置时使用动画过渡;
<scroll-view class='brand_left'scroll-y="true"scroll-with-animationscroll-into-
view='{{选中的子元素id}}'>
<blockwx:for='{{品牌车系列表}}'wx:key='this'>
<viewclass='brand_letter_box'id="{{子元素id}}">
...
view>
block>
scroll-view>
然后实现选中或者连续触摸(按压不松手)动态生成(接口返回)字母排序列表区域效果,这里需要结合使用小程序touchstart、touchmove、touchend,使用catch事件绑定可以阻止冒泡事件和向上冒泡。<viewclass='brand_right'catchtouchstart="clickRight"catchtouchmove="handlerMove"
catchtouchend="clearLetterShow">
<viewwx:for='{{letterList}}'wx:key="{{index}}"class="letter_item{{activeId==index?'on':''}}"id='{{item}}'data-id="{{index}}">{{item}}view>
view>
触摸开始clickRight方法:
clickRight: function (e) {
let letter = e.target.id
let i =e.target.dataset.id
this.setData({
isLetterShow: true,
activeId: i,
letter: letter,
toView: 'index' + letter
})
},
连续触摸(按压不松手)handermove方法
handlerMove(e){
let that = this
let { cityList } = this.data;
let moveY = e.touches[0].clientY;
let rY = moveY - this.offsetTop;
if (rY >= 0) {
let index = (rY<= this.apHeight) ? 0 : Math.ceil((rY - this.apHeight) /this.apHeight)
if (0 <= index< cityList.length) {
let nonwAp =cityList[index];
if (index !=that.data.activeId) {
nonwAp && this.setData({
isLetterShow: true,
letter: nonwAp.first,
activeId: index,
toView: 'index' +nonwAp.first
});
}
}
}
},
松开手指clearLetterShow方法
clearLetterShow(){
this.setData({
isLetterShow: false,
})
},
四、页面滚动定位可视区域对应首字母
首先,计算同首字母品牌的子元素所占高度getItemHeight方法,.car-item为单个品牌所占高度,则同首字母品牌所占高度为字母所占高度+同字母品牌个数*单个品牌所占高度,具体代码为:// 计算列表item高度
getItemHeight(list) {
var query = this.createSelectorQuery();
query.select('.car-item').boundingClientRect()
query.select('.brand_first_letter').boundingClientRect()
query.exec((res) => {
var itemHeight= res[0].height;
varletterHeight = res[1].height;
let itemArr =list.map(v => v.data.length * itemHeight + letterHeight)
let arr = []
itemArr.map((vi, ci) => {
if (ci == 0) {
arr.push(vi)
} else {
arr.push(itemArr[ci] + arr[ci - 1])
}
})
this.setData({
itemArr: arr
})
})
}
}
依次叠加前一项子元素的高度,得到所有子元素所在页面位置数组,该数组如下:
然后在父层增加滚动方法srcollLeft方法,将当前滚动位置e.detail.scrollTop与每个子元素所在位置进行比较,得到所在位置的首字母,并设置选中scrollLeft: function (e) {
let that = this
let { itemArr, activeId,activeScrollTop, cHeight, cityList } = this.data;
itemArr.every((c, ci) => {
if(e.detail.scrollTop < c) {
if (ci != this.data.activeId){
let nonwAp =cityList[ci];
nonwAp && that.setData({
activeId: ci
})
return
}
} //false 停止遍历
else {
returntrue//true 继续遍历
}
})
},
五、性能优化
1. 减少setData开销:每调用一次setData, 都是逻辑层向渲染层的一次通讯,这个通信还不是直接传给webView, 而是通过走了native层,通讯的开销很大;
渲染层收到通讯后,还需要重新渲染出来,所以,一次setData带来两次开销:通信的开销 + webview更新的开销;
如果一个数据不会影响渲染层,则不必放在setData里;
用到bindScroll事件,也是一次通讯,是webview层向js逻辑层的通讯,这次通讯的开销比较大,如果回调里有setData,那性能就会很差,一定要在回调里增加条件判断来禁止不必要的setData;
压缩代码,清理冗余代码;
图片放在cdn;
采用分包策略-分包预加载和独立分包;
onload的阶段就可以向服务器发起请求,不用等ready;
请求接口数据可以用storage、globalData放到缓存里,减少请求时间等待;
请求中可以先展示骨架图,缩短视觉等待时间;
自定义组件的更新只在组件内部进行,不受页面其他不能分内容的影响;
各个组件也将具有各自独立的逻辑空间。每个组件都分别拥有自己的独立的数据、setData调用;