日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

前端組件化開發(fā)實(shí)踐

 方海龍的書館 2016-01-19

前言

一位計(jì)算機(jī)前輩曾說過:

Controlling complexity is the essence of computer programming.

隨著前端開發(fā)復(fù)雜度的日益提升,組件化開發(fā)應(yīng)運(yùn)而生,并隨著 FIS、React 等優(yōu)秀框架的出現(xiàn)遍地開花。這一過程同樣發(fā)生在美團(tuán),面臨業(yè)務(wù)規(guī)模的快速發(fā)展和工程師團(tuán)隊(duì)的不斷擴(kuò)張,我們歷經(jīng)引入組件化解決資源整合問題、逐步增強(qiáng)組件功能促進(jìn)開發(fā)效率、重新打造新一代組件化方案適應(yīng)全棧開發(fā)和共享共建等階段,努力“controlling complexity”。本文將介紹我們組件化開發(fā)的實(shí)踐過程。

組件化 1.0:資源重組

在美團(tuán)早期,前端資源是按照頁面或者類似業(yè)務(wù)頁面集合的形式進(jìn)行組織的。例如 order.js 對(duì)應(yīng)訂單相關(guān)頁面的交互,account.css 對(duì)應(yīng)賬戶相關(guān)頁面的樣式。這種方式在過去的較長(zhǎng)一段時(shí)間內(nèi),持續(xù)支撐了整個(gè)項(xiàng)目的正常推進(jìn),功勛卓著。

legacy-flow

隨著業(yè)務(wù)規(guī)模的增加和開發(fā)團(tuán)隊(duì)的擴(kuò)張,這套機(jī)制逐漸顯示出它的一些不足:

  • 資源冗余

    頁面的逐漸增加,交互的逐漸復(fù)雜化,導(dǎo)致對(duì)應(yīng)的 css 和 js 都有大幅度增長(zhǎng),進(jìn)而出現(xiàn)為了依賴某個(gè) js 中的一個(gè)函數(shù),需要加載整個(gè)模塊,或者為了使用某個(gè) css 中的部分樣式依賴整個(gè) css,冗余資源較多

  • 對(duì)應(yīng)關(guān)系不直觀

    沒有顯而易見的對(duì)應(yīng)規(guī)則,導(dǎo)致的一個(gè)問題是修改某個(gè)業(yè)務(wù)模塊的 css 或者 js 時(shí),幾乎只能依靠 grep。靠人來維護(hù)頁面模塊 html、css 和 js 之間的依賴關(guān)系,容易犯錯(cuò),常常出現(xiàn)內(nèi)容已經(jīng)刪除但是 css 或 js 還存在的問題

  • 難于單元測(cè)試

    以頁面為最小粒度進(jìn)行資源整合,不同功能的業(yè)務(wù)模塊相互影響,復(fù)雜度太高,自動(dòng)化測(cè)試難以推進(jìn)

2013 年開始,在調(diào)研了 FIS、BEM 等方案之后,結(jié)合美團(tuán)開發(fā)框架的實(shí)際,我們初步實(shí)現(xiàn)了一套輕量級(jí)的組件化開發(fā)方案。主要的改進(jìn)是:

  • 以頁面功能組件為單位聚合前端資源
  • 自動(dòng)加載符合約定的 css、js 資源
  • 將業(yè)務(wù)數(shù)據(jù)到渲染數(shù)據(jù)的轉(zhuǎn)換過程獨(dú)立出來
component-flow

舉例來說,美團(tuán)頂部的搜索框就被實(shí)現(xiàn)為一個(gè)組件。

smart-box

代碼構(gòu)成:

www/component/smart-box/
├── smart-box.js    # 交互
├── smart-box.php   # 渲染數(shù)據(jù)生產(chǎn)、組件配置
├── smart-box.scss  # 樣式
├── smart-box.tpl   # 內(nèi)容
└── test
    ├── default.js  # 自動(dòng)化測(cè)試
    └── default.php # 單測(cè)頁面

調(diào)用組件變得十足簡(jiǎn)單:

echo View::useComponent('smart-box', [
    'keyword' => $keyword
]);

對(duì)比之前,可以看到組件化的一些特點(diǎn):

  • 按需加載

    只加載必要的前端資源

  • 對(duì)應(yīng)關(guān)系非常清晰

    組件所需要的前端資源都在同一目錄,職責(zé)明確且唯一,對(duì)應(yīng)關(guān)系顯著

  • 易于測(cè)試

    組件是具備獨(dú)立展現(xiàn)和交互的最小單元,可利用 Phantom 等工具自動(dòng)化測(cè)試

此外,由于前端資源集中進(jìn)行調(diào)度,組件化也為高階性能優(yōu)化提供了空間。例如實(shí)現(xiàn)組件級(jí)別的 BigRender、通過數(shù)據(jù)分析進(jìn)行資源的合并加載等等。

組件化 2.0:趨于成熟

