在chameleon项目中我们完成一个跨端组件平常有两种思绪:运用第三方组件封装与基于chameleon语法一致完成。本篇是编写chameleon跨端组件的准确姿态系列文章的上篇,以封装一个跨端的indexlist组件为例,起首引见怎样文雅的运用第三方库封装跨端组件,然后给出编写chameleon跨端组件的发起。运用chameleon语法一致完成跨端组件请关注文章《编写chameleon跨端组件的准确姿态(下篇)》
依托壮大的多态协定,chameleon项目中可以轻松运用各端的第三方组件封装自身的跨端组件库。基于第三方组件可以应用现有生态敏捷完成需求,然则却存在许多瑕玷,比方多端第三方组件自身的功用与款式差别、组件质量得不到保证以及绝大部份组件并不须要经由历程多态组件差别化完成,如许反而提拔了历久的保护本钱;运用chameleon语法一致完成则可以圆满处理上述题目,而且扩大一个新的端时现有组件可以直接运转。本文的末了也会细致对照一下两种计划的好坏。
因而,发起将经由历程第三方库完成跨端组件库作为临时计划,从历久保护的角度来讲,发起开辟者运用chameleon语法一致完成绝大部份跨端组件,只要一些迥殊庞杂而且已有成熟第三方库或许框架才能临时不支持的组件,才斟酌运用第三方组件封装成对应的跨端组件。
因为本文引见的是运用第三方库封装跨端组件, 因而示例的indexlist组件采纳第三方组件封装来完成, 经由历程chameleon一致完成跨端组件的要领可以看《编写chameleon跨端组件的准确姿态(下篇)》。
终究完成的indexlist结果图:
前期预备
运用各端第三方组件完成chameleon跨端组件须要以下前期预备:
项目初始化
建立一个新项目 cml-demo
cml init project
进入项目
cd cml-demo
组件设想
开辟一个模块时我们起首应当依据功用肯定其输入与输出,对应到组件开辟上来讲,就是要肯定组件的属性和事宜,个中属性示意组件接收的输入,而事宜则示意组件在特定机遇对外的输出。
为了轻易申明,本例临时完成一个具有基本功用的indexlist。一个indexlist组件最少应当在用户挑选某一项时抛出一个onselect事宜,通报用户当前所选中项的数据;最少应当接收一个datalist,作为其衬着的数据源,这个datalist应当是一个类似于以下构造的对象数组:
const dataList = [
{
name: '阿里',
pinYin: 'ali',
py: 'al'
}, {
name: '北京',
pinYin: 'beijing',
py: 'bj'
},
.....
]
寻觅第三方组件库
因为本文引见的是怎样运用第三方库封装跨端组件,因而在肯定组件需求以及完成思绪后去寻觅相符要求的第三方库。在开辟之前,作者调研了现在较为盛行的各端组件库,引荐以下:
web端:
wx端:
weex端:
除了上述组件库以外,开辟者也可以依据自身的现实需求去寻觅经由包装以后相符预期的第三方库。停止文章编写时,作者未找到较成熟的支付宝及百度小顺序第三方库,因而临时先完成web、微信小顺序以及weex端,这也表现出了运用第三方库扩大跨端组件的局限性:当没有成熟的对应端第三方库时,没法完成该端的组件开辟;而运用chameleon语法一致完成则可以处理上述题目,扩大新的端时已有组件可以直接运转,无需分外扩大。 本文在完成indexlist组件时分别运用了cube-ui, iview weapp以及weex-ui, 以下会引见详细的开辟历程.
组件开辟
初始化
建立多态组件
cml init component
挑选“多态组件”, 并输入组件名字“indexlist”, 完成组件的建立, 建立以后的组件位于src/components/indexlist文件夹下。
接口校验
多态组件中的.interface文件应用接口校验语法对组件的属性和事宜举行范例定义,保证各端的属性和事宜一致。肯定了组件的属性与事宜以后就最先编写.interface文件, 修正src/components/indexlist/indexlist.interface:
type eventDetail = {
name: String,
pinYin: String,
py: String
}
type arrayItem = {
name: String,
pinYin: String,
py: String
}
type arr = [arrayItem];
interface IndexlistInterface {
dataList: arr,
onselect(eventDetail: eventDetail): void
}
详细的interface文件语法可以参考此处, 本文不再赘述。
web端组件开辟
装置cube-ui
npm i cube-ui -S
在src/components/indexlist/indexlist.web.cml的json文件中引入cube-ui的indexlist组件
"base": {
"usingComponents": {
"cube-index-list": "cube-ui/src/components/index-list/index-list"
}
}
修正src/components/indexlist/indexlist.web.cml中的模板代码,援用cube-ui的indexlist组件:
<view class="index-list-wrapper">
<cube-index-list
:data="list"
@select="onItemSelect"
/>
</view>
修正src/components/indexlist/indexlist.web.cml中的js代码, 依据cube-ui文档将数据处理成相符其组件预期的构造, 并向上抛出onselect事宜:
const words = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
class Indexlist implements IndexlistInterface {
props = {
dataList: {
type: Array,
default() {
return []
}
}
}
data = {
list: [],
}
methods = {
initData() {
const cityData = [];
words.forEach((item, index) => {
cityData[index] = {};
cityData[index].items = [];
cityData[index].name = item;
});
this.dataList.forEach((item) => {
let firstName = item.pinYin.substring(0, 1).toUpperCase();
let index = words.indexOf(firstName);
cityData[index].items.push(item)
});
this.list = cityData;
},
onItemSelect(item) {
this.$cmlEmit('onselect', item);
}
}
mounted() {
this.initData();
}
}
export default new Indexlist();
编写必要的款式:
.index-list-wrapper {
width: 750cpx;
height: 1200cpx;
}
以上便运用cube-ui完成了web端indexlist组件的开辟,结果以下:
weex端组件开辟
装置weex-ui
npm i weex-ui -S
在src/components/indexlist/indexlist.weex.cml的json文件中引入weex-ui的wxc-indexlist组件:
"base": {
"usingComponents": {
"wex-indexlist": "weex-ui/packages/wxc-indexlist"
}
}
修正src/components/indexlist/indexlist.weex.cml中的模板代码,援用weex-ui的wxc-indexlist组件:
<view class="index-list-wrapper">
<wex-indexlist
:normal-list="list"
@wxcIndexlistItemClicked="onItemSelect"
/>
</view>
修正src/components/indexlist/indexlist.weex.cml中的js代码:
class Indexlist implements IndexlistInterface {
props = {
dataList: {
type: Array,
default() {
return []
}
}
}
data = {
list: [],
}
mounted() {
this.initData();
}
methods = {
initData() {
this.list = this.dataList;
},
onItemSelect(e) {
this.$cmlEmit('onselect', e.item);
}
}
}
export default new Indexlist();
编写必要款式,此时发明weex端与web端有部份反复款式,因而将款式抽离出来建立indexlist.less,在web端与weex端的cml文件中引入该款式
<style lang="less">
@import './indexlist.less';
</style>
indexlist.less文件内容:
.index-list-wrapper {
width: 750cpx;
height: 1200cpx;
}
以上便运用weex-ui完成了weex端indexlist组件的开辟,结果以下:
wx端组件编写
依据iview weapp文档, 起首到Github下载iview weapp代码,将dist目次拷贝到项目的src目次下,然后在src/components/indexlist/indexlist.wx.cml的json文件中引入iview的index与index-item组件:
"base": {
"usingComponents": {
"i-index":"/iview/index/index",
"i-index-item": "/iview/index-item/index"
}
},
修正src/components/indexlist/indexlist.wx.cml中的模板代码,援用iview的index与index-item组件:
<view class="index-list-wrapper">
<i-index
height="1200rpx"
>
<i-index-item
wx:for="{{cities}}"
wx:for-index="index"
wx:key="{{index}}"
wx:for-item="item"
name="{{item.key}}"
>
<view
class="index-list-item"
wx:for="{{item.list}}"
wx:for-index="in"
wx:key="{{in}}"
wx:for-item="it"
c-bind:tap="onItemSelect(it)"
>
<text>{{it.name}}</text>
</view>
</i-index-item>
</i-index>
</view>
修正src/components/indexlist/indexlist.wx.cml中的js代码, 依据iview weapp文档将数据处理成相符其组件预期的构造, 并向上抛出onselect事宜:
const words = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
class Indexlist implements IndexlistInterface {
props = {
dataList: {
type: Array,
default() {
return []
}
}
}
data = {
cities: []
}
methods = {
initData() {
let storeCity = new Array(26);
words.forEach((item,index)=>{
storeCity[index] = {
key: item,
list: []
};
});
this.dataList.forEach((item)=>{
let firstName = item.pinYin.substring(0,1).toUpperCase();
let index = words.indexOf(firstName);
storeCity[index].list.push(item);
});
this.cities = storeCity;
},
onItemSelect(item) {
this.$cmlEmit('onselect', item);
}
}
mounted() {
this.initData();
}
}
export default new Indexlist();
编写必要款式:
@import 'indexlist.less';
.index-list {
&-item {
height: 90cpx;
padding-left: 20cpx;
justify-content: center;
border-bottom: 1cpx solid #F7F7F7
}
}
以上便运用iview weapp完成了wx端indexlist组件的开辟, 结果以下:
组件运用
修正src/pages/index/index.cml文件内里的json设置,援用建立的indexlist组件
"base": {
"usingComponents": {
"indexlist": "/components/indexlist/indexlist"
}
},
修正src/pages/index/index.cml文件中的模板部份,援用建立的indexlist组件
<view class="page-wrapper">
<indexlist
dataList="{{dataList}}"
c-bind:onselect="onItemSelect"
/>
</view>
个中dataList是一个对象数组,示意组件要衬着的数据源。详细构造为:
const dataList = [
{
name: '阿里',
pinYin: 'ali',
py: 'al'
}, {
name: '北京',
pinYin: 'beijing',
py: 'bj'
},
.....
]
开辟总结
依据上述例子可以看出,chameleon项目可以轻松连系第三方库封装自身的跨端组件库。运用第三方组件封装跨端组件库的步骤大抵以下:
- 跨端组件设想
- 依据现实需求引入适宜的第三方组件
- 依据第三方组件文档,将数据处理成相符预期的构造,并在恰当机遇抛出事宜
- 编写必要款式
一些思索
明白*.[web|wx|weex].cml
依据组件多态文档, 像indexlist.web.cml、indexlist.wx.cml与indexlist.weex.cml的这些文件是灰度区, 它们是唯一可以挪用基层端才能的CML文件,这里的基层端才能既包含基层端组件,比方在web端和weex端的.vue文件等;也包含基层端的api,比方微信小顺序的wx.pageScrollTo等。这一层的存在是为了挪用基层端代码,各端详细的逻辑完成应当在基层来完成, 这类范例的优点是不言而喻的: 跟着营业庞杂度的提拔,各个基层端保护的功用逐步变多,个中通用的部份又可以经由历程一般cml文件抽离出来被一致挪用,如许可以保证差别化部份始终是最小鸠合,灰度区是存粹的;假如将营业逻辑都放在了灰度区,跟着功用庞杂度的上升,三端通用功用/组件就没法到达合理的笼统,致使灰度层既有雷同功用,又有差别化部份,这明显不是开辟者情愿看到的场景。
在灰度区的模板、逻辑、款式和json文件平分别具有以下划定规矩:
模板
- 挪用基层组件时,既可以运用chameleon语法,也可以运用各端原生语法;在灰度区chameleon编译器不会编译各个端原生语法,比方v-for,bindtap等。发起在模板部份依然运用chameleon模板语法,只要在完成对应平台不支持的语法(比方web端v-html等)时才运用原生语法。
- 援用基层全局组件时须要增加origin-前缀,如许可以“通知”chameleon编译器是在援用基层的原生组件,chameleon编译器就不会对其举行处理了。这类做法同时处理了组件定名争执题目,比方在微信小顺序端援用<origin-button>示意挪用小顺序原生的button组件而不是chameleon内置的button组件。
逻辑
- 在script逻辑代码中,除了编写一般cml逻辑代码以外,开辟者还可以运用基层端的全局变量和恣意要领,包含性命周期函数。这类机制保证开辟者可以天真扩大各端特有功用,而不须要依靠多态接口。
款式
- 既可以运用cmss语法也可以运用基层端的css语法。
json文件
在各端对应的灰度区文件中均可以依据上述范例运用各端的原生语法,然则为了范例依然发起运用chameleon系统的语法划定规矩。整体来讲,灰度区可以认为是chameleon系统与各端原生组件/要领的衔接点,向下运用各端功用/组件,向上经由历程多态协定供应各端一致的挪用接口。