摘要:在编写跨端组件的正确姿势上篇中,我们介绍了如何使用第三方库封装跨端组件,但是绝大多数组件并不需要那样差异化实现,绝大多数情况下我们推荐使用语法统一实现跨端组件。
在chameleon项目中我们实现一个跨端组件一般有两种思路:使用第三方组件封装与基于chameleon语法统一实现。
在《编写chameleon跨端组件的正确姿势(上篇)》中, 我们介绍了如何使用第三方库封装跨端组件,但是绝大多数组件并不需要那样差异化实现,绝大多数情况下我们推荐使用chameleon语法统一实现跨端组件。本篇是编写chameleon跨端组件的正确姿势系列文章的下篇,与上篇给出的示例相同,本篇也以封装一个跨端的indexlist组件为例,首先介绍如何使用chameleon语法统一实现一个跨端组件,然后对比两种组件开发方式并给出开发建议。
以下效果依此为weex端、web端、支付宝小程序端、微信小程序端以及百度小程序端:
创建一个新项目 cml-demo
cml init project
进入项目
cd cml-demo组件创建
cml init component
选择“普通组件”, 并并输入组件名字“indexlist”, 完成组件的创建, 创建之后的组件位于src/components/indexlist文件夹下。
组件设计为了方便说明,本例暂时实现一个具备基础功能的indexlist组件。从功能方面讲,indexlist组件主要由两部分组成,主列表区域和索引区域。在用户点击组件右侧索引时,主列表能够快速定位到对应区域;在用户滑动组件主列表时,右侧索引跟随滑动不停切换当前索引项。从输入输出方面讲,组件至少应该在用户选择某一项时抛出一个onselect事件,传递用户当前所选中项的数据;至少应该接受一个datalist,作为其渲染的数据源,这个datalist应该是一个类似于以下结构的对象数组:
const dataList = [ { name: "阿里", pinYin: "ali", }, { name: "北京", pinYin: "beijing", }, ..... ]主要数据结构设计
根据设计的组件功能与输入输出, 我们开始设计数据结构。
indexlist组件右侧的索引列对应的数据结构为一个数组,其中的每一项表示一个索引,具体结构如下:
this.shortcut = [ "A", "B", "C", ....]
indexlist组件的主列表区域对应的数据结构也是一个数组,其中的每一项表示一个子列表区域(例如以首字母a开头的子列表)。下面我们考虑每一个子列表区域中至少应该包含的字段:
一个name字段,表示该子列表区域的名称;
一个items字段,该字段也是一个数组,数组中的每一项表示该子列表区域的每一项;
一个offsetTop, 表示该子列表区域距离主列表顶部的距离,通过该字段实现点击右侧索引时能够通过滚动相应距离快速定位到该子列表;
一个totalHeight字段,表示该子列表区域的所占的高度,通过该字段与offsetTop字段可以确定每个子列表所在的高度范围, 以此实现右侧索引跟随滑动不停切换当前索引项
由上面分析可得主列表区域数据结构如下:
this.list = [ { name: "B", items:[ { name: "北京", pinYin: "beijing" }, { name: "包头", pinYin: "baotou" } ... ], offsetTop: 190, totalHeight: 490 }, .... ]功能实现
从前文可知,输入组件的datalist具有如下结构:
const dataList = [ { name: "阿里", pinYin: "ali", }, { name: "北京", pinYin: "beijing", }, ..... ]
可以发现该datalist结构是扁平并且缺乏很多信息(例如totalHeight等)的,因此首先要从输入数据中整理出来所需的数据结构,修改src/components/indexlist/indexlist.cml的js部分:
initData() { // get shortcut this.dataList.forEach(item => { if (item.pinYin) { let firstName = item.pinYin.substring(0, 1); if (item.pinYin && this.shortcut.indexOf(firstName.toUpperCase()) === -1) { this.shortcut.push(firstName.toUpperCase()); }; }; }); // handle input data const cityData = this.shortcut.map(item => ({items:[], name: item})); this.dataList.forEach((item) => { let firstName = item.pinYin.substring(0, 1).toUpperCase(); let index = this.shortcut.indexOf(firstName); cityData[index].items.push(item); }); // calculate item offsetTop && totalHeight cityData.forEach((item, index) => { let arr = cityData.slice(0, index); item.totalHeight = this.itemNameHeight + item.items.length * this.itemContentHeight; item.offsetTop = arr.reduce((total, cur) => (total + this.itemNameHeight + cur.items.length * this.itemContentHeight), 0); }); this.list = cityData; },
这样我们就拿到了主列表数组this.list与索引列表数组this.shortcut, 然后根据数组结构编写模板内容。模板内容分为两大部分,一个是主列表区域,修改src/components/indexlist/indexlist.cml文件模板部分:
{{listitem.name}} {{subitem.name}}
其中scroller是一个chameleon提供的内置滚动组件,其属性值scrolltop表示当前滚动的距离,onscroll表示滚动时触发的事件。在主列表这一部分,我们要实现如下功能:
在滚动时,右侧索引不停切换当前索引项的功能
点击列表中的每一项时,向外抛出onselect事件
修改src/components/indexlist/indexlist.cml文件js部分:
handleScroll(e) { let { scrollTop } = e.detail; scrollTop = Math.ceil(scrollTop); this.activeIndex = this.list.findIndex(item => scrollTop >= item.offsetTop && scrollTop < item.totalHeight + item.offsetTop ) }, handleSelect(e) { this.$cmlEmit("onselect", e) }
当前激活的索引(this.activeIndex)经过计算得到,规则为:如果当前scroller滚动的距离在对应子列表所在的高度范围内,则认为该索引是激活的。
另一部分是索引区域,修改src/components/indexlist/indexlist.cml文件模板部分,增加索引区域模板内容:
{{item}}
在索引区域,我们要实现点击索引值主列表能够快速定位到对应区域,修改src/components/indexlist/indexlist.cml文件js部分:
scrollToItem(shortcut) { let { offsetTop } = this.list.find(item => item.name === shortcut); this.offsetTop = offsetTop; }
索引区域应该定位在视窗右侧并且上下居中。由于chameleon暂时不支持在css中使用百分比,因此我们通过chameleon-api提供的对外接口获取屏幕视窗高度,然后使用js计算得到位置, 配合部分css来实现索引区域定位在视窗右侧居中。修改src/components/indexlist/indexlist.cml文件js部分:
// computed compScwStyle() { return `top:${this.viewportHeight / 2}cpx` } // method async getViewportHeight() { let res = await cml.getSystemInfo(); this.viewportHeight = res.viewportHeight; },
至此便通过chameleon语法统一实现了一个跨端indexlist组件,该组件直接可以在web、weex、微信小程序、支付宝小程序与百度小程序五个端运行。为了方便描述,上述代码只是简单介绍了组件实现的核心代码,跳过了样式和一些逻辑细节。
修改src/pages/index/index.cml文件里面的json配置,引用创建的indexlist组件
"base": { "usingComponents": { "indexlist": "/components/indexlist/indexlist" } },
修改src/pages/index/index.cml文件中的模板部分,引用创建的indexlist组件
其中dataList是一个对象数组,表示组件要渲染的数据源
一些思考本篇文章主要介绍了如何通过chameleon语法实现跨端组件。对比编写chameleon跨端组件的正确姿势(上篇).md)介绍的通过第三方库封装的方法可以发现,两种方式是完全不同的,现详细对比一下这两种实现方式的优势与劣势, 并给出开发建议:
优势 | 劣势 | 开发建议 | |
基于第三方组件库实现 | - 可利用已有生态迅速完成跨端组件 |
- 组件的实现依赖第三方库,如果没有成熟的对应端第三方库则无法完成该端组件开发 - 由于各端第三方组件存在差异,封装的跨端组件样式与功能存在差异 - 第三方组件升级时,要对应调整跨端组件的实现,维护成本较大 - 第三方组件库质量不能得到保证 |
- 将基于各端第三方组件封装跨端组件库的方法作为临时方案
- 对于特别复杂并且已有成熟第三方库或者框架能力暂时不支持的组件,可以考虑使用第三方组件封装成对应的跨端组件,例如图表组件、地图组件等等 |
基于chameleon统一实现 |
- 新的端接入时,能够直接运行 - 一般情况下,不存在各端样式与功能差异 - 绝大部分组件不需要各端差异化实现,使用chameleon语法实现开发与维护成本更低 - 能够导出原生组件供多端使用 |
- 从零搭建时间与技术成本较高 |
从长期维护的角度来讲,建议使用chameleon生态来统一实现跨端组件库 如果仅仅是各端api层面的不同,建议使用多态接口抹平差异,而不使用多态组件 |
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/102464.html
摘要:使用语法统一实现跨端组件请关注文章编写跨端组件的正确姿势下篇依靠强大的多态协议,项目中可以轻松使用各端的第三方组件封装自己的跨端组件库。这种做法同时解决了组件命名冲突问题,例如在微信小程序端引用表示调用小程序原生的组件而不是内置的组件。 在chameleon项目中我们实现一个跨端组件一般有两种思路:使用第三方组件封装与基于chameleon语法统一实现。本篇是编写chameleon跨端...
摘要:基于对跨端工作的积累,规范了一套跨端标准,称之为协议开发者只需要按照标准扩展流程,即可快速扩展任意架构模式的终端。实现了微信端的基本扩展,用户可以以此为模板进行开发。新框架太多?学不动啦?有这一套跨端标准,今后再也不用学习新框架了。各个小程序按自己喜好各自为政?有了这套标准,再也不用重复开发各种新平台啦。如今前端比较流行的 React Native、Weex、Flutter 等跨平台开发框架...
摘要:但是从年微信推出小程序,到至今各大厂商都推出自己的小程序,跨端开发就不仅仅是技术的问题了。实现了微信端的基本扩展,用户可以以此为模板进行开发。 新框架太多?学不动啦?有这一套跨端标准,今后再也不用学习新框架了。 各个小程序按自己喜好各自为政?有了这套标准,再也不用重复开发各种新平台啦。 如今前端比较流行的 React Native、Weex、Flutter 等跨平台开发框架,对于开发来...
摘要:中国互联网络信息中心发布的中国互联网络发展状况统计报告显示,截至年月,我国网民规模达亿人,微信月活亿支付宝月活亿百度月活亿另一方面,中国手机占智能手机整体的比例超过,月活约亿。在年末正式发布了面向未来的跨端的。 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的? 原创: 嘉宾-张楠 开源中国 以往我们说某一功能跨多端,往往是指在诸如 PC、移动等不同类型的设备之...
阅读 3561·2023-04-25 23:32
阅读 2016·2019-08-30 15:55
阅读 2632·2019-08-30 15:52
阅读 3093·2019-08-30 10:54
阅读 816·2019-08-29 16:16
阅读 602·2019-08-29 15:09
阅读 3627·2019-08-26 14:05
阅读 1599·2019-08-26 13:22