從跨端到開放式跨端跨框架
承載 Web 的主戰場轉移—
2017 年 1 月 9 日凌晨,微信正式推出小程序,爲移動端家族增加了新的業務形態和玩法,當大家還在探討這一新興平臺能做什麼的時候,京東率先上線了「京東購物」小程序,隨後,更多的電商行業執牛耳者紛紛入駐小程序,從此,承載電商的主戰場逐漸從需要自建流量的移動端 APP 向小程序傾斜。
小程序的出現,爲電商行業的研發帶來了巨大的挑戰。繼微信之後越來越多的頭部流量互聯網公司紛紛盯上小程序這一蛋糕,相繼推出了各自的小程序平臺,比如京東、阿里、百度、字節跳動、360 等等,爲了讓我們的電商業務能快速移植到這些小程序平臺,幫助我們的業務快速拓展渠道,我們開始了新的嘗試。
我們開始嘗試使用技術的手段,探索一種能夠統一所有平臺的新技術。
披荊斬棘——初代架構誕生—
用 React 寫小程序?
前面有提到,爲了解決各大小程序平臺帶來的多端開發的痛點問題,社區先湧現出了 WePy[1] 和 mpvue[2],那我們爲什麼不直接採用,而要選擇 “造輪子” 呢?
在當時的前端界言及前端框架,必離不開依然保持着統治地位的 React[3] 與 Vue[4],這兩個都是非常優秀的前端 UI 框架,而且在網上也經常能看到兩個框架的粉絲之間熱情交流,碰撞出一些思想火花,讓社區異常活躍。
而我們團隊也在 2017 年勇敢地拋棄了歷史包袱,非常榮幸的引入了 React 技術棧。這讓我們團隊丟掉了煤油燈,開始通上了電,遠離了刀耕火種的前端開發方式。爲了解決當時業務環境對極致性能以及低版本 IE 瀏覽器兼容性的要求,我們還研發出了一款優秀的類 React 框架 Nerv[5] ,並因此對 React 開發思想以及技術棧理解更加深刻。
遺憾的是,當時社區並沒有一款使用 React 開發小程序的框架。
與小程序的開發方式相比,React 明顯顯得更加現代化、規範化,而且 React 天生組件化更適合我們的業務開發,JSX 也比字符串模板有更強的表現力。那時候我們開始思考,我們能不能用 React 來寫小程序?[6]
理性地探索
通過對比體驗 小程序和 React ,我們還是能發現兩者之間相似的地方,比如生命週期、數據更新方式以及事件綁定,都具有非常多相似的地方,這爲我們使用 React 來小程序提供了非常良好的基礎。
但是,我們也應該看到小程序和 React 之間的巨大的差異,那就是模板。在 React
中,是使用 JSX
來作爲組件的模板的,而小程序則與 Vue
一樣,是使用字符串模板的。這是兩種完全不一樣的東西,也是我們方案探索上的巨大障礙。所以,爲了實現使用 React 來寫小程序這一目標,我們必須解決兩者之間巨大差異的問題。
解決差異
既然微信不支持 JSX
,那我們只需要將 JSX
編譯成小程序模板不久可以在微信上運行了嗎,這一步可以通過 Babel[7] 來實現。
Babel
作爲一個 代碼編譯器
,能夠將 ES6/7/8 的代碼編譯成 ES5 的代碼,其的編譯過程主要包含三個階段:
-
解析過程,在這個過程中進行詞法、語法分析,以及語義分析,生成符合 ESTree 標準 [8] 虛擬語法樹 (AST)
-
轉換過程,針對 AST 做出已定義好的操作,
babel
的配置文件.babelrc
中定義的preset
、plugin
就是在這一步中執行並改變 AST 的 -
生成過程,將前一步轉換好的 AST 生成目標代碼的字符串
再次回到我們的需求,將 JSX
編譯成小程序模板,非常幸運的是 babel
的核心編譯器 babylon
是支持對 JSX
語法的解析的,我們可以直接利用它來幫我們構造 AST,而我們需要專注的核心就是如何對 AST 進行轉換操作,得出我們需要的新 AST,再將新 AST 進行遞歸遍歷,生成小程序的模板。
以上僅僅是我們轉換規則的冰山一角,JSX
的寫法極其靈活多變,我們只能通過窮舉的方式,將常用的、React 官方推薦的寫法作爲轉換規則加以支持。
初代架構誕生
經過我們一次次的探索,我們已經可以將類 React 代碼轉成可以在小程序環境運行的代碼了。但是我們激動之餘,冷靜下來繼續思考,我們還能不能幹點別的有意思的事情呢。
我們發現,在平常的工作中,我們業務通常有一些 “多端” 的需求。就是同一個業務或頁面,需要同時適配 小程序、H5 、甚至 React Native 。這個時候,你就會發現,差不多的界面和邏輯,你可能需要重複寫上好幾輪。
因此,我們希望希望在解決使用 React 開發微信小程序的同時,還能同時是適配到 H5 端、移動端、以及各平臺的小程序。Write once, run anywhere
,相信是所有工程師的夢想。
但是仔細思考我們又會發現,僅僅將代碼按照對應語法規則轉換過去後,還遠遠不夠,因爲不同端會有自己的原生組件,端能力 API 等等,代碼直接轉換過去後,並不能直接執行,還需要運行時的適配。因此,我們按照微信小程序的組件庫與 API 的標準,在其他端(H5、RN)分別實現了組件庫和 API 庫。
Taro 1 架構
Taro 從立項之初到架構穩定差不多用了三個月左右的時間,從最初的激烈討論方案,各種思想的碰撞,到方案逐漸成型,進入火熱的開發迭代,再到現在的多個平臺小程序端、H5 端和 RN 端的順利支持,Taro 的未來已來。
初露鋒芒——GitHub 開源—
2018 年 6 月 7 日,多端統一開發框架 - Taro 正式開源。
作爲首個支持以 React 語法寫微信小程序並適配到多端的開發框架,Taro 一鳴驚人。開源不到兩個月,在 GitHub 上有 6600 多個 Star,連續數週霸榜 Github Trending;同時已經處理近 300 個 Issue ,還有 100 多個在等待反饋與優化;在公司內、外主動反饋的使用 Taro 的項目已有十多個。
截至 2019 年 12 月 18 日,Taro 已擁有 22254 Stars 和 250 名 Contributors,社區主動提交的開發案例 150+:taro-user-cases[9],其中不乏多端案例。
截止 2021 年 1 月 11 日,Taro 已擁有 27914 Star ,位列小程序開發框架前列。
Taro 團隊不斷跟進社區裏提出的問題和反饋,一直保持着高速迭代,並圍繞 多端適配、開發體驗以及社區共建 三個方面持續優化 Taro 框架。
爲了方便開發者快速開發項目,我們基於 Taro 推出了業內首款小程序多端組件庫 Taro UI[10],
Taro UI 二維碼
在多端適配方面,我們一直持續跟進,成爲社區適配端最多的小程序開發框架。
爲了更好的開發體驗,我們支持了 React Hooks、CSS Modules、Mobx 等。
在社區共建方面,我們推出了 Taro 論壇、Taro 物料市場等平臺,後面發佈了 社區共建計劃 [11]。
在跟進業務的同時,我們還需要不斷跟進社區裏提出的問題和反饋,因而就要不斷加班加點地去完成,雖然有時會覺得很累,但是技術上的成就感以及能幫助到更多開發者時的滿足感還是不斷地激勵着我們前進,讓 Taro 項目越來越好。
篳路藍縷——一段痛苦摸索的升級之旅—
Taro 在 GitHub 上開源之後雖然收穫了非常多的讚譽,短短的時間內就突破了 10000 Stars,但由於 Taro 初期的自定義組件架構設計得非常複雜,導致使用組件開發的時候總會引起非常多的問題,一直爲許多用戶詬病。
在 Taro 設計的初期,由於微信小程序剛推出的自定義組件功能並不完善,實現不了傳入自定義函數等問題,無法滿足組件化靈活使用的需求,所以 Taro 的組件化架構是採用 template 標籤來實現的。
使用 template 方案來實現的組件化架構在通常情況下運行良好,但面對複雜循環嵌套輸出組件的時候則問題頻出,主要原因是:
-
JS 邏輯與模板隔離,需要分別處理,導致組件傳參非常麻煩,難以對齊
-
template 實現的自定義組件無法嵌套子組件
所以,在 Taro 最初的時光,自定義組件的問題一直是我們抹不去的痛,作爲官方團隊,在痛苦思索解決方案的同時,還要面對社區不斷的問題反饋和質疑,讓我們總覺得前途一片灰暗。
但前人的經驗告訴我們,不能就此放棄,魯迅先生曾經說過「此後如竟沒有炬火,我便是唯一的光」。我們在 template 方案掙扎良久之後,終於還是將目光投向了小程序自帶的自定義組件身上。
正所謂山窮水復疑無路,柳暗花明又一村,小程序的自定義組件恰好有了一波更新,經過數個日夜加班探索之後,之前困擾我們的問題都得到了一一解決,完美實現了新的自定義組件架構,帶來了更加穩定的版本。
在新的架構中,我們會把 Taro 的組件直接編譯成小程序的原生組件的 Component
方法調用,再通過運行時的適配,來對組件參數、生命週期適配、以及事件傳入等進行處理,藉助小程序的組件化能力來實現 Taro 的組件處理。
經過這一版方案重構之後,Taro 的穩定性與可靠性得到了質的飛躍,社區的好評聲不斷,而 Taro 的關注數也得到陡峭的增長。這一版架構方案也成了 Taro 持續時間最久的方案,爲 Taro 日後成爲「一款值得信賴」的多端方案打下了堅實的基礎。
這是一段篳路藍縷的艱苦創業之旅,對於 Taro 團隊來說也是一段非常寶貴的經驗。沒有一直一帆風順的旅途,唯有不輕言放棄和勇於開拓才能雲開見日,讓我們走的更遠。
高歌猛進——不斷地突破自我—
在完成了自定義組件架構的改造之後,Taro 開始了全速發展之路。
在 2018 年 11 月份,Taro 推出了 1.1 版本,完成了百度、支付寶小程序的適配支持,成爲業界首個同時適配多個小程序平臺的多端開發框架,並且在適配期間,Taro 團隊和百度、支付寶小程序官方團隊建立了聯繫,爲對方小程序的發展提出了非常多的建設性意見。與此同時,Taro 成爲百度小程序官方推薦使用框架之一。
而短短一個月之後,2018 年 12 月份,Taro 火速推出了 1.2 版本,這是一個非常具有創新意義的版本,在這個版本中,Taro 首創了小程序原生代碼反向轉換技術,能夠將小程序原生代碼反向轉換成 Taro 代碼。原有的微信小程序通過 Taro 轉換,就能快速移植到其他平臺,這爲業務的快速擴張提供了巨大的想象空間,爲業務的高效交付提供了極大的助力。
而「京東快遞」業務正是反向轉換特性成功應用的一個典範案例。最初「京東快遞」只有微信小程序平臺,通過 Taro 的反向轉換特性,以極低的成本快速移植到百度、頭條小程序平臺,並且可以只維護一份代碼,同時維護 3 端應用,極大地降低了維護成本。
在 Taro 1.2 發佈之後,Taro 在業界收穫了巨大的讚譽和關注:GitHub 上 Stars 數量超過 19000 粒,NPM 下載量也穩居同類開發框架之首,同時 Taro 團隊也和騰訊、百度、華爲等數十家業界巨頭的研發團隊展開了深入和有效的合作。
時間匆匆而逝,來到了 2019 年 6 月,時隔半年,我們終於發佈了 Taro 1.3,這是我們醞釀最久的版本:經歷了橫跨 6 個月的開發時間,近 2000 次的代碼提交,同時有近百位開發者的共同參與。在 Taro 1.3 中,我們不僅僅帶來了對 QQ 小程序的支持,同時還支持了快應用,成爲業界支持平臺最多的多端開發框架,而更重要的是,在這個版本中我們成功將業界火熱的 React Hooks 搬到了 Taro 中,支持讓用戶使用 React Hooks 的方式來開發小程序,成爲業界首創。
從 Taro 正式版發佈,到 Taro 1.3,可以看出 Taro 一直不斷使用創新的理念打磨自我,以創新爲使命,爲業界帶來體驗優異的多端開發解決方案。
擁抱變化——爲未來思考—
儘管 Taro 一直保持超高的迭代速度,但自從自定義組件架構改造之後,Taro 的整體架構設計沒有發生太大變化,這讓 Taro 在這個時刻在變化的時代稍顯佛系,且對於一個時刻想要突破自己的技術團隊來說,常規性質的維護工作,顯然無法安撫我們躁動的心,畢竟人的夢想,是永遠不會停止的,所以我們決定啓動一系列的顛覆式重構設計。
我們首先從 CLI 開始入手進行改造,衆所周知,原來 Taro CLI 的編譯構建系統是自研的,整個構建系統邏輯複雜,要考慮的邊際條件衆多,這就導致了以下問題:
-
維護困難,每次需要新增一個功能,例如支持解析 Markdown 文件,就需要直接改動 CLI,不夠靈活
-
難以共建,CLI 的代碼非常複雜,而且邏輯分支衆多,讓很多想要一起共建的人難以入手
-
可擴展性偏低,自研的構建系統,設計之初沒有考慮到後續的擴展性,導致開發者想要添加自定義的功能無從下手
基於以上問題,我們決定使用 Webpack 來實現編譯構建,於是誕生了 2.0。
Taro 2.0 的 CLI 將會變得非常輕量,只會做區分編譯平臺、處理不同平臺編譯入參等操作,隨後再調用對應平臺的 runner 編譯器 做代碼編譯操作,而原來大量的 AST 語法操作將會改造成 Webpack Plugin 以及 Loader,交給 Webpack 來處理。
相較於舊的構建系統,新的小程序編譯帶來了以下優勢:
-
利於維護,大量的邏輯交由 Webpack 來處理,我們只需要維護一些插件
-
更加穩定,相較於自研的構建系統,新的構建會更加穩定,降低一些奇怪錯誤的出現概率
-
可擴展性強,可以通過自行加入 Webpack Loader 與 Plugin 的方式做自己想要的擴展
-
各端編譯統一,接入 Webpack 後,Taro 各端的編譯配置可以實現非常大程度的統一
可以看到新的構建系統會有很大的進步。同時,更重要的是,基於 Webpack,我們可以在小程序中嘗試更多的特性與技術,例如通過 tree shaking 來優化代碼包大小等等,讓小程序開發更加與業界發展同步,讓 Taro 更加擁抱社區。
Taro 2.0 只是一個開始。
在 10 年代最後一場 GMTC 全球大前端技術大會上,Taro 團隊向大家展示了 小程序跨框架開發的探索與實踐 [12] 的艱辛旅程,同時也提前曝光了正在緊密開發中的 Taro Next。
那是一個完全區別於以往的版本,一條與現在 Taro 截然不同的道路,預示着 Taro 正在革自己的命。
節物風光不相待,桑田碧海須臾改。
20 年代呼嘯而來,下一個 10 年,很多框架都會死去,很多技術也會煥然而生,沒有什麼是不變的,唯一不變的只有變化,我們能做的也只能是擁抱變化,爲未來思考。
乘風破浪——新架構起航—
正如前文所提到的,Taro 迎來了全新的架構。
不同於 Taro 1、2 時代的架構,新的架構主要基於運行時,我們都知道使用 React 開發 web,渲染頁面主要依靠的是 react-dom 去操作 DOM 樹,而 React Native 依靠的是 Yoga 佈局引擎,但是我們卻能通過 React 將他們聯繫在一起,這主要是通過抽象的 Virtual DOM 技術來實現的,通過 Virtual DOM 達到跨平臺統一的目的。而小程序中雖然沒有直接暴露 DOM 和 BOM API,但是我們卻可以類比 React 和 React Native 的思想,在小程序中模擬實現 DOM 以及 BOM 的 API,從而實現直接將 React 運行到小程序環境中的目的,這就是 Taro 新架構的由來。
在新架構加持下,Taro 不再僅侷限於 React 陣營,可以不再限制使用的框架語法,而 Taro 官方內置了 React、Nerv、Vue2、Vue3 四種框架的支持。
Taro2 到 Taro3,是一次技術的躍遷,也是一次創新的勝利,更是 Taro 團隊不斷探索,永不止步精神的體現。Taro 這艘大船又重新楊帆起航,帶着初心再次出發。
乘風破浪破浪會有時,直掛雲帆濟滄海。
衆擎易舉——開放式架構—
自 2020 年 7 月初 Taro 正式發佈了 Taro 3,至 12 月半年時間已然略去。期間我們不斷地修復着問題,同時也在構想着下一個 minor 版本。
面對小程序平臺越來越多的大環境,Taro 是選擇偏安一隅,只支持部分的主流小程序,還是成爲所有小程序平臺 開發、多端轉換的基礎設施,Taro 在 v3.1 給出了答案:開放式架構。
基於 Taro 3 的架構,對於單一平臺的兼容性代碼分佈於 Taro 核心庫的各個角落,涉及編譯時與運行時等部分。支持一個新的平臺需要改動所有的這些地方,開發複雜度高,同時社區也難以參與貢獻。
因此我們萌生了打造一個開放式框架的想法。目標是可以通過插件的形式擴展 Taro 的端平臺支持能力:
-
插件開發者無需修改 Taro 核心庫代碼,即可編寫出一個端平臺插件。
-
插件使用者只需安裝、配置端平臺插件,即可把代碼編譯到指定平臺。
-
開發者可以繼承現有的端平臺插件,然後對平臺的適配邏輯進行自定義。
Taro 3.1 架構圖
我們把內置支持的 6 個平臺封裝成了插件,CLI 默認會全部加載這些插件。封裝的過程中,我們檢索了各小程序最新的組件、API,並全數進行更新與支持,對齊各小程序最新的能力。而藉助開放式架構,我們編寫了若干端平臺插件,開發者安裝後即可使用。
結語—
開源不易,貴在堅持。Taro 一路走來,有衆多開發者相伴,進入過 中國活躍度 Top 5 的開源項目 [13],像河水不斷奔湧向前的 Taro 既爭先也爭滔滔不絕。Taro 團隊衷心感謝各位參與過本項目開源建設的朋友,無論是爲 Taro 提交過代碼、建設周邊生態,還是反饋過問題,甚至只是茶餘飯後討論、吐槽 Taro 的各位。
參考資料
[1]
WePy: https://github.com/Tencent/wepy
[2]
mpvue: http://mpvue.com/
[3]
React: https://zh-hans.reactjs.org/
[4]
Vue: https://vuejs.org/
[5]
Nerv: https://github.com/NervJS/nerv
[6]
那時候我們開始思考,我們能不能用 React 來寫小程序?: https://aotu.io/notes/2018/06/25/the-birth-of-taro/
[7]
Babel: https://www.babeljs.cn/
[8]
ESTree 標準: https://github.com/estree/estree
[9]
taro-user-cases: https://taro-docs.jd.com/taro/showcase
[10]
Taro UI: https://github.com/NervJS/taro-ui
[11]
社區共建計劃: https://taro-docs.jd.com/taro/docs/join-in
[12]
小程序跨框架開發的探索與實踐: https://taro-docs.jd.com/taro/blog/2020-01-02-gmtc
[13]
中國活躍度 Top 5 的開源項目: https://www.infoq.cn/article/dCY0AHH71rBBjq3pIfh7
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aGX_rFMCi5JGvZv7UjcSHA