剖析美團網站前端的組件化開發(fā)經驗

前言
一位計算機前輩曾說過:
Controlling complexity is the essence of computer programming.
隨著前端開發(fā)復雜度的日益提升,組件化開發(fā)應運而生,并隨著 FIS、React 等優(yōu)秀框架的出現遍地開花。這一過程同樣發(fā)生在美團,面臨業(yè)務規(guī)模的快速發(fā)展和工程師團隊的不斷擴張,美團歷經引入組件化解決資源整合問題、逐步增強組件功能促進開發(fā)效率、重新打造新一代組件化方案適應全棧開發(fā)和共享共建等階段,努力“controlling complexity”。本文將介紹美團組件化開發(fā)的實踐過程。
組件化 1.0:資源重組
在美團早期,前端資源是按照頁面或者類似業(yè)務頁面集合的形式進行組織的。例如 order.js 對應訂單相關頁面的交互,account.css 對應賬戶相關頁面的樣式。這種方式在過去的較長一段時間內,持續(xù)支撐了整個項目的正常推進,功勛卓著。
隨著業(yè)務規(guī)模的增加和開發(fā)團隊的擴張,這套機制逐漸顯示出它的一些不足:
1.資源冗余
頁面的逐漸增加,交互的逐漸復雜化,導致對應的 css 和 js 都有大幅度增長,進而出現為了依賴某個 js 中的一個函數,需要加載整個模塊,或者為了使用某個 css 中的部分樣式依賴整個 css,冗余資源較多
2.對應關系不直觀
沒有顯而易見的對應規(guī)則,導致的一個問題是修改某個業(yè)務模塊的 css 或者 js 時,幾乎只能依靠 grep??咳藖砭S護頁面模塊 html、css 和 js 之間的依賴關系,容易犯錯,常常出現內容已經刪除但是 css 或 js 還存在的問題。
3.難于單元測試
以頁面為最小粒度進行資源整合,不同功能的業(yè)務模塊相互影響,復雜度太高,自動化測試難以推進。
2013 年開始,在調研了 FIS、BEM 等方案之后,結合美團開發(fā)框架的實際,美團初步實現了一套輕量級的組件化開發(fā)方案。主要的改進是:
1.以頁面功能組件為單位聚合前端資源
2.自動加載符合約定的 css、js 資源
3.將業(yè)務數據到渲染數據的轉換過程獨立出來
舉例來說,美團頂部的搜索框就被實現為一個組件。
代碼構成:
www/component/smart-box/
├── smart-box.js # 交互
├── smart-box.php # 渲染數據生產、組件配置
├── smart-box.scss # 樣式
├── smart-box.tpl # 內容
└── test
├── default.js # 自動化測試
└── default.php #
調用組件變得十足簡單:
- echo View::useComponent('smart-box', [
- 'keyword' => $keyword
- ]);
對比之前,可以看到組件化的一些特點:
1.按需加載
只加載必要的前端資源
2.對應關系非常清晰
組件所需要的前端資源都在同一目錄,職責明確且唯一,對應關系顯著
3.易于測試
組件是具備獨立展現和交互的最小單元,可利用 Phantom 等工具自動化測試
此外,由于前端資源集中進行調度,組件化也為高階性能優(yōu)化提供了空間。例如實現組件級別的 BigRender、通過數據分析進行資源的合并加載等等。
組件化 2.0:趨于成熟
組件化 1.0 上線后,由于簡單易用,很快得到工程師的認可,并開始在各項業(yè)務中應用起來。新的需求接踵而來,一直持續(xù)到 2014 年底,這個階段美團稱之為組件化 2.0。下面介紹下主要的幾個改進。
Lifecycle
組件在高內聚的同時,往往需要暴露一些接口供外界調用,從而能夠適應復雜的頁面需求,例如提交訂單頁面需要在支付密碼組件啟動完成后綁定提交時的檢查。Web Components、React 等都選擇了生命周期事件/方法,美團也是一樣。
組件的生命周期:
一個組件的完整生命周期包括:
1.init,初始化組件根節(jié)點和配置
2.fetch,加載 css 和 js 資源
3.render,內容渲染,默認的渲染內容方式是 BigRender
4.ready,進行數據綁定等操作
5.update,數據更新
6.destroy,解除所有事件監(jiān)聽,刪除所有組件節(jié)點
組件提供 pause、resume 方法以方便進行生命周期控制。各個階段使用 Promise 串行進行,異步的管理更清晰。使用自定義語義事件,在修改默認行為、組件間通信上充分利用了 YUI 強大的自定義事件體系,有效降低了開發(fā)維護成本。
舉個例子,頁面初始化時組件的啟動過程實際也是借助生命周期實現的:
- var afterLoadList = [];
- Y.all('[data-component]').each(function (node) {
- var component = new Y.mt.Component(node);
- // 綁定 init 生命周期事件,在 init 默認行為完成后執(zhí)行回調
- component.after('init', function (e) {
- // 如果配置了延遲啟動
- if (e.config.afterLoad) {
- // 暫停組件生命周期
- e.component.pause();
- // 壓入延遲啟動數組
- afterLoadList.push(e.component);
- }
- });
- // 開始進入生命周期
- component.start();
- });
- Y.on('load', function () {
- // 在頁面 load 事件發(fā)生時恢復組件生命周期
- afterLoadList.forEach(function (component) {
- component.resume();
- });
- });
回過頭來看,引入生命周期除了帶來擴展性外,更重要的是理順了組件的各個階段,有助于更好的理解和運用。
Data Binding
數據綁定是美團期盼已久的功能,將 View 和 ViewModel 之間的交互自動化無疑會節(jié)省工程師的大量時間。在組件化減少關注點和降低復雜度后,實現數據綁定變得更加可能。
美團最終實現的數據綁定方案主要參考了 Angular,通過在 html 節(jié)點上添加特定的屬性聲明綁定邏輯,js 掃描這些內容并進行相應的渲染和事件綁定。當數據發(fā)生變化時,對應的內容全部重新渲染。
- <ul class="addressList">
- <li
- mt-bind-repeat="addr in addrList"
- mt-bind-html="addr.text"
- >
- </li>
- </ul>
- <script>
- Y.use(['mt-bind', 'mt-scope'], function () {
- Y.mt.bind.init(document.body);
- var scope = Y.one('.addressList').getScope();
- // 將 scope.addrList 設置為一個數組,DOM 上將自動渲染其內容
- scope.$set('addrList', [
- { text: "first address" },
- { text: "second address" }
- ]);
- });
- </script>
使用屬性聲明綁定邏輯的好處是可以同時支持后端渲染,這對于美團團購這樣的偏展現型業(yè)務是非常必要的,用戶可以很快看到頁面內容。
Flux
實現數據綁定后,美團不得不面對另外一個問題:如何協同多個組件間的數據。因為某個組件的數據變化,很有可能引起其他組件的變化。例如當修改購買數量,總金額會變化,而總金額超過 500 后,還需要展示大額消費提醒。
為了解決這個問題,美團引入了 Flux,使用全局消息總線的思路進行跨組件交互。
例如因為交互復雜而一直讓美團非常頭疼的項目購買頁,在應用組件 + Flux 重構后,各模塊之間的互動更加清晰:
其他方面的改進還有很多,包括引入模板引擎 LightnCandy 約束模板邏輯、支持組件任意嵌套、支持異步加載并自動初始化等。
隨著組件化 2.0 的逐步完善,基本已經可以從容應對日常開發(fā),在效率和質量方面都上了一個臺階。
組件化 3.0:重啟征程
時間的車輪滾滾前行,2014 年底,美團遇到一些新的機遇和挑戰(zhàn):
基于 Node 的全棧開發(fā)模式開始應用,前后端渲染有了更多的可能性
YUI 停止維護,需要一套新的資源管理方案
新業(yè)務不斷增加,需要找到一種組件共享的方式,避免重復造輪子
結合之前的實踐,以及在這一過程中逐漸積累的對業(yè)內方案的認知,美團提出了新的組件化方案:
基于 React 開發(fā)頁面組件,使用 NPM 進行分發(fā),方便共建共享
基于 Browserify 二次開發(fā),建設資源打包工具 Reduce,方便瀏覽器加載
建設適應組件化開發(fā)模式的工程化開發(fā)方案 Turbo,方便工程師將組件應用于業(yè)務開發(fā)中
React
在組件化 2.0 的過程中,美團發(fā)現很多功能和 React 重合,例如 Data Binding、Lifecycle、前后端渲染,甚至直接借鑒的 Flux。除此之外,React 的函數式編程思想、增量更新、兼容性良好的事件體系也讓美團非常向往。借著前端全棧開發(fā)的契機,美團開始考慮基于 React 進行組件化 3.0 的建設。
NPM + Reduce
NPM + Reduce 構成了美團新的資源管理方案,其中:
NPM 負責組件的發(fā)布和安裝??梢哉J為是“分”的過程,粒度越小,重用的可能性越大
Reduce 負責將頁面資源進行打包??梢哉J為是“合”的過程,讓瀏覽器更快地加載
一個典型的組件包:
smart-box/
├── package.json # 組件包元信息
├── smart-box.jsx # React Component
├── smart-box.scss # 樣式
└── test
└── main.js # 測試
NPM 默認只支持 js 文件的管理,美團對 NPM 中的 package.json 進行了擴展,增加了 style 字段,以使打包工具 Reduce 也能夠對 css 和 css 中引用的 image、font 進行識別和處理:
- {
- "style": "./smart-box.scss"
- }
只要在頁面中 require 了 smart-box,經過 Reduce 打包后,js、css 甚至圖片、字體,都會出現在瀏覽器中。
- var SmartBox = require('@mtfe/smart-box');
- // 頁面
- var IndexPage = React.createClass({
- render: function () {
- return (
- <Header>
- <SmartBox keyword={ this.props.keyword } />
- </Header>
- ...
- );
- }
- });
- module.exports = IndexPage;
整體思路和組件化 1.0 如出一轍,卻又那么不同。
Turbo
單單解決分發(fā)和打包的問題還不夠,業(yè)務開發(fā)過程如果變得繁瑣、難以 Debug、性能低下的話,恐怕不會受到工程師歡迎。
為了解決這些問題,美團在 Node 框架的基礎上,提供了一系列中間件和開發(fā)工具,逐步構建對組件友好的前端工程化方案 Turbo。主要有:
1.支持前后端同構渲染,讓用戶更早看到內容
2.簡化 Flux 流程,數據流更加清晰易維護
3.引入 ImmutableJS,保證 Store 以外的數據不可變
4.采用 cursor 機制,保證數據修改/獲取同步
5.支持 Hot Module Replacement,改進開發(fā)流自動化
6.通過這些改進,一線工程師可以方便的使用各種組件,專注在業(yè)務本身上。開發(fā)框架層面的支持也反過來促進了組件化的發(fā)展,大家更樂于使用一系列組件來構建頁面功能。
小結
發(fā)現痛點、分析調研、應用改進的解決問題思路在組件化開發(fā)實踐中不斷運用。歷經三個大版本的演進,組件化開發(fā)模式有效緩解了業(yè)務發(fā)展帶來的復雜度提升的壓力,并培養(yǎng)工程師具備小而美的工程思想,形成共建共享的良好氛圍。毫無疑問,組件化這種“分而治之”的思想將會長久地影響和促進前端開發(fā)模式。美團現在已經準備好,迎接新的機遇和挑戰(zhàn),用技術的不斷革新提升工程師的幸福感。
相關文章
- 這篇文章主要介紹了美團網站搜索排序解決方案分享,分為線上篇和線下篇來講,深入剖析了美團將大數據排序進行優(yōu)化的架構,十分推薦!需要的朋友可以參考下2016-03-25
- 這篇文章主要介紹了美團點評對于網站性能優(yōu)化的經驗總結,根據美團點評的項目總結出了一些網站構件原則及一些實際案例的分享,需要的朋友可以參考下2016-03-23
- 這篇文章主要介紹了美團內部所采用的網站壓力測試方案,包括其對開源壓力測試工具的應用以及其相關流量拷貝等功能的使用,,需要的朋友可以參考下2016-03-23
美團酒店服務使用Node.js實現JavaScript全棧開發(fā)的經驗分享
這篇文章主要介紹了美團酒店服務使用Node.js實現JavaScript全棧開發(fā)的經驗分享,前端js+數據庫MongoDB+后端Node的JavaScript一站式開發(fā)是時下的熱門建站方案,需要的朋友可以2016-03-08- 美團錢包怎么綁定銀行卡消費?現在美團錢包已經可以使用了,使用美團團購的時候我們可以直接使用美團錢包支付,這時候就需要綁定銀行卡或者支付寶,下面我們來看看詳細的綁2016-02-28
- 美團錢包怎么使用直接掃碼付款?美團支付已經上線,現在用戶可以使用掃一掃付款碼就可以使用銀行卡付款了,下面我們就來看看美團錢包掃碼付款的詳細使用方法,需要的朋友可2016-02-28
- 基于HTML5實現仿美團網信息篩選條件特效源碼,這是一款仿蘋果iPhone手機版美團網信息篩選條件代碼,有興趣的朋友直接下載源碼哦2015-12-23
剖析美團的以Python為主導的云平臺發(fā)展戰(zhàn)略
這篇文章主要介紹了美團的以Python為主導的云計算發(fā)展戰(zhàn)略,美團對于Python的Django和Tornado框架的應用著實令人眼前一亮,是為國內Python技術的一大主要推動力量,需要的朋友2015-12-16- 美團積分怎么獲得?美團積分有什么作用,用美團積分可以兌換其他禮物,也可以用來抽獎,積分越高獲得的東西就越珍貴,但是怎么獲取積分呢?下面分享美團積分的使用方法2015-12-14
- 這篇文章主要介紹了簡介Docker在美團網站服務器上的應用,Docker是當下最為火爆的容器類虛擬化技術,需要的朋友可以參考下2015-12-05