前言:隨著小程序開發(fā)的熱度上升,小程序開發(fā)框架也層出不窮。但目前每個框架都會綁定一個專屬 DSL,如類 React 或者類 Vue,在一個框架內(nèi),開發(fā)者無法根據(jù)團(tuán)隊(duì)技術(shù)棧自由選擇 DSL,同時也無法共享框架本身的生態(tài)與工具。
本次分享將為大家介紹 Taro 如何將各種語法的前端框架運(yùn)行在小程序上,討論一個框架支持多 DSL 的實(shí)現(xiàn)探索,使得開發(fā)者可以使用任意熱門框架/語法/DSL 來編寫小程序應(yīng)用,同時復(fù)用相關(guān)生態(tài)。
2017 年 1 月 9 日凌晨,萬眾期待的微信小程序正式上線。
在此之前,京東投入一個前端小團(tuán)隊(duì),經(jīng)過一個月的封閉式開發(fā),以一周一個版本的速度進(jìn)行迭代,終于在第一時間發(fā)布了自己的 「京東購物」 小程序,盡管功能和界面現(xiàn)在看起來有些簡陋,但在當(dāng)時是完全符合微信小程序「觸手可及,用完即走」的理念。
當(dāng)然,隨著整個項(xiàng)目的不斷迭代,現(xiàn)在的 「京東購物」小程序在設(shè)計(jì)、交互以及功能復(fù)雜度已經(jīng)全面向 APP 端看齊,這里面的工程化實(shí)踐已經(jīng)由 劉慧敏 老師在 GMTC 全球大前端技術(shù)大會(北京站)2019[1] 進(jìn)行過分享,有興趣的可以下載 PPT:京東購物小程序工程化之路[2]。
當(dāng)時的微信小程序的開發(fā)存在一些缺點(diǎn),比如依賴管理混亂、工程化流程落后、ES Next 支持不完善、命名規(guī)范不統(tǒng)一等。這些問題在現(xiàn)在看來都已經(jīng)有了各種官方或非官方的解決辦法,但是在當(dāng)時小程序開發(fā)的探索階段,這些問題都是一些痛點(diǎn)問題。
有句話我個人特別喜歡,那就是「當(dāng)一門語言的能力不足,而用戶的運(yùn)行環(huán)境又不支持其它選擇的時候,這門語言就會淪為 “編譯目標(biāo)” 語言」。
縱觀整個前端的歷史,無論是 CSS 預(yù)處理器的大行其道、各種模版的流行,還是 CoffeeScript 乃至 TypeScript[3] 的誕生,都印證了這個說法,微信小程序這里也不例外。因此,各種小程序開發(fā)框架如百花齊放,層出不窮。
這些小程序開發(fā)框架最主要的區(qū)別是 DSL,這點(diǎn)從 logo 顏色上就可以看出來,除了滴滴的 Chameleon[4] 是自定義 DSL 外,其余的綠色的 logo 是遵循了 Vue 語法(如 mpvue ),藍(lán)色的 logo 是遵循了 React 的語法(如 Taro)。
在微信小程序之后,各大廠商紛紛發(fā)布了自己的小程序平臺,比如:支付寶、百度、頭條、QQ 等,再加上快應(yīng)用、網(wǎng)易、360、京東等,小程序的賽道越來越擁擠,開發(fā)人員需要適配的小程序平臺越來越多,因此,各大小程序開發(fā)框架也紛紛進(jìn)行了多端適配。
因此,站在這個時間節(jié)點(diǎn)反過來回顧整個小程序開發(fā)框架的進(jìn)程,你會發(fā)現(xiàn)整個 2018 年乃至 2019 年初,小程序的開發(fā)框架主要的區(qū)別和重心在于:DSL 以及 多端適配。
正所謂「業(yè)務(wù)孵化技術(shù),技術(shù)服務(wù)業(yè)務(wù)」,Taro[5] 的誕生源自于業(yè)務(wù)需求的增加,當(dāng)時我們的團(tuán)隊(duì)需要同時負(fù)責(zé):京東購物,TOPLIFE 等業(yè)。團(tuán)隊(duì)人力資源捉襟見肘,與此同時,以上的業(yè)務(wù)都或多或少存在多端的需求,比如 微信小程序、H5、React Native(京東的主流 APP 基本都內(nèi)置了 React Native 渲染引擎),而且可以預(yù)見的是,以后很有可能需要適配更多的小程序平臺,而每個端開發(fā)一套代碼又不現(xiàn)實(shí),會導(dǎo)致:研發(fā)成本上升,代碼維護(hù)困難。
當(dāng)時我們團(tuán)隊(duì)自研了一款 類 React 框架:Nervjs[6], 整個團(tuán)隊(duì)的技術(shù)棧因此全部轉(zhuǎn)向了 React ,而當(dāng)時市面上又沒有一款遵循 React 語法的小程序框架,因此,我們開發(fā)了 Taro,希望能夠使用 React 語法寫小程序的同時,通過「Write once Run anywhere」來實(shí)現(xiàn)跨端的。
整個 Taro 框架從 2018 年 6 月 7 日開源至今,一致保持著高速迭代,這些迭代主要集中在三個方面:
經(jīng)過團(tuán)隊(duì) 一年多的努力,Taro 得到了社區(qū)的廣泛認(rèn)可,截止 2019 年 12 月 18 日,Taro 已擁有 22254 Stars 和 250 名 Contributors,社區(qū)主動提交的開發(fā)案例 150+:taro-user-cases[10],其中不乏多端案例。
但是盡管如此,Taro 還是存在一些問題無法解決,或者說:沒那么好解決。比如:和 React DSL
強(qiáng)綁定、JSX 適配工作量大、社區(qū)貢獻(xiàn)復(fù)雜等。這些問題歸根到底,很大一部分是 Taro 的架構(gòu)問題。
因此我們團(tuán)隊(duì)也一直在等待一次合適的機(jī)會,對整個架構(gòu)進(jìn)行一次提升,同時修復(fù)一些項(xiàng)目快速迭代欠下的技術(shù)債。
最主要的是,單純的項(xiàng)目維護(hù)迭代已經(jīng)滿足不了我們團(tuán)隊(duì)躁動的心,我們渴望借此機(jī)會進(jìn)行一次技術(shù)突破。
在講 Taro 架構(gòu)之前,我們先來回顧一下小程序的架構(gòu)。
微信小程序主要分為 邏輯層 和 視圖層,以及在他們之下的原生部分。邏輯層主要負(fù)責(zé) JS 運(yùn)行,視圖層主要負(fù)責(zé)頁面的渲染,它們之間主要通過 Event
和 Data
進(jìn)行通信,同時通過 JSBridge
調(diào)用原生的 API。這也是以微信小程序?yàn)槭椎拇蠖鄶?shù)小程序的架構(gòu)。
由于原生部分對于前端開發(fā)者來說就像是一個黑盒,因此,整個架構(gòu)圖的原生部分可以省略。同時,我們我們對 邏輯層 和 視圖層 也做一下簡化,最后可以得到小程序架構(gòu)圖的極簡版:
也就是說,只需要在邏輯層調(diào)用對應(yīng)的 App()/Page()
方法,且在方法里面處理 data、提供生命周期/事件函數(shù)等,同時在視圖層提供對應(yīng)的模版及樣式供渲染就能運(yùn)行小程序了。這也是大多數(shù)小程序開發(fā)框架重點(diǎn)考慮和處理的部分。
Taro 當(dāng)前的架構(gòu)主要分為:編譯時 和 運(yùn)行時。
其中編譯時主要是將 Taro 代碼通過 Babel[11] 轉(zhuǎn)換成 小程序的代碼,如:JS
、WXML
、WXSS
、JSON
。
運(yùn)行時主要是進(jìn)行一些:生命周期、事件、data 等部分的處理和對接。
有過 Babel 插件開發(fā)經(jīng)驗(yàn)的應(yīng)該對一下流程十分熟悉,Taro 的編譯時也是遵循了此流程,使用 babel-parser[12] 將 Taro 代碼解析成抽象語法樹,然后通過 babel-types[13] 對抽象語法樹進(jìn)行一系列修改、轉(zhuǎn)換操作,最后再通過 babel-generate[14] 生成對應(yīng)的目標(biāo)代碼。
詳情可以參考:babel-handbook[15]
整個編譯時最復(fù)雜的部分在于 JSX 編譯。
我們都知道 JSX 是一個 JavaScript 的語法擴(kuò)展,它的寫法千變?nèi)f化,十分靈活。這里我們是采用 窮舉 的方式對 JSX 可能的寫法進(jìn)行了一一適配,這一部分工作量很大,實(shí)際上 Taro 有大量的 Commit 都是為了更完善的支持 JSX 的各種寫法。
但盡管如此,我們也不可能完全覆蓋所有的情況,因此還是推薦大家按照官方規(guī)范書寫 React 代碼,同時,我們也提供了豐富的 ESlint 插件來輔助大家書寫規(guī)范的代碼。
這一塊我們團(tuán)隊(duì)內(nèi)部一直有個梗:如果你使用 Taro 開發(fā)感覺 Bug 少,那說明你的 React 代碼寫得很規(guī)范。
接下來,我們可以對比一下編譯后的代碼,可以發(fā)現(xiàn),編譯后的代碼中,React 的核心 render 方法 沒有了。同時代碼里增加了 BaseComponent
和 createComponent
,它們是 Taro 運(yùn)行時的核心。
// 編譯前
import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
import './index.scss'
export default class Index extends Component {
config = {
navigationBarTitleText: '首頁'
}
componentDidMount () { }
render () {
return (
<View className=‘index' onClick={this.onClick}>
<Text>Hello world!</Text>
</View>
)
}
}
// 編譯后
import {BaseComponent, createComponent} from '@tarojs/taro-weapp'
class Index extends BaseComponent {
// ...
_createDate(){
//process state and props
}
}
export default createComponent(Index)
BaseComponent
大概的 UML 圖如下,主要是對 React 的一些核心方法:setState
、forceUpdate
等進(jìn)行了替換和重寫,結(jié)合前面編譯后 render 方法被替換,大家不難猜出:Taro 當(dāng)前架構(gòu)只是在開發(fā)時遵循了 React 的語法,在代碼編譯之后實(shí)際運(yùn)行時,和 React 并沒有關(guān)系。
而 createComponent
主要作用是調(diào)用 Component()
構(gòu)建頁面;對接事件、生命周期等;進(jìn)行 Diff Data
并調(diào)用 setData
方法更新數(shù)據(jù)。
因此,整個 Taro 當(dāng)前架構(gòu)的特點(diǎn)是:
小程序開發(fā)框架百花齊放,我們也從社區(qū)里得到了不少啟發(fā)。
接下來我們來看看 遵循 vue 語法的小程序開發(fā)框架的代表:mpvue[16] 是怎樣實(shí)現(xiàn)的。
看過 Vue 源碼的同學(xué)對上面的文件夾和架構(gòu)肯定熟悉,本質(zhì)上,mpvue 就是 fork 了一份 vuejs/vue@2.4.1
的代碼,保留了 Vue runtime 能力,同時添加了小程序平臺的支持。
具體在源碼中的表現(xiàn)就是:在 Vue 源碼的 platforms 文件夾下面增加了 mp 目錄,在里面實(shí)現(xiàn)了 complier(編譯時)
和 runtime (運(yùn)行時)
支持。
mpvue 的實(shí)現(xiàn)同樣分為:編譯時和運(yùn)行時。
其中編譯時做的事情和 Taro 很類似:將 Vue SFC 寫法的代碼編譯成 小程序代碼文件(JS、WXML、WXSS、JSON)。
最大的區(qū)別是 Taro 將 JSX 編譯成 小程序模版,而 mpvue 是將 Vue 模版編譯成 小程序模版。但是由于 Vue 模版和 小程序模版的相似性,mpvue 在這一塊的工作量比 Taro 少得多。
而 mpvue 的運(yùn)行時和 Vue 的運(yùn)行時是強(qiáng)關(guān)聯(lián)的,首先我們來看看 Vue 的運(yùn)行時。
一個 .vue 的單文件由三部分構(gòu)成: template
, script
, style
。
橙色路徑部分, template 會在編譯的過程中,在 vue-loader
中通過 ast 進(jìn)行分析,最終生成一段 render 函數(shù),執(zhí)行 render 函數(shù)會生成虛擬 dom 樹,虛擬 DOM 樹是對真實(shí) DOM 樹的抽象,樹中的節(jié)點(diǎn)被稱作 vnode 。
Vue 拿到 虛擬 DOM 樹之后,就可以去和上次老的 虛擬 DOM 樹 做 patch diff
對比。patch 階段之后,vue 就會使用真實(shí)的操作 DOM 的方法(比如說 insertBefore
, appendChild
之類的),去操作 DOM 結(jié)點(diǎn),更新視圖。
同時,綠色路徑的部分,在實(shí)例化 Vue 的時候,會對數(shù)據(jù) data 做響應(yīng)式的處理,在監(jiān)測到 data 發(fā)生改變時,會調(diào)用 render 函數(shù),生成最新的虛擬 DOM 樹, 接著對比老的虛擬 DOM 樹進(jìn)行 patch, 找出最小修改代價的 vnode 節(jié)點(diǎn)進(jìn)行修改。
而 mpvue 的運(yùn)行時,會首先將 patch 階段的 DOM 操作相關(guān)方法置空,也就是什么都不做。其次,在創(chuàng)建 Vue 實(shí)例的同時,還會偷偷的調(diào)用 Page()
用于生成了小程序的 page 實(shí)例。然后 運(yùn)行時的 patch 階段會直接調(diào)用 $updateDataToMp()
方法,這個方法會獲取掛在在 page 實(shí)例上維護(hù)的數(shù)據(jù) ,然后通過 setData
方法更新到視圖層。
mpvue 整體原理圖也就如下:
因此,和 Taro 重編譯時輕運(yùn)行時不同,mpvue 算是:半編譯時,半運(yùn)行時。這點(diǎn)從代碼量的對比也能大致反映出來。
mpvue 的 WXML 模版和 Taro 一樣,也是通過代碼編譯得到的;不同于 Taro 運(yùn)行時和 React 無關(guān),mpvue 本質(zhì)上還是將 Vue 運(yùn)行在了小程序,且實(shí)現(xiàn)了 Vue@2.4.1
絕大部分特性(只有極少數(shù)特性由于小程序模版的限制未能實(shí)現(xiàn),如 :filter
、slot
、v-html
);且整個框架基于 Webpack 實(shí)現(xiàn)了較為完善的工程化。
其他小程序框架的實(shí)現(xiàn)原理和效果上的差異性,也帶來了我們的一些思考:
這一次,我們站在瀏覽器的角度來思考前端的本質(zhì):無論開發(fā)這是用的是什么框架,React 也好,Vue 也罷,最終代碼經(jīng)過運(yùn)行之后都是調(diào)用了瀏覽器的那幾個 BOM/DOM 的 API ,如:createElement
、appendChild
、removeChild
等。
因此,我們創(chuàng)建了 taro-runtime[18] 的包,然后在這個包中實(shí)現(xiàn)了 一套 高效、精簡版的 DOM/BOM API(下面的 UML 圖只是反映了幾個主要的類的結(jié)構(gòu)和關(guān)系):
然后,我們通過 Webpack 的 ProvidePlugin[19] 插件,注入到小程序的邏輯層。
這樣,在小程序的運(yùn)行時,就有了 一套高效、精簡版的 DOM/BOM API。
在 DOM/BOM
注入之后,理論上來說,Nerv/Preact 就可以直接運(yùn)行了。但是 React 有點(diǎn)特殊,因?yàn)?React-DOM
包含大量瀏覽器兼容類的代碼,導(dǎo)致包太大,而這部分代碼我們是不需要的,因此我們需要做一些定制和優(yōu)化。
在 React 16+ ,React 的架構(gòu)如下:
最上層是 React 的核心部分 react-core
,中間是 react-reconciler
,其的職責(zé)是維護(hù) VirtualDOM
樹,內(nèi)部實(shí)現(xiàn)了 Diff/Fiber
算法,決定什么時候更新、以及要更新什么。
而 Renderer
負(fù)責(zé)具體平臺的渲染工作,它會提供宿主組件、處理事件等等。例如 React-DOM
就是一個渲染器,負(fù)責(zé) DOM 節(jié)點(diǎn)的渲染和 DOM 事件處理。
因此,我們實(shí)現(xiàn)了 taro-react[20] 包,用來連接 react-reconciler
和 taro-runtime
的 BOM/DOM API:
具體的實(shí)現(xiàn)主要分為兩步:
react-reconciler
的 hostConfig
配置,即在 hostConfig
的方法中調(diào)用對應(yīng)的 Taro BOM/DOM 的 API。ReactDOM.render
)方法,可以看成是創(chuàng)建 Taro DOM Tree
的容器。經(jīng)過上面的步驟,React 代碼實(shí)際上就可以在小程序的運(yùn)行時正常運(yùn)行了,并且會生成 Taro DOM Tree
,那么偌大的 Taro DOM Tree 怎樣更新到頁面呢?
首先,我們將小程序的所有組件挨個進(jìn)行模版化處理,從而得到小程序組件對應(yīng)的模版,如下圖就是小程序的 view 組件經(jīng)過模版化處理后的樣子:
然后,我們會:基于組件的 template,動態(tài) “遞歸” 渲染整棵樹。
具體流程為先去遍歷 Taro DOM Tree
根節(jié)點(diǎn)的子元素,再根據(jù)每個子元素的類型選擇對應(yīng)的模板來渲染子元素,然后在每個模板中我們又會去遍歷當(dāng)前元素的子元素,以此把整個節(jié)點(diǎn)樹遞歸遍歷出來。
整個 Taro Next 的 React 實(shí)現(xiàn)流程圖如下:
別看 React 和 Vue 在開發(fā)時區(qū)別那么大,其實(shí)在實(shí)現(xiàn)了 BOM/DOM API 之后,它們之間的區(qū)別就很小了。
Vue 和 React 最大的區(qū)別就在于運(yùn)行時的 CreateVuePage
方法,這個方法里進(jìn)行了一些運(yùn)行時的處理,比如:生命周期的對齊。
其他的部分,如通過 BOM/DOM 方法構(gòu)建、修改 DOM Tree 及渲染原理,都是和 React 一致的。
提到 Flutter ,就不得不提 Flutter Web[21] ,Flutter Web 是在標(biāo)準(zhǔn)瀏覽器 API 之上實(shí)現(xiàn) Flutter 的核心繪圖層,本質(zhì)上也是最終調(diào)用了 BOM/DOM API。因此,理論來說,也是可以進(jìn)行適配的,但這一塊我們并不會投入太多的精力,最終會像快應(yīng)用一樣交給社區(qū)來實(shí)現(xiàn)和維護(hù)。
接下來和大家展開聊一下 Taro Next 更多的細(xì)節(jié)實(shí)現(xiàn),比如:事件、更新、生命周期。
首先的 Taro Next 事件,具體的實(shí)現(xiàn)方式如下:
bindtap
、bindchange
、bindsubmit
等。eventHandler
函數(shù),和 eh 方法綁定,收集所有的小程序事件document.getElementById()
方法獲取觸發(fā)事件對應(yīng)的 TaroNode
createEvent()
創(chuàng)建符合規(guī)范的 TaroEvent
TaroNode.dispatchEvent
重新觸發(fā)事件可以看到,Taro Next 事件本質(zhì)上是基于 Taro DOM 實(shí)現(xiàn)了一套自己的事件機(jī)制,這樣做的好處之一是,無論小程序是否支持事件的冒泡與捕獲,Taro 都能支持。
無論是 React 還是 Vue ,最終都會調(diào)用 Taro DOM 方法,如:appendChild
、insertChild
等。
這些方法在修改 Taro DOM Tree 的同時,還會調(diào)用 enqueueUpdate
方法,這個方法能獲取到每一個 DOM 方法最終修改的節(jié)點(diǎn)路徑和值,如:{root.cn.[0].cn.[4].value: '1'}
,并通過 setData
方法更新到視圖層。
可以看到,這里更新的粒度是 DOM 級別,只有最終發(fā)生改變的 DOM 才會被更新過去,相對于之前 data 級別的更新會更加精準(zhǔn),性能更好。
相對與其他部分大刀闊斧的升級改造,生命周期可能是變動最小的部分之一。和之前類似,生命周期的實(shí)現(xiàn)是在運(yùn)行時維護(hù)的 App 實(shí)例 / Page 實(shí)例進(jìn)行了生命周期方法的一一對應(yīng)。
const config: PageInstance = {
onLoad (this: MpInstance, options) {
//...
},
onUnload () {
//...
},
onShow () {
safeExecute('onShow')
},
onHide () {
safeExecute('onHide')
},
onPullDownRefresh () {
safeExecute('onPullDownRefresh')
}
//...
}
和之前的架構(gòu)不同,Taro Next 是 近乎全運(yùn)行。
新的架構(gòu)基本解決了之前的遺留問題:
前面提到,同等條件下,編譯時做的工作越多,也就意味著運(yùn)行時做的工作越少,性能會更好。Taro Next 的新架構(gòu)變成 近乎全運(yùn)行 之后,花了很多精力在性能優(yōu)化上面。
再這之前??梢韵瓤匆幌?Taro Next 的流程和原生小程序的流程對比。
可以發(fā)現(xiàn),相比原生小程序,Taro Next 多了紅色部分的帶來的性能隱患,如:引入 React/Vue 帶來的 包的 Size 增加,運(yùn)行時的損耗、Taro DOM Tree 的構(gòu)建和更新、DOM data 初始化和更新。
而我們真正能做的,只有綠色部分,也就是:Taro DOM Tree 的構(gòu)建和更新、DOM data 初始化和更新。
首先我們來看包 Size,下面的表格是 TodoMVC 的例子,在原生、Taro Old、Taro Next 等情況下的包大小對比,可以看到,引入 React/Vue 后,包大小在 Gzip 情況下大概增加了 30k 左右。
不過我們在前面一再強(qiáng)調(diào):和之前模版通過編譯生成的不同,Taro Next 的模版是固定的,然后基于組件的 template,動態(tài) “遞歸” 渲染整棵 Taro DOM 樹。也就是說,Taro Next 的 WXML 大小是有上限的。
隨著項(xiàng)目的增加,頁面越來越多,原生的項(xiàng)目 WXML 體積會不斷增加,而 Taro Next 不會。也就是說,當(dāng)頁面的數(shù)量超過一個臨界點(diǎn)時,Taro Next 的包體積可能會更小。因此,包 Size 的問題不足為慮。
在 Taro DOM Tree 的構(gòu)建和更新階段,我們實(shí)現(xiàn)了一套僅實(shí)現(xiàn)了高效的、精簡版 DOM/BOM API,而且僅僅實(shí)現(xiàn)了必要的。
Github 上有一個倉庫 jsdom[22],基本上是在 Node.js 上實(shí)現(xiàn)了一套 Web 標(biāo)準(zhǔn)的 DOM/BOM ,這個倉庫的代碼在壓縮前大概有 2.1M,而 Taro Next 的核心的 DOM/BOM API 代碼才 1000 行不到。
因此,我們最大限度的保證了 Taro DOM Tree 構(gòu)建和更新階段的性能。
在數(shù)據(jù)更新階段,首先前面有提到過,Taro Next 的更新是 DOM 級別的,比 Data 級別的更新更加高效,因?yàn)?Data 粒度更新實(shí)際上是有冗余的,并不是所有的 Data 的改變最后都會引起 DOM 的更新。
其次,Taro 在更新的時候?qū)?Taro DOM Tree 的 path
進(jìn)行壓縮,這點(diǎn)也極大的提升了性能。
最終的結(jié)果是:在某些業(yè)務(wù)場景寫,add
、select
數(shù)據(jù),Taro Next 的性能比原生的還要好。
當(dāng)然,實(shí)驗(yàn)的數(shù)據(jù)總歸會有缺陷,最終具體的性能表現(xiàn),還要靠各種復(fù)雜業(yè)務(wù)場景的檢驗(yàn)。大家如果對 Taro Next 的性能感興趣的,可以自行跑一下 taro-benchmark[23] 包,對比一下結(jié)果。
我們也在一直持續(xù)的全方位優(yōu)化 Taro Next 的性能,具體可以關(guān)注 Taro Next 的最新的 Commit 。
Taro Next 將會在不久之后的 3.0 版本正式發(fā)布,支持使用 React/Vue 開發(fā)跨端小程序,然后在會在后續(xù)的迭代中拓展至其他端,并完善對應(yīng)的生態(tài)。
Taro 團(tuán)隊(duì)還是會將支持的重點(diǎn)放在 React/Vue,F(xiàn)lutter 和 Angular 會像快應(yīng)用一樣,交給社區(qū)來適配和維護(hù),快應(yīng)用就是華為的 Qiyu8[24] 和 Issacpeng[25] 在幫我們進(jìn)行適配,非常感謝他們。
同時,我們還打造了 「Taro 移動端一站式研發(fā)平臺」,將先前積累的多端開發(fā)工作流和工程化的方案進(jìn)行了統(tǒng)一,并內(nèi)置了數(shù)據(jù)監(jiān)控、組件市場以及可視化搭建,當(dāng)前正處于內(nèi)測階段。
Learn Once Write AnyWhere
,而我們 Taro 的口號是 Write Once Run AnyWhere
,這一點(diǎn)也經(jīng)常導(dǎo)致我們經(jīng)常被人噴,這里說一點(diǎn)我自己的想法:Learn Once Write AnyWhere
其實(shí)本質(zhì)上對開發(fā)者更友好,比如開發(fā)者只需要學(xué)習(xí) React 技術(shù)棧,就可以開發(fā) Web/移動端 應(yīng)用,但是對項(xiàng)目就沒那么友好了,每個項(xiàng)目都得維護(hù)一份代碼;而 Write Once Run AnyWhere
是對開發(fā)者沒那么友好(適配的端越多,適配的成本必然也會水漲船高,對開發(fā)者要求也很變高),但是根據(jù)我們的實(shí)踐,對項(xiàng)目會更友好,「一套代碼,多端適配」。當(dāng)然,這里適配的粒度,并不一定是項(xiàng)目級別的,其實(shí)在我們的具體實(shí)踐中,有相當(dāng)一部分是:業(yè)務(wù)級甚至是頁面級的。正所謂「單絲不成線,獨(dú)木不成林」,Taro 發(fā)展至今早已不在屬于單一團(tuán)隊(duì)的項(xiàng)目了,而是整個 Taro 開發(fā)社區(qū)共同的項(xiàng)目。
最后,還是借此機(jī)會感謝一些社區(qū)所有幫助過 Taro 的成長的人,特別是 Taro 的貢獻(xiàn)者們,非常感謝!
同時也感謝受邀成為 TaroUI[26] 核心維護(hù)人員的 Garfield550[27] (小姐姐)、梁音[28]、ShaoQian Liu[29],他們將支撐起 TaroUI 的后續(xù)迭代與維護(hù)。
當(dāng)然還有在社區(qū)中樂于助人、積極貢獻(xiàn)的 zacksleo[30] 、Jay Fong[31]、loveonelong[32]、lolipop99[33]、波仔糕[34]、原罪[35]、lentoo[36] 、白領(lǐng)夏公子[37] 、YuanQuan[38]、 tourze[39]、 lingxiaoZhu[40] 等等。
此外,還要感謝一直默默為 Taro 發(fā)展提供寶貴建議的研發(fā)團(tuán)隊(duì):騰訊云、數(shù)字廣東、騰訊 CDC、網(wǎng)易嚴(yán)選、華為開源團(tuán)隊(duì)、招聯(lián)消費(fèi)金融等等。
長風(fēng)破浪會有時,直掛云帆濟(jì)滄海。
歡迎關(guān)注凹凸實(shí)驗(yàn)室博客:aotu.io[41]
或者關(guān)注凹凸實(shí)驗(yàn)室公眾號(AOTULabs),不定時推送文章:
GMTC 全球大前端技術(shù)大會(北京站)2019: https://gmtc.infoq.cn/2019/beijing/
[2]京東購物小程序工程化之路: http://ppt.geekbang.org/slide/download?cid=42&pid=2416
[3]TypeScript: https://www.typescriptlang.org/
[4]Chameleon: https://github.com/didi/chameleon
[5]Taro: https://github.com/NervJS/taro
[6]Nervjs: https://github.com/NervJS/nerv
[7]Taro 論壇: https://taro-club.jd.com/
[8]Taro物料市場: https://taro-ext.jd.com/
[9]社區(qū)共建計(jì)劃: https://taro-club.jd.com/topic/680/taro-%E9%82%80%E4%BD%A0%E5%8A%A0%E5%85%A5%E7%A4%BE%E5%8C%BA%E5%85%B1%E5%BB%BA-%E7%A4%BE%E5%8C%BA%E5%85%B1%E5%BB%BA%E5%80%A1%E8%AE%AE%E4%B9%A6
[10]taro-user-cases: https://github.com/NervJS/taro-user-cases
[11]Babel: https://babeljs.io/
[12]babel-parser: https://babeljs.io/docs/en/babel-parser
[13]babel-types: https://babeljs.io/docs/en/babel-types
[14]babel-generate: https://babeljs.io/docs/en/babel-generator
[15]babel-handbook: https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md
[16]mpvue: https://github.com/Meituan-Dianping/mpvue
[17]Remax: https://github.com/remaxjs/remax
[18]taro-runtime: https://github.com/NervJS/taro/tree/next/packages/taro-runtime
[19]ProvidePlugin: https://webpack.js.org/plugins/provide-plugin/
[20]taro-react: https://github.com/NervJS/taro/tree/next/packages/taro-react
[21]Flutter Web: https://flutter.dev/web
[22]jsdom: https://github.com/jsdom/jsdom
[23]taro-benchmark: https://github.com/NervJS/taro-benchmark
[24]Qiyu8: https://github.com/Qiyu8
[25]Issacpeng: https://github.com/Issacpeng
[26]TaroUI: https://github.com/NervJS/taro-ui
[27]Garfield550: https://github.com/Garfield550
[28]梁音: https://github.com/yinLiangDream
[29]ShaoQian Liu: https://github.com/lsqy
[30]zacksleo: https://github.com/zacksleo
[31]Jay Fong: https://github.com/fjc0k
[32]loveonelong: https://github.com/loveonelong
[33]lolipop99: https://github.com/lolipop99
[34]波仔糕: https://github.com/bozaigao
[35]原罪: https://github.com/fwh1990
[36]lentoo: https://github.com/lentoo/
[37]白領(lǐng)夏公子: https://taro-club.jd.com/user/%E7%99%BD%E9%A2%86%E5%A4%8F%E5%85%AC%E5%AD%90
[38]YuanQuan: https://github.com/YuanQuan
[39]tourze: https://github.com/tourze
[40]lingxiaoZhu: https://github.com/lingxiao-Zhu
[41]aotu.io: https://aotu.io/
聯(lián)系客服