組件化 1.0 上線后,由于簡(jiǎn)單易用,很快得到工程師的認(rèn)可,并開始在各項(xiàng)業(yè)務(wù)中應(yīng)用起來。新的需求接踵而來,一直持續(xù)到 2014 年底,這個(gè)階段我們稱之為組件化 2.0。下面介紹下主要的幾個(gè)改進(jìn)。

Lifecycle

組件在高內(nèi)聚的同時(shí),往往需要暴露一些接口供外界調(diào)用,從而能夠適應(yīng)復(fù)雜的頁面需求,例如提交訂單頁面需要在支付密碼組件啟動(dòng)完成后綁定提交時(shí)的檢查。Web Components、React 等都選擇了生命周期事件/方法,我們也是一樣。

組件的生命周期:

component-lifecycle

一個(gè)組件的完整生命周期包括:

  • init,初始化組件根節(jié)點(diǎn)和配置
  • fetch,加載 css 和 js 資源
  • render,內(nèi)容渲染,默認(rèn)的渲染內(nèi)容方式是 BigRender
  • ready,進(jìn)行數(shù)據(jù)綁定等操作
  • update,數(shù)據(jù)更新
  • destroy,解除所有事件監(jiān)聽,刪除所有組件節(jié)點(diǎn)

組件提供 pause、resume 方法以方便進(jìn)行生命周期控制。各個(gè)階段使用 Promise 串行進(jìn)行,異步的管理更清晰。使用自定義語義事件,在修改默認(rèn)行為、組件間通信上充分利用了 YUI 強(qiáng)大的自定義事件體系,有效降低了開發(fā)維護(hù)成本。

舉個(gè)例子,頁面初始化時(shí)組件的啟動(dòng)過程實(shí)際也是借助生命周期實(shí)現(xiàn)的:

var afterLoadList = [];
Y.all('[data-component]').each(function (node) {
    var component = new Y.mt.Component(node);
    // 綁定 init 生命周期事件,在 init 默認(rèn)行為完成后執(zhí)行回調(diào)
    component.after('init', function (e) {
        // 如果配置了延遲啟動(dòng)
        if (e.config.afterLoad) {
            // 暫停組件生命周期
            e.component.pause();
            // 壓入延遲啟動(dòng)數(shù)組
            afterLoadList.push(e.component);
        }
    });
    // 開始進(jìn)入生命周期
    component.start();
});

Y.on('load', function () {
    // 在頁面 load 事件發(fā)生時(shí)恢復(fù)組件生命周期
    afterLoadList.forEach(function (component) {
        component.resume();
    });
});

回過頭來看,引入生命周期除了帶來擴(kuò)展性外,更重要的是理順了組件的各個(gè)階段,有助于更好的理解和運(yùn)用。

Data Binding

數(shù)據(jù)綁定是我們期盼已久的功能,將 View 和 ViewModel 之間的交互自動(dòng)化無疑會(huì)節(jié)省工程師的大量時(shí)間。在組件化減少關(guān)注點(diǎn)和降低復(fù)雜度后,實(shí)現(xiàn)數(shù)據(jù)綁定變得更加可能。

我們最終實(shí)現(xiàn)的數(shù)據(jù)綁定方案主要參考了 Angular,通過在 html 節(jié)點(diǎn)上添加特定的屬性聲明綁定邏輯,js 掃描這些內(nèi)容并進(jìn)行相應(yīng)的渲染和事件綁定。當(dāng)數(shù)據(jù)發(fā)生變化時(shí),對(duì)應(yīng)的內(nèi)容全部重新渲染。

<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 設(shè)置為一個(gè)數(shù)組,DOM 上將自動(dòng)渲染其內(nèi)容   
    scope.$set('addrList', [
        { text: "first address" },
        { text: "second address" }
    ]);
});
</script>

使用屬性聲明綁定邏輯的好處是可以同時(shí)支持后端渲染,這對(duì)于美團(tuán)團(tuán)購(gòu)這樣的偏展現(xiàn)型業(yè)務(wù)是非常必要的,用戶可以很快看到頁面內(nèi)容。

Flux

實(shí)現(xiàn)數(shù)據(jù)綁定后,我們不得不面對(duì)另外一個(gè)問題:如何協(xié)同多個(gè)組件間的數(shù)據(jù)。因?yàn)槟硞€(gè)組件的數(shù)據(jù)變化,很有可能引起其他組件的變化。例如當(dāng)修改購(gòu)買數(shù)量,總金額會(huì)變化,而總金額超過 500 后,還需要展示大額消費(fèi)提醒。

為了解決這個(gè)問題,我們引入了 Flux,使用全局消息總線的思路進(jìn)行跨組件交互。

例如因?yàn)榻换?fù)雜而一直讓我們非常頭疼的項(xiàng)目購(gòu)買頁,在應(yīng)用組件 + Flux 重構(gòu)后,各模塊之間的互動(dòng)更加清晰:

component-flux

其他方面的改進(jìn)還有很多,包括引入模板引擎 LightnCandy 約束模板邏輯、支持組件任意嵌套、支持異步加載并自動(dòng)初始化等。

