Bootstrap

2.10 滚动选择器:如何自定义省市区多级联动选择器?(一)

视频链接:

2.10 滚动选择器:如何自定义省市区多级联动选择器?(一)

片 1

 

上节课我们学习了 scroll-view 组件,以及如何渲染一个长列表,这节课我们开始学习 picker、picker-view、picker-view-column 这三个组件,以及如何基于 picker-view 实现一个省市区三级联动的多项选择器。

 

有人说,picker 本身有一个模式是 region,就是省市级三级联动的,为什么还要多此一举,再自定义实现一个呢?

 

的确官方组件 picker,有一个 mode 等于 region 的时候,就是省市区三级联动的组件,但是这个默认组件在某些特定场景下,它并不能满足我们的样式需求。

 

不一定是省市区,像基于其它数据源的选择器,我们也可以自定义实现,还有它的样式,我们也可以自如的去控制,这一点 picker 就无法做到了。

 

我们先看一看这三个组件的主要属性。

 

object array 这个属性值是怎么回事?如何使用它?

片 2

 

对于 picker 组件,它的 range 属性,可以是数组,也可以是对象数组,也就是 object array。如果我们要选择的值,和显示的值不一样,不是一个值,这个时候就可以使用 object array。

 

array: ['美国', '中国', '巴西', '日本'],

objectArray: [

{

id: 0,

name: '美国'

},

{

id: 1,

name: '中国'

},

{

id: 2,

name: '巴西'

},

{

id: 3,

name: '日本'

}

],

片 3

 

我们看一下这段代码,当使用这种对象数组数据结构的时候,它仍然是一个数组,只不过它的数组元素的类型不再是一个简单的字符串,而是一个 object。

 

在使用这个数组结构以后呢,我们需要另外指定一个 range-key,来指定元素对象中的哪一个字段,是用于在 picker 中显示文本的。

 

如何取用户选择的值?

片 4

 

总的来讲,这个问题,要从事件中去取。

 

picker 组件选择的值在变化的时候,也就是用户单击“确定”按钮以后,会派发一个 change 事件,假如我们的事件名是event,我们可以从 event.detail.value 中,取到组件的 value 值,这个值要注意,它是一个索引值。数据源是数组,这个 value 只是数组的索引,是从 0 开始的。

 

对于 picker 组件,当 mode 属性是 multiSector 类型的时侯,还会多一个 columnchange 事件,在这个事件的详情对象 event.detail 这个对象里面,有两个字段,一个是 column,代表当前滑动选择的是第几列,它也是从零开始计数的;另一个字段是 value,代表改变的这一列在改变之后,它的新的值是什么。

 

所有 js 当中、小程序中涉及到的索引,我们都可以认为是从 0 开始的。有人可能会问,难道还有不是从 0 开始的语言吗?

 

答案肯定是有的,像 VBScript,它的数组就是从下标 1 开始的,如果用 0 下标的话,代码就报错了。

 

对于 picker-view 组件,它也有一个 change 事件,也是通过 event.detail.value 获取当前选择的值。不同的地方在于,picker-view 的选择值,永远是一个数组,即使它的子组件只有一个 picker-view-column,value 也是一个数组,是一个只有一个元素的数组。

 

如何使用多项选择器,如何从多项选择器里面取到值,并使用取到的值?

片 5

 

这涉及到多项选择器的设计,我们先看一段代码。

 

<picker mode="multiSelector" bindchange="bindMultiPickerChange" bindcolumnchange="bindMultiPickerColumnChange" value="{{multiIndex}}" range="{{multiArray}}">

<view class="picker">

当前选择:{{multiArray[0][multiIndex[0]]}},{{multiArray[1][multiIndex[1]]}},{{multiArray[2][multiIndex[2]]}}

view>

picker>

片 6

 

在这段代码中,对于普通的单列单选择器来讲,我们取到的 value 的值,它是一个数字,它代表选择了我们数据源数组的索引是哪一个。对于多项选择器,我们取到的 value 值,它是一个索引数组,选择器有几项,这个数组就有几个元素。

 

多项选择器取到的 value 值,和单列选择器一样,只是数据类型不同,一个是数值,一个是数组。在使用的时候,如果是多项选择器,直接把 value 当成一个数组去使用就可以了。

 

