摘要:使用語法統(tǒng)一實(shí)現(xiàn)跨端組件請(qǐng)關(guān)注文章編寫跨端組件的正確姿勢(shì)下篇依靠強(qiáng)大的多態(tài)協(xié)議,項(xiàng)目中可以輕松使用各端的第三方組件封裝自己的跨端組件庫。這種做法同時(shí)解決了組件命名沖突問題,例如在微信小程序端引用表示調(diào)用小程序原生的組件而不是內(nèi)置的組件。
在chameleon項(xiàng)目中我們實(shí)現(xiàn)一個(gè)跨端組件一般有兩種思路:使用第三方組件封裝與基于chameleon語法統(tǒng)一實(shí)現(xiàn)。本篇是編寫chameleon跨端組件的正確姿勢(shì)系列文章的上篇,以封裝一個(gè)跨端的indexlist組件為例,首先介紹如何優(yōu)雅的使用第三方庫封裝跨端組件,然后給出編寫chameleon跨端組件的建議。使用chameleon語法統(tǒng)一實(shí)現(xiàn)跨端組件請(qǐng)關(guān)注文章《編寫chameleon跨端組件的正確姿勢(shì)(下篇)》
依靠強(qiáng)大的多態(tài)協(xié)議,chameleon項(xiàng)目中可以輕松使用各端的第三方組件封裝自己的跨端組件庫。基于第三方組件可以利用現(xiàn)有生態(tài)迅速實(shí)現(xiàn)需求,但是卻存在很多缺點(diǎn),例如多端第三方組件本身的功能與樣式差異、組件質(zhì)量得不到保證以及絕大部分組件并不需要通過多態(tài)組件差異化實(shí)現(xiàn),這樣反而提升了長期的維護(hù)成本;使用chameleon語法統(tǒng)一實(shí)現(xiàn)則可以完美解決上述問題,并且擴(kuò)展一個(gè)新的端時(shí)現(xiàn)有組件可以直接運(yùn)行。本文的最后也會(huì)詳細(xì)對(duì)比一下兩種方案的優(yōu)劣。
因此,建議將通過第三方庫實(shí)現(xiàn)跨端組件庫作為臨時(shí)方案,從長期維護(hù)的角度來講,建議開發(fā)者使用chameleon語法統(tǒng)一實(shí)現(xiàn)絕大部分跨端組件,只有一些特別復(fù)雜并且已有成熟第三方庫或者框架能力暫時(shí)不支持的組件,才考慮使用第三方組件封裝成對(duì)應(yīng)的跨端組件。
由于本文介紹的是使用第三方庫封裝跨端組件, 因此示例的indexlist組件采用第三方組件封裝來實(shí)現(xiàn), 通過chameleon統(tǒng)一實(shí)現(xiàn)跨端組件的方法可以看《編寫chameleon跨端組件的正確姿勢(shì)(下篇)》。
最終實(shí)現(xiàn)的indexlist效果圖:
前期準(zhǔn)備使用各端第三方組件實(shí)現(xiàn)chameleon跨端組件需要如下前期準(zhǔn)備:
創(chuàng)建一個(gè)新項(xiàng)目 cml-demo
cml init project
進(jìn)入項(xiàng)目
cd cml-demo
開發(fā)一個(gè)模塊時(shí)我們首先應(yīng)該根據(jù)功能確定其輸入與輸出,對(duì)應(yīng)到組件開發(fā)上來說,就是要確定組件的屬性和事件,其中屬性表示組件接受的輸入,而事件則表示組件在特定時(shí)機(jī)對(duì)外的輸出。
為了方便說明,本例暫時(shí)實(shí)現(xiàn)一個(gè)具備基礎(chǔ)功能的indexlist。一個(gè)indexlist組件至少應(yīng)該在用戶選擇某一項(xiàng)時(shí)拋出一個(gè)onselect事件,傳遞用戶當(dāng)前所選中項(xiàng)的數(shù)據(jù);至少應(yīng)該接受一個(gè)datalist,作為其渲染的數(shù)據(jù)源,這個(gè)datalist應(yīng)該是一個(gè)類似于以下結(jié)構(gòu)的對(duì)象數(shù)組:
const dataList = [ { name: "阿里", pinYin: "ali", py: "al" }, { name: "北京", pinYin: "beijing", py: "bj" }, ..... ]
由于本文介紹的是如何使用第三方庫封裝跨端組件,因此在確定組件需求以及實(shí)現(xiàn)思路后去尋找符合要求的第三方庫。在開發(fā)之前,作者調(diào)研了目前較為流行的各端組件庫,推薦如下:
web端:
cube-ui
vux
mint-ui
vant
wx端:
iview weapp
vant weapp
weui
weex端:
weex-ui
除了上述組件庫之外,開發(fā)者也可以根據(jù)自己的實(shí)際需求去尋找經(jīng)過包裝之后符合預(yù)期的第三方庫。截止文章編寫時(shí),作者未找到較成熟的支付寶及百度小程序第三方庫,因此暫時(shí)先實(shí)現(xiàn)web、微信小程序以及weex端,這也體現(xiàn)出了使用第三方庫擴(kuò)展跨端組件的局限性:當(dāng)沒有成熟的對(duì)應(yīng)端第三方庫時(shí),無法完成該端的組件開發(fā);而使用chameleon語法統(tǒng)一實(shí)現(xiàn)則可以解決上述問題,擴(kuò)展新的端時(shí)已有組件能夠直接運(yùn)行,無需額外擴(kuò)展。 本文在實(shí)現(xiàn)indexlist組件時(shí)分別使用了cube-ui, iview weapp以及weex-ui, 以下會(huì)介紹具體的開發(fā)過程.
組件開發(fā)創(chuàng)建多態(tài)組件
cml init component
選擇“多態(tài)組件”, 并輸入組件名字“indexlist”, 完成組件的創(chuàng)建, 創(chuàng)建之后的組件位于src/components/indexlist文件夾下。
多態(tài)組件中的.interface文件利用接口校驗(yàn)語法對(duì)組件的屬性和事件進(jìn)行類型定義,保證各端的屬性和事件一致。確定了組件的屬性與事件之后就開始編寫.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文件語法可以參考此處, 本文不再贅述。
安裝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組件:
修改src/components/indexlist/indexlist.web.cml中的js代碼, 根據(jù)cube-ui文檔將數(shù)據(jù)處理成符合其組件預(yù)期的結(jié)構(gòu), 并向上拋出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組件的開發(fā),效果如下:
安裝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組件:
修改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();
編寫必要樣式,此時(shí)發(fā)現(xiàn)weex端與web端有部分重復(fù)樣式,因此將樣式抽離出來創(chuàng)建indexlist.less,在web端與weex端的cml文件中引入該樣式
indexlist.less文件內(nèi)容:
.index-list-wrapper { width: 750cpx; height: 1200cpx; }
以上便使用weex-ui完成了weex端indexlist組件的開發(fā),效果如下:
根據(jù)iview weapp文檔, 首先到Github下載iview weapp代碼,將dist目錄拷貝到項(xiàng)目的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組件:
{{it.name}}
修改src/components/indexlist/indexlist.wx.cml中的js代碼, 根據(jù)iview weapp文檔將數(shù)據(jù)處理成符合其組件預(yù)期的結(jié)構(gòu), 并向上拋出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組件的開發(fā), 效果如下:
修改src/pages/index/index.cml文件里面的json配置,引用創(chuàng)建的indexlist組件
"base": { "usingComponents": { "indexlist": "/components/indexlist/indexlist" } },
修改src/pages/index/index.cml文件中的模板部分,引用創(chuàng)建的indexlist組件
其中dataList是一個(gè)對(duì)象數(shù)組,表示組件要渲染的數(shù)據(jù)源。具體結(jié)構(gòu)為:
const dataList = [ { name: "阿里", pinYin: "ali", py: "al" }, { name: "北京", pinYin: "beijing", py: "bj" }, ..... ]
根據(jù)上述例子可以看出,chameleon項(xiàng)目可以輕松結(jié)合第三方庫封裝自己的跨端組件庫。使用第三方組件封裝跨端組件庫的步驟大致如下:
跨端組件設(shè)計(jì)
根據(jù)實(shí)際需求引入合適的第三方組件
根據(jù)第三方組件文檔,將數(shù)據(jù)處理成符合預(yù)期的結(jié)構(gòu),并在適當(dāng)時(shí)機(jī)拋出事件
編寫必要樣式
一些思考根據(jù)組件多態(tài)文檔, 像indexlist.web.cml、indexlist.wx.cml與indexlist.weex.cml的這些文件是灰度區(qū), 它們是唯一可以調(diào)用下層端能力的CML文件,這里的下層端能力既包含下層端組件,例如在web端和weex端的.vue文件等;也包含下層端的api,例如微信小程序的wx.pageScrollTo等。這一層的存在是為了調(diào)用下層端代碼,各端具體的邏輯實(shí)現(xiàn)應(yīng)該在下層來實(shí)現(xiàn), 這種規(guī)范的好處是顯而易見的: 隨著業(yè)務(wù)復(fù)雜度的提升,各個(gè)下層端維護(hù)的功能逐漸變多,其中通用的部分又可以通過普通cml文件抽離出來被統(tǒng)一調(diào)用,這樣可以保證差異化部分始終是最小集合,灰度區(qū)是存粹的;如果將業(yè)務(wù)邏輯都放在了灰度區(qū),隨著功能復(fù)雜度的上升,三端通用功能/組件就無法達(dá)到合理的抽象,導(dǎo)致灰度層既有相同功能,又有差異化部分,這顯然不是開發(fā)者愿意看到的場(chǎng)景。
在灰度區(qū)的模板、邏輯、樣式和json文件中分別具有如下規(guī)則:
模板
調(diào)用下層組件時(shí),既可以使用chameleon語法,也可以使用各端原生語法;在灰度區(qū)chameleon編譯器不會(huì)編譯各個(gè)端原生語法,例如v-for,bindtap等。建議在模板部分仍然使用chameleon模板語法,只有在實(shí)現(xiàn)對(duì)應(yīng)平臺(tái)不支持的語法(例如web端v-html等)時(shí)才使用原生語法。
引用下層全局組件時(shí)需要添加origin-前綴,這樣可以“告訴”chameleon編譯器是在引用下層的原生組件,chameleon編譯器就不會(huì)對(duì)其進(jìn)行處理了。這種做法同時(shí)解決了組件命名沖突問題,例如在微信小程序端引用
邏輯
在script邏輯代碼中,除了編寫普通cml邏輯代碼之外,開發(fā)者還可以使用下層端的全局變量和任意方法,包括生命周期函數(shù)。這種機(jī)制保證開發(fā)者可以靈活擴(kuò)展各端特有功能,而不需要依賴多態(tài)接口。
樣式
既可以使用cmss語法也可以使用下層端的css語法。
json文件
*web.cml:base.usingComponents可以引入普通cml組件和任意.vue擴(kuò)展名組件,路徑規(guī)則見組件配置。
*wx.cml:base.usingComponents可以引入普通cml組件和普通微信小程序組件,路徑規(guī)則見組件配置。
*weex.cml:base.usingComponents可以引入普通cml組件和任意.vue擴(kuò)展名組件,路徑規(guī)則見組件配置。
在各端對(duì)應(yīng)的灰度區(qū)文件中均可以根據(jù)上述規(guī)范使用各端的原生語法,但是為了規(guī)范仍然建議使用chameleon體系的語法規(guī)則。總體來說,灰度區(qū)可以認(rèn)為是chameleon體系與各端原生組件/方法的銜接點(diǎn),向下使用各端功能/組件,向上通過多態(tài)協(xié)議提供各端統(tǒng)一的調(diào)用接口。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/102465.html
摘要:在編寫跨端組件的正確姿勢(shì)上篇中,我們介紹了如何使用第三方庫封裝跨端組件,但是絕大多數(shù)組件并不需要那樣差異化實(shí)現(xiàn),絕大多數(shù)情況下我們推薦使用語法統(tǒng)一實(shí)現(xiàn)跨端組件。 在chameleon項(xiàng)目中我們實(shí)現(xiàn)一個(gè)跨端組件一般有兩種思路:使用第三方組件封裝與基于chameleon語法統(tǒng)一實(shí)現(xiàn)。在《編寫chameleon跨端組件的正確姿勢(shì)(上篇)》中, 我們介紹了如何使用第三方庫封裝跨端組件,但是絕大...
摘要:基于對(duì)跨端工作的積累,規(guī)范了一套跨端標(biāo)準(zhǔn),稱之為協(xié)議開發(fā)者只需要按照標(biāo)準(zhǔn)擴(kuò)展流程,即可快速擴(kuò)展任意架構(gòu)模式的終端。實(shí)現(xiàn)了微信端的基本擴(kuò)展,用戶可以以此為模板進(jìn)行開發(fā)。新框架太多?學(xué)不動(dòng)啦?有這一套跨端標(biāo)準(zhǔn),今后再也不用學(xué)習(xí)新框架了。各個(gè)小程序按自己喜好各自為政?有了這套標(biāo)準(zhǔn),再也不用重復(fù)開發(fā)各種新平臺(tái)啦。如今前端比較流行的 React Native、Weex、Flutter 等跨平臺(tái)開發(fā)框架...
摘要:但是從年微信推出小程序,到至今各大廠商都推出自己的小程序,跨端開發(fā)就不僅僅是技術(shù)的問題了。實(shí)現(xiàn)了微信端的基本擴(kuò)展,用戶可以以此為模板進(jìn)行開發(fā)。 新框架太多?學(xué)不動(dòng)啦?有這一套跨端標(biāo)準(zhǔn),今后再也不用學(xué)習(xí)新框架了。 各個(gè)小程序按自己喜好各自為政?有了這套標(biāo)準(zhǔn),再也不用重復(fù)開發(fā)各種新平臺(tái)啦。 如今前端比較流行的 React Native、Weex、Flutter 等跨平臺(tái)開發(fā)框架,對(duì)于開發(fā)來...
摘要:中國互聯(lián)網(wǎng)絡(luò)信息中心發(fā)布的中國互聯(lián)網(wǎng)絡(luò)發(fā)展?fàn)顩r統(tǒng)計(jì)報(bào)告顯示,截至年月,我國網(wǎng)民規(guī)模達(dá)億人,微信月活億支付寶月活億百度月活億另一方面,中國手機(jī)占智能手機(jī)整體的比例超過,月活約億。在年末正式發(fā)布了面向未來的跨端的。 開源中國專訪:Chameleon原理首發(fā),其它跨多端統(tǒng)一框架都是假的? 原創(chuàng): 嘉賓-張楠 開源中國 以往我們說某一功能跨多端,往往是指在諸如 PC、移動(dòng)等不同類型的設(shè)備之...
閱讀 2650·2023-04-26 02:47
閱讀 3066·2023-04-26 00:42
閱讀 937·2021-10-12 10:12
閱讀 1459·2021-09-29 09:35
閱讀 1769·2021-09-26 09:55
閱讀 543·2019-08-30 14:00
閱讀 1595·2019-08-29 12:57
閱讀 2417·2019-08-28 18:00