隨著組件化 2.0 的逐步完善,基本已經(jīng)可以從容應(yīng)對(duì)日常開發(fā),在效率和質(zhì)量方面都上了一個(gè)臺(tái)階。

組件化 3.0:重啟征程

時(shí)間的車輪滾滾前行,2014 年底,我們遇到一些新的機(jī)遇和挑戰(zhàn):

  • 基于 Node 的全棧開發(fā)模式開始應(yīng)用,前后端渲染有了更多的可能性
  • YUI 停止維護(hù),需要一套新的資源管理方案
  • 新業(yè)務(wù)不斷增加,需要找到一種組件共享的方式,避免重復(fù)造輪子

結(jié)合之前的實(shí)踐,以及在這一過程中逐漸積累的對(duì)業(yè)內(nèi)方案的認(rèn)知,我們提出了新的組件化方案:

  • 基于 React 開發(fā)頁面組件,使用 NPM 進(jìn)行分發(fā),方便共建共享
  • 基于 Browserify 二次開發(fā),建設(shè)資源打包工具 Reduce,方便瀏覽器加載
  • 建設(shè)適應(yīng)組件化開發(fā)模式的工程化開發(fā)方案 Turbo,方便工程師將組件應(yīng)用于業(yè)務(wù)開發(fā)中

React

在組件化 2.0 的過程中,我們發(fā)現(xiàn)很多功能和 React 重合,例如 Data Binding、Lifecycle、前后端渲染,甚至直接借鑒的 Flux。除此之外,React 的函數(shù)式編程思想、增量更新、兼容性良好的事件體系也讓我們非常向往。借著前端全棧開發(fā)的契機(jī),我們開始考慮基于 React 進(jìn)行組件化 3.0 的建設(shè)。

NPM + Reduce

NPM + Reduce 構(gòu)成了我們新的資源管理方案,其中:

  • NPM 負(fù)責(zé)組件的發(fā)布和安裝??梢哉J(rèn)為是“分”的過程,粒度越小,重用的可能性越大
  • Reduce 負(fù)責(zé)將頁面資源進(jìn)行打包??梢哉J(rèn)為是“合”的過程,讓瀏覽器更快地加載

一個(gè)典型的組件包:

smart-box/
├── package.json    # 組件包元信息
├── smart-box.jsx   # React Component
├── smart-box.scss  # 樣式
└── test
    └── main.js     # 測(cè)試

NPM 默認(rèn)只支持 js 文件的管理,我們對(duì) NPM 中的 package.json 進(jìn)行了擴(kuò)展,增加了 style 字段,以使打包工具 Reduce 也能夠?qū)?css 和 css 中引用的 image、font 進(jìn)行識(shí)別和處理:

{
    "style": "./smart-box.scss"
}

只要在頁面中 require 了 smart-box,經(jīng)過 Reduce 打包后,js、css 甚至圖片、字體,都會(huì)出現(xiàn)在瀏覽器中。

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è)務(wù)開發(fā)過程如果變得繁瑣、難以 Debug、性能低下的話,恐怕不會(huì)受到工程師歡迎。

為了解決這些問題,我們?cè)?Node 框架的基礎(chǔ)上,提供了一系列中間件和開發(fā)工具,逐步構(gòu)建對(duì)組件友好的前端工程化方案 Turbo。主要有:

  • 支持前后端同構(gòu)渲染,讓用戶更早看到內(nèi)容
  • 簡(jiǎn)化 Flux 流程,數(shù)據(jù)流更加清晰易維護(hù)
  • 引入 ImmutableJS,保證 Store 以外的數(shù)據(jù)不可變
  • 采用 cursor 機(jī)制,保證數(shù)據(jù)修改/獲取同步
  • 支持 Hot Module Replacement,改進(jìn)開發(fā)流自動(dòng)化

通過這些改進(jìn),一線工程師可以方便的使用各種組件,專注在業(yè)務(wù)本身上。開發(fā)框架層面的支持也反過來促進(jìn)了組件化的發(fā)展,大家更樂于使用一系列組件來構(gòu)建頁面功能。

小結(jié)

發(fā)現(xiàn)痛點(diǎn)、分析調(diào)研、應(yīng)用改進(jìn)的解決問題思路在組件化開發(fā)實(shí)踐中不斷運(yùn)用。歷經(jīng)三個(gè)大版本的演進(jìn),組件化開發(fā)模式有效緩解了業(yè)務(wù)發(fā)展帶來的復(fù)雜度提升的壓力,并培養(yǎng)工程師具備小而美的工程思想,形成共建共享的良好氛圍。毫無疑問,組件化這種“分而治之”的思想將會(huì)長(zhǎng)久地影響和促進(jìn)前端開發(fā)模式。我們現(xiàn)在已經(jīng)準(zhǔn)備好,迎接新的機(jī)遇和挑戰(zhàn),用技術(shù)的不斷革新提升工程師的幸福感。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多