像在上面的这个代码示例里面,在展示文本的时候。首先是从 multiIndex[0]取到第 1 列选择的索引,然后再拿这个索引从数据源多维数组里面,也就是 multiArray,去取要展示的文本数据。对于多列选择器,我们的数据源,它是一个多维数组,所以对于第一列,我们用 multiArray[0] 作为数组对象取数据,第二列用 multiArray[1]作为数组对象取数据,以这种方式取出文本数据。

 

如何理解选择器的 mode 属性?它都有哪些可以用的值?

片 7

 

1,selector 普通选择器

2,multiSelector 多列选择器

3,time 时间选择器

4,date 日期选择器

5,region 省市区选择器

片 8

 

我们看一下这个列表,mode 属性有五个合法值(见上面)。

 

表面上有 5 种类型,本质上其实只有两种类型,一个是普通的单列选择器,一个是多列选择器。后面的关于时间、日期、省市区这三个类型的选择器,可以说是微信团队实现的,在特定场景下使用的,三种特殊的多列选择器。

 

我们完全可以根据自己的需求,基于 multiSelector 这个 mode,以不同的数据源,创造出适合需求的多列选择器。

 

如何在日期选择器中直接选择月,如何处理日期选择器的粒度?

片 9

 

当 picker 组件的 mode 属性为 date 时 ,这时候选项最多有三列:年、月、日,它的 fields 属性 默认为 day。

 

1,year 选择器粒度为年

2,month 选择器粒度为月份

3,day 选择器粒度为天

片 10

 

fileds 属性就是控制日期选择器的粒度的,fields 属性的有效值有上面这三个(见上面)。

 

如果我们将 fields 属性改为 year,那么选项列只有年这一列了;同理如果改为 month,只有年、月这两列了。

 

当我们改变 fileds 属性时,通过 event.detail.value 取到的值,也会相应发生改变,当 fields 为 year 时,value 只有年份;同时当 fields 为 month 时,value 只包括年月两个元素信息了。

 

当 fields 为 day 时,value 包括哪些值呢?这个问题留给你,不妨想一想。

 

微信小程序的编码规范是什么?事件名如何写?函数名如何写?变量如何写,文件名如何定义等等。

片11

 

(一)四种常用的命名方法

(二)wxss 命名规范

(三)wxml 命名规范

(四)js 命名规范

(五)文件与目录的命名规范

片 12

 

说到编码规范,主要是指各种命名方式。至于缩进、排版之类的样式,我们直接使用开发者工具的格式化功能就可以了。

 

至于注释,这个要看个人习惯啊,有的人习惯写很多注释,有的人认为清晰的代码本身就是注释啊,不想写注释。这个要看个人的习惯,我本人是很喜欢写注释的,因为我的记性不太好啊,过一段时间自己写的代码都不知道是什么了。

 

(一)四种常用的命名方法

1,小驼峰命名法

2,大驼峰命名法

3,连字符命名法

4,匈牙利命名法

片 13

 

我们看一下这个列表,广泛使用的命名法包括以上 4 种(见上面),现在我们分别看一下这4种命名方式。

 

1,小驼峰命名法

 

例如 displayScreenSize 这个名称,它像骆驼的驼峰一样,一高一低,第一个单词的首字母小写,后面每个单词的首字母大写,这被称之为“小驼峰命名法”,小字呢指第一个单词首字母小写。

 

2,大驼峰命名法

 

与小驼峰命名法对应的,就是大驼峰命名法,不同点是大驼峰第一个单词首字母大写,例如 DisplayScreenSize。

 

大驼峰命名法有时候也叫帕斯卡命名法,但我们叫它大驼峰,这更方便我们与小驼峰放在一起记忆。

 

3,连字符命名法

 

顾名思义,就是以连字符做为单词分隔符的命名法,例如 picker-view-column。

 

4,匈牙利命名法

 

大名鼎鼎的苹果产品的命名,就借鉴了这种命名方法,例如 iPhone、iOS、iPad 等,第一个字母都是小写的 i。

 

匈牙利命名法要求在正式的标志符名称前面,加上一个或多个小写字母,再以一个下线数与后面的字符连接,例如:m_lpszStr,据说这个名称表示一个指向以 0 字符结尾的字符串的长指针成员变量

 

变量名如果都写成这样,除了读文档,还有作者本人,恐怕谁也不知道它的含义了。

 

名称还是要尽量简洁明了,尽量要表义,要从字面上就看出来这个名称是干什么用的。

 

