摘要:作為兩個(gè)小程序開(kāi)發(fā)框架都使用過(guò),并應(yīng)用在生產(chǎn)環(huán)境里的人,自然是要比較一下兩者的異同點(diǎn)。在這里與當(dāng)前很流行的小程序開(kāi)發(fā)框架之一進(jìn)行簡(jiǎn)單對(duì)比,主要還是為了方便大家更快速地了解,從而選擇更適合自己的開(kāi)發(fā)方式。
前言
前陣子,來(lái)自我們凹凸實(shí)驗(yàn)室的遵循 React 語(yǔ)法規(guī)范的多端開(kāi)發(fā)方案 - Taro終于對(duì)外開(kāi)源了,歡迎圍觀star(先打波廣告)。作為第一批使用了Taro開(kāi)發(fā)的TOPLIFE小程序的開(kāi)發(fā)人員之一,自然是走了不少?gòu)澛罚闪瞬簧倏?,也幫忙找過(guò)不少bug。現(xiàn)在項(xiàng)目總算是上線(xiàn)了,那么,也是時(shí)候給大家總結(jié)分享下了。
與wepy比較當(dāng)初開(kāi)發(fā)TOPLIFE第一期的時(shí)候,用的其實(shí)是wepy(那時(shí)Taro還沒(méi)有開(kāi)發(fā)完成),然后在第二期才全面轉(zhuǎn)換為用Taro開(kāi)發(fā)。作為兩個(gè)小程序開(kāi)發(fā)框架都使用過(guò),并應(yīng)用在生產(chǎn)環(huán)境里的人,自然是要比較一下兩者的異同點(diǎn)。
相同點(diǎn)組件化開(kāi)發(fā)
npm包支持
ES6+特性支持,Promise,Async Functions等
CSS預(yù)編譯器支持,Sass/Stylus/PostCSS等
支持使用Redux進(jìn)行狀態(tài)管理
…..
相同的地方也不用多說(shuō)什么,都2018年了,這些特性的支持都是為了讓小程序開(kāi)發(fā)變得更現(xiàn)代,更工程化,重點(diǎn)是區(qū)別之處
不同點(diǎn)開(kāi)發(fā)風(fēng)格
實(shí)現(xiàn)原理
wepy支持slot,taro暫不支持直接渲染children
開(kāi)發(fā)風(fēng)格
最大的不同之處,自然就是開(kāi)發(fā)風(fēng)格上的差異,wepy使用的是類(lèi)Vue開(kāi)發(fā)風(fēng)格, Taro使用的是類(lèi)React開(kāi)發(fā)風(fēng)格,可以說(shuō)開(kāi)發(fā)體驗(yàn)上還是會(huì)有較大的區(qū)別。貼一下官方的demo簡(jiǎn)單闡述下
wepy demo
view(class="container") view(class="userinfo" @tap="tap") mycom(:prop.sync="myprop" @fn.user="myevent") text {{now}}
taro demo
import Taro, { Component } from "@tarojs/taro" import { View, Button } from "@tarojs/components" export default class Index extends Component { constructor () { super(...arguments) this.state = { title: "首頁(yè)", list: [1, 2, 3] } } componentWillMount () {} componentDidMount () {} componentWillUpdate (nextProps, nextState) {} componentDidUpdate (prevProps, prevState) {} shouldComponentUpdate (nextProps, nextState) { return true } add = (e) => { // dosth } render () { return () } } {this.state.title} {this.state.list.map(item => { return ( {item} ) })}
可以見(jiàn)到在wepy里,css、template、script都放在一個(gè)wepy文件里,template還支持多種模板引擎語(yǔ)法,然后支持computed、watcher等屬性,這些都是典型的vue風(fēng)格
而在taro里,就是徹頭徹尾的react風(fēng)格,包括constructor,componentWillMount、componentDidMount等各種react的生命周期函數(shù),還有return里返回的jsx,熟悉react的人上手起來(lái)可以說(shuō)是非常快了
除此之外還有一些細(xì)微的差異之處:
wepy里的模板,或者說(shuō)是wxml,用的都是小程序里原生的組件,就是小程序文檔里的各種組件;而taro里使用的每個(gè)組件,都需要從@tarojs/components里引入,包括View,Text等基礎(chǔ)組件(這種做其實(shí)是為了轉(zhuǎn)換多端做準(zhǔn)備)
事件處理上
taro中,是用click事件代替tap事件
wepy使用的是簡(jiǎn)寫(xiě)的寫(xiě)法@+事件;而taro則是on+事件名稱(chēng)
阻止冒泡上wepy用的是@+事件.stop;而taro則是要顯式地使用e.stopPropagation()來(lái)阻止冒泡
事件傳參wepy可以直接在函數(shù)后面?zhèn)鲄?,?b>@tap="click({{index}})";而taro則是使用bind傳參,如onClick={this.handleClick.bind(null, params)}
wepy使用的是小程序原生的生命周期,并且組件有page和component的區(qū)分;taro則是自己實(shí)現(xiàn)了類(lèi)似react的生命周期,而且沒(méi)有page和component的區(qū)分,都是component
總的來(lái)說(shuō),畢竟是兩種不同的開(kāi)發(fā)風(fēng)格,自然還是會(huì)有許多大大小小的差異。在這里與當(dāng)前很流行的小程序開(kāi)發(fā)框架之一wepy進(jìn)行簡(jiǎn)單對(duì)比,主要還是為了方便大家更快速地了解taro,從而選擇更適合自己的開(kāi)發(fā)方式。
實(shí)踐體驗(yàn)taro官方提供的demo是很簡(jiǎn)單的,主要是為了讓大家快速上手,入門(mén)。那么,當(dāng)我們要開(kāi)發(fā)偏大型的項(xiàng)目時(shí),應(yīng)該如何使用taro使得開(kāi)發(fā)體驗(yàn)更好,開(kāi)發(fā)效率更高?作為深度參與TOPLIFE小程序開(kāi)發(fā)的人員之一,談一談我的一些實(shí)踐體驗(yàn)及心得
如何組織代碼使用taro-cli生成模板是這樣的
├── dist 編譯結(jié)果目錄 ├── config 配置目錄 | ├── dev.js 開(kāi)發(fā)時(shí)配置 | ├── index.js 默認(rèn)配置 | └── prod.js 打包時(shí)配置 ├── src 源碼目錄 | ├── pages 頁(yè)面文件目錄 | | ├── index index頁(yè)面目錄 | | | ├── index.js index頁(yè)面邏輯 | | | └── index.css index頁(yè)面樣式 | ├── app.css 項(xiàng)目總通用樣式 | └── app.js 項(xiàng)目入口文件 └── package.json
假如引入了redux,例如我們的項(xiàng)目,目錄是這樣的
├── dist 編譯結(jié)果目錄 ├── config 配置目錄 | ├── dev.js 開(kāi)發(fā)時(shí)配置 | ├── index.js 默認(rèn)配置 | └── prod.js 打包時(shí)配置 ├── src 源碼目錄 | ├── actions redux里的actions | ├── asset 圖片等靜態(tài)資源 | ├── components 組件文件目錄 | ├── constants 存放常量的地方,例如api、一些配置項(xiàng) | ├── reducers redux里的reducers | ├── store redux里的store | ├── utils 存放工具類(lèi)函數(shù) | ├── pages 頁(yè)面文件目錄 | | ├── index index頁(yè)面目錄 | | | ├── index.js index頁(yè)面邏輯 | | | └── index.css index頁(yè)面樣式 | ├── app.css 項(xiàng)目總通用樣式 | └── app.js 項(xiàng)目入口文件 └── package.json
比較常見(jiàn)的一種項(xiàng)目目錄組織方式,相比初始模板多了幾個(gè)文件夾,用于存放redux相關(guān)的內(nèi)容及其他的一些東西,整個(gè)項(xiàng)目結(jié)構(gòu)相信還是比較直觀,簡(jiǎn)單明了的
更好地使用reduxredux大家應(yīng)該都不陌生,一種狀態(tài)管理的庫(kù),通常會(huì)搭配一些中間件使用。我們的項(xiàng)目主要是用了redux-thunk和redux-logger中間件,一個(gè)用于處理異步請(qǐng)求,一個(gè)用于調(diào)試,追蹤actions
相信大家都遇到過(guò)這種時(shí)候,接口返回的數(shù)據(jù)和頁(yè)面顯示的數(shù)據(jù)并不是完全對(duì)應(yīng)的,往往需要再做一層預(yù)處理。那么這個(gè)業(yè)務(wù)邏輯應(yīng)該在哪里管理,是組件內(nèi)部,還是redux的流程里?
舉個(gè)例子:
例如上圖的購(gòu)物車(chē)模塊,接口返回的數(shù)據(jù)是
{ code: 0, data: { shopMap: {...}, // 存放購(gòu)物車(chē)?yán)锷唐返牡赇佇畔⒌膍ap goods: {...}, // 購(gòu)物車(chē)?yán)锏纳唐沸畔? ... } ... }
對(duì)的,購(gòu)車(chē)?yán)锏纳唐返赇伜蜕唐肥欠旁趦蓚€(gè)對(duì)象里面的,但視圖要求它們要顯示在一起。這時(shí)候,如果直接將返回的數(shù)據(jù)存到store,然后在組件內(nèi)部render的時(shí)候東拼西湊,將兩者信息匹配,再做顯示的話(huà),會(huì)顯得組件內(nèi)部的邏輯十分的混亂,不夠純粹。
所以,我個(gè)人比較推薦的做法是,在接口返回?cái)?shù)據(jù)之后,直接將其處理為與頁(yè)面顯示對(duì)應(yīng)的數(shù)據(jù),然后再dispatch處理后的數(shù)據(jù),相當(dāng)于做了一層攔截,像下面這樣:
const data = result.data // result為接口返回的數(shù)據(jù) const cartData = handleCartData(data) // handleCartData為處理數(shù)據(jù)的函數(shù) dispatch({type: "RECEIVE_CART", payload: cartData}) // dispatch處理過(guò)后的函數(shù) ... // handleCartData處理后的數(shù)據(jù) { commoditys: [{ shop: {...}, // 商品店鋪的信息 goods: {...}, // 對(duì)應(yīng)商品信息 }, ...] }
可以見(jiàn)到,處理數(shù)據(jù)的流程在render前被攔截處理了,將對(duì)應(yīng)的商品店鋪和商品放在了一個(gè)對(duì)象了.
這樣做有幾個(gè)好處
一個(gè)是組件的渲染更純粹,在組件內(nèi)部不用再關(guān)心如何將數(shù)據(jù)修修改改而滿(mǎn)足視圖要求,只需關(guān)心組件本身的邏輯,例如點(diǎn)擊事件,用戶(hù)交互等
二是數(shù)據(jù)的流動(dòng)更可控,假如后續(xù)后臺(tái)返回的數(shù)據(jù)有變動(dòng),我們要做的只是改變handleCartData函數(shù)里面的邏輯,不用改動(dòng)組件內(nèi)部的邏輯。
后臺(tái)數(shù)據(jù)——>攔截處理——>期望的數(shù)據(jù)結(jié)構(gòu)——>組件
實(shí)際上,不只是后臺(tái)數(shù)據(jù)返回的時(shí)候,其它數(shù)據(jù)結(jié)構(gòu)需要變動(dòng)的時(shí)候都可以做一層數(shù)據(jù)攔截,攔截的時(shí)機(jī)也可以根據(jù)業(yè)務(wù)邏輯調(diào)整,重點(diǎn)是要讓組件內(nèi)部本身不關(guān)心數(shù)據(jù)與視圖是否對(duì)應(yīng),只專(zhuān)注于內(nèi)部交互的邏輯,這也很符合react本身的初衷,數(shù)據(jù)驅(qū)動(dòng)視圖
connect大家都知道是用來(lái)連接store、actions和組件的,很多時(shí)候就只是根據(jù)樣板代碼復(fù)制一下,改改組件各自的store、actions。實(shí)際上,我們還可以做一些別的處理,例如:
export default connect(({ cart, }) => ({ couponData: cart.couponData, commoditys: cart.commoditys, editSkuData: cart.editSkuData }), (dispatch) => ({ // ...actions綁定 }))(Cart) // 組件里 render () { const isShowCoupon = this.props.couponData.length !== 0 return isShowCoupon &&}
上面是很普通的一種connect寫(xiě)法,然后render函數(shù)根據(jù)couponData里是否數(shù)據(jù)來(lái)渲染。這時(shí)候,我們可以把this.props.couponData.length !== 0這個(gè)判斷丟到connect里,達(dá)成一種computed的效果,如下:
export default connect(({ cart, }) => { const { couponData, commoditys, editSkuData } = cart const isShowCoupon = couponData.length !== 0 return { isShowCoupon, couponData, commoditys, editSkuData }}, (dispatch) => ({ // ...actions綁定 }))(Cart) // 組件里 render () { return this.props.isShowCoupon &&}
可以見(jiàn)到,在connect里定義了isShowCoupon變量,實(shí)現(xiàn)了根據(jù)couponData來(lái)進(jìn)行computed的效果
實(shí)際上,這也是一種數(shù)據(jù)攔截處理。除了computed,還可以實(shí)現(xiàn)其它的功能,具體就由各位看官自由發(fā)揮了
一些需要注意的地方那taro,或者是小程序開(kāi)發(fā),有沒(méi)有什么要注意的地方?當(dāng)然有,走過(guò)的彎路可以說(shuō)是非常多了
估計(jì)是每個(gè)頁(yè)面的數(shù)據(jù)在小程序內(nèi)部都有緩存,所以做了10層的限制。帶來(lái)的問(wèn)題就是假如頁(yè)面存在循環(huán)跳轉(zhuǎn),即A頁(yè)面可以跳到B頁(yè)面,B頁(yè)面也可以跳到A頁(yè)面,然后用戶(hù)從A進(jìn)入了B,想返回A的時(shí)候,往往是直接在B頁(yè)面里點(diǎn)擊跳轉(zhuǎn)到A,而不是點(diǎn)返回回到A,如此一來(lái),10層很快就突破了。所以我們自己對(duì)navigateTo函數(shù)做了一層封裝,防止溢出
上面說(shuō)到,頁(yè)面內(nèi)容有緩存。所以假如某個(gè)頁(yè)面是根據(jù)不同的數(shù)據(jù)渲染視圖,新渲染時(shí)會(huì)有上一次渲染的緩存,導(dǎo)致頁(yè)面看起來(lái)有個(gè)閃爍的變化,用戶(hù)體驗(yàn)非常不好。其實(shí)解決的辦法也很簡(jiǎn)單,每次在componentWillUnmount生命周期中清理一下當(dāng)前頁(yè)面的數(shù)據(jù)就好了。小程序說(shuō)到底不是h5,不會(huì)說(shuō)每次進(jìn)入頁(yè)面就會(huì)刷新,也不會(huì)離開(kāi)就銷(xiāo)毀,刷新,清理數(shù)據(jù)的動(dòng)作都需要自己再生命周期函數(shù)里主動(dòng)觸發(fā)
頁(yè)面的滾動(dòng)事件只能通過(guò)onPageScroll來(lái)監(jiān)聽(tīng),所以當(dāng)我想在組件里進(jìn)監(jiān)聽(tīng)操作時(shí),要將該部分的邏輯提前到onPageScroll函數(shù),提高了抽象成本。例如我需要開(kāi)發(fā)一個(gè)滾動(dòng)到某個(gè)位置就吸頂?shù)?b>tab,本來(lái)可以在tab內(nèi)部處理的邏輯被提前了,減少了其可復(fù)用性
本來(lái)也想詳細(xì)描述下的,不過(guò)在我們幾位大佬的努力,加班加點(diǎn)下,已經(jīng)開(kāi)發(fā)出eslint插件,及補(bǔ)充完整了taro文檔。大家只要遵循eslint插件規(guī)范,查看文檔,應(yīng)該不會(huì)有太大問(wèn)題,有問(wèn)題歡迎提issue
總結(jié)總的來(lái)說(shuō),用taro來(lái)開(kāi)發(fā)小程序體驗(yàn)還是很不錯(cuò)的,最重要的是,可以使用jsx寫(xiě)小程序了?。?!作為react粉的一員,可以說(shuō)是相當(dāng)?shù)呐d奮了~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/95813.html
摘要:讓人又愛(ài)又恨的微信小程序自微信小程序以下簡(jiǎn)稱(chēng)小程序誕生以來(lái),就伴隨著贊譽(yù)與爭(zhēng)議不斷。同時(shí)于開(kāi)發(fā)者來(lái)說(shuō),小程序的生態(tài)不斷在完善,許多的坑已被踩平,雖然還是存在一些令人詬病的問(wèn)題,但已經(jīng)足見(jiàn)微信的誠(chéng)意了。 Taro 介紹 在互聯(lián)網(wǎng)不斷發(fā)展的今天,前端程序員們也不斷面臨著新的挑戰(zhàn),在這個(gè)變化多端、不斷革新自己的領(lǐng)域,每一年都有新的美好事物在發(fā)生。從去年微信小程序的誕生,到今年的逐漸火熱,以及...
摘要:多端統(tǒng)一開(kāi)發(fā)框架優(yōu)秀學(xué)習(xí)資源匯總官方資源項(xiàng)目倉(cāng)庫(kù)官方文檔項(xiàng)目倉(cāng)庫(kù)官方文檔微信小程序官方文檔百度智能小程序官方文檔支付寶小程序官方文檔字節(jié)跳動(dòng)小程序官方文檔文章教程不敢閱讀包源碼帶你揭秘背后的哲學(xué)從到構(gòu)建適配不同端微信小程序等的應(yīng)用小程序最 Awesome Taro 多端統(tǒng)一開(kāi)發(fā)框架 Taro 優(yōu)秀學(xué)習(xí)資源匯總 showImg(https://segmentfault.com/img/r...
摘要:所以在小程序出現(xiàn)之后,一股框架之風(fēng)也很快的出現(xiàn),微信小程序剛推出之后,就出現(xiàn)了兩個(gè)比較出名的小程序開(kāi)發(fā)框架,。 原文地址:https://ant-move.github.io/we... 這里說(shuō)的去除小程序框架其實(shí)并不嚴(yán)謹(jǐn),因?yàn)樾〕绦虮旧硪菜闶且粋€(gè)框架,而且是一個(gè)功能更加完善的框架系統(tǒng)。在前端的概念中,我們一般說(shuō)一個(gè)框架是指一個(gè)用來(lái)幫助開(kāi)發(fā)者構(gòu)建用戶(hù)界面的框架,而小程序框架本身不僅僅包...
摘要:整個(gè)小程序所有分包大小不超過(guò)單個(gè)分包主包大小不能超過(guò)微信小程序主流框架對(duì)比應(yīng)該算是最早發(fā)布的小程序開(kāi)發(fā)框架,提供了類(lèi)的語(yǔ)法風(fēng)格和特性,現(xiàn)階段應(yīng)該也是應(yīng)用最廣泛的框架吧。不過(guò)微信官方為了防止下載離線(xiàn)包的時(shí)間過(guò)程,也嚴(yán)格限制了小程序包的體積。 那些年我們踩過(guò)的坑css樣式不能引用本地圖片資源,只能引用線(xiàn)上資源(background-image),引用本地圖片資源只能用標(biāo)簽。{{}}不能執(zhí)行...
摘要:前言是一個(gè)可以很好實(shí)現(xiàn)一次開(kāi)發(fā),多端統(tǒng)一的框架,本文只介紹它小程序端開(kāi)發(fā)的一些內(nèi)容。 前言:taro是一個(gè)可以很好實(shí)現(xiàn)一次開(kāi)發(fā),多端統(tǒng)一的框架,本文只介紹它小程序端開(kāi)發(fā)的一些內(nèi)容。小程序項(xiàng)目搭建gitup已經(jīng)有很清楚的說(shuō)明:https://github.com/NervJS/taro 一.主要操作步驟及命令: 1.cnpm install -g @tarojs/cli 全局安裝taro...
閱讀 1261·2021-09-04 16:41
閱讀 2423·2021-09-02 10:18
閱讀 927·2019-08-29 16:40
閱讀 2623·2019-08-29 16:14
閱讀 914·2019-08-26 13:41
閱讀 1308·2019-08-26 12:24
閱讀 739·2019-08-26 10:24
閱讀 2879·2019-08-23 17:54