接下来结合这四种命名法,我们看看,在小程序开发中,对各种命名情况一般怎么处理。

 

(二)wxss 命名规范

片 14

 

首先我们看wxss命名规范,wxss 其实就是 css,css 本身的样式命名方法,简单讲就是连字符命名,例如 background-color,border-radius 等。

 

对于复杂的类名称命名,一般采用 BEM 命名法。什么是BEM命名方法呢?

 

模块__模块元素-描述符

片 15

 

BEM 是 Block Element Modifier 的缩写,它将一个类名分成这三个部分:模块_、模块元素和描述符(见上面)。下面我们分别看一看这三个部分。

 

第1个,模块,它控制整个组件的样式,一般是组件名。

 

第2个,模块元素,它规定这个组件中子元素的样式。

 

第3个,描述符,它描述元素或模块具有的一种种类或功能。描述符用于给模块或模块元素,增加特别的样式,以表示这个元素处于某种特别的状态,比如说被选中、被暂停等等。

 

在 BEM 命名法中,我们使用两个下划线“__”连接模块与模块元素,使用一个连字符“-”在尾部连接描述符。有可能每个部分是两个或更多单词,这种情况用连字符单词间隔。

 

1,weui-cell__radio

2,weui-cell__radio-selected

片 16

 

下面我们举两个例子(见上面),通过例子看一下BEM这种命名方法。

 

第 1 个,weui-cell__radio,它表示 weui-cell 这个模块它的子元素 radio 组件的样式。对于所有 weui 组件,为了和其它组件库的样式有所区别,默认都是以 weui 开头的。

 

在这里我们可以把 weui-cell 理解为是两个单词,中间使用了连字符做了一个分隔。一般不允许直接将两个单词都是小写的连在一起,就像小程序中的一些事件名称一样,那样很难辨认。

 

第 2 个,weui-cell__radio-selected,它表示 radio 这个子组件它被选中时的样式。选中以后,颜色可能会暗沉下来,有人喜欢将这种颜色状态的变化,命名为样式,例如 weui-cell__radio-dark 或 weui-cell__radio-light,其实这是不好的命名。

 

我们要尽量用功能或状态名称做为描述符,要避免拿视觉效果词汇做为描述符,像 dark、light 这些单词都是描述视觉效果的词汇。

 

我们再举个例子,在开发中,一般我们以组件名作为 BEM 命名法里的模块元素名称,然后以连字符间隔,后面接着是一个状态名称,例如 icon-checked。

 

如果这个样式不是全局通用的,是某个模块里专用的,我们可以在前面再加一个模块名称,例如 student__icon-checked。如果在我们的系统里,student 又分为男生和女生,那么样式还可以再拆分为 boy-student__icon-checked 和 girl-student__icon-checked。

 

(三)wxml 命名规范

片 17

 

接下来我们再看wxml命名规范,wxml 文件里面的标签名称,一般以简单连字符间隔,例如 picker-view、picker-view-column 等。我们自定义的组件,名称也要保持这样的风格。

 

wxml 组件的属性,名称也要采用连字符命名法,例如 ext-class、indicator-style、indicator-class 等。

 

在组件的事件属性上绑定的 js 事件函数句柄的名称,要遵从 js 命名规范,使用小驼峰命名法。在单词的组成上,一般使用“on+名称+状态或动词”这样的形式,例如 onRegionChanged。

 

尽量使用有含义的单词,避免使用通用单词。例如给一个组件的 tap 事件写 js 事件句柄函数,如果写成 onTap,这样就太随意了。

 

Tap 没有具体的含义,这个 onTap 函数,它的函数体要处理什么事件呢,最好在名称中就体现出来,这样我们不必看 js 代码,就大概知道它的作用了。

 

当然了,如果整个页面里只有一个 tap 事件,也可以使用 onTap 这个名称。如果整个页面只处理了一个 tap 事件,那么这个 js 事件句柄函数叫 onTap 也无妨。主要还是看在哪个范围之内定义,作到范围之内清晰,编码规范的目的就达到了。

 

(四)js 命名规范

片 18

 

接下来我们接着看js命名规范。在Js里面,涉及到类名,一般使用大驼峰命名法。

 

其它的,像函数名、方法名、变量名、函数参数名,统一都使用小驼峰命名法。以前对于 const 常量,有人喜欢使用下划线间隔的全大写单词形式命名,现在慢慢也很少用了。主要因为这种形式编写起来比较费劲,不如直接与普通的变量一样,使用小驼峰命名简单。

 

(五)文件与目录的命名规范

片 19

 

文件名、文件夹名称,一般都使用连字符命名法。

 

以上大概是小程序开发中经常涉及到的一些命名规范。在使用的时候,以简单清晰为原则,怎么简单怎么来,规范是为了让代码更清晰,规范而不是枷锁,不是为了增加我们的编码负担。

 

课后作业:如何使用 vtabs

片 20

 

在上节课我们留了一个作业:weui 组件库中有一个 vtabs 组件,它是一个有侧边栏分类的商品浏览组件,请你尝试在小程序项目中使用它。

 

这是一个 weui 组件库中的一个扩展组件,使用它之前首先需要使用 npm 安装。

 

npm i @miniprogram-component-plus/vtabs --save

npm i @miniprogram-component-plus/vtabs-content --save

"usingComponents": {

"mp-vtabs": "@miniprogram-component-plus/vtabs/index",

"mp-vtabs-content": "@miniprogram-component-plus/vtabs-content/index"

}

片 21

 

vtabs 是纵向选项卡组件,使用它的时候,需要与 vtabs-content 组件结合起来一起使用,所以在安装的时候,需要安装两个依赖包(见上面指令)。

 

接下来我们需要在 json 文件中,进行引用声明(见上面代码)。在引用之后就可以在 wxml 代码里使用它们了。

 

vtabs 的实现原理,与上节课我们介绍的侧边栏分类浏览组件是类似的,也是基于 scroll-view 组件实现的,单击左侧菜单,右侧商品通过 scroll-into-view 滚动到目标区域;在右侧商品区域自由滚动时,通过预先存储好的高度,计算应该将哪个左侧菜单高亮。

 

值得一提的是,这个 vtabs 的实现,使用父子组件关系,并且父子组件通过声明的方式,实现了父子组件的调用。

 

relations: {

'../vtabs/index': {

type: 'parent'

}

},

methods: {

calcHeight: function calcHeight(callback) {

var query = this.createSelectorQuery();

query.select('.weui-vtabs-content__item').boundingClientRect(function (rect) {

callback && callback(rect);

}).exec();

}

}

片 22

 

在 vtabs-content 组件中,有 relations 声明,声明 vtabs 是它的父组件。并且在这个组件的 methods 对象中,有一个 calcHeight 函数,这个函数是计算 vtabs-content 本身高度的。每个分类的商品有多有少,所以每个 vtabs-content 的高度也是不一样的。

 

relations: {

'../vtabs-content/index': {

type: 'child',

linked: function linked(target) {

var _this = this;

target.calcHeight(function (rect) {

_this.data._contentHeight[target.data.tabIndex] = rect.height;

if (_this._calcHeightTimer) {

clearTimeout(_this._calcHeightTimer);

}

_this._calcHeightTimer = setTimeout(function () {

_this.calcHeight();

}, 100);

});

},

unlinked: function unlinked(target) {

delete this.data._contentHeight[target.data.tabIndex];

}

}

},

片 23

 

在 vtabs 中也有一个 relations 声明,声明 vtabs-content 是子组件,并在 linked 属性中设置调用了子组件预先定义好的的 calcHeight 方法,并且将取到的高度存储了起来。

 

我们注意到,在 vtabs 中,在调用了子组件的 calcHeight 方法之后,又设定了一个延时定时器,在 100ms 之后,又调用了自己的 calcHeight 函数,这一步操作,是为了生成每个商品区域的绝对位置高度。

 

在上一步,调用子组件的 clacHeight 函数,只是取到了子组件的高度,并按索引存了起来。在 tabs 自己的 clacHeight 函数中,又对存储的高度做了一个遍历,将每个商品区域的绝对位置高度计算了出来。

 

整体讲,vtabs 的设计更为高级一点。

(可以在本课视频中查看示例效果)

好,这节课就讲到这里,这节课我们主要学习了选择器组件,了解了这个组件的主要属性,还学习了小程序的编码规范,在以后的学习与开发过程中,我们要尽量按照编码规范去规范自己的代码,养成良好的习惯。下节课我们继续学习这个选择器组件,并着手基于 picker-view,用两种方法,自定义实现一个三级联动的选择器。

 

2020 年 5 月 24 日