前端開發(fā)體系建設(shè)日記

2018-02-24 15:49 更新

原文出處:https://github.com/fouber/blog/issues/2
作者:fouber

目錄

前端開發(fā)體系建設(shè)日記

上周寫了一篇?文章?介紹前端集成解決方案的基本理論,很多同學(xué)看過之后大呼不過癮。

干貨?fuck things?在哪里!

本打算繼續(xù)完善理論鏈,形成前端工程的知識結(jié)構(gòu)。但鑒于如今的快餐文化,po主決定還是先寫一篇實戰(zhàn)介紹,讓大家看到前端工程體系能為團隊帶來哪些好處,調(diào)起大家的胃口再說。

ps: 寫完才發(fā)現(xiàn)這篇文章真的非常非常長,涵蓋了前端開發(fā)中的很多方面,希望大家能有耐心看完,相信一定會有所斬獲。。。

2014年02月12日 - 晴

新到松鼠團隊的第二天,小伙伴?@nino?找到我說

nino: 視頻項目打算重新梳理一下,希望能引入新的技術(shù)體系,解決現(xiàn)有的一些問題。

po主不禁暗喜,好機會,這是我專業(yè)啊,藍翔技校-前端集成解決方案學(xué)院-自動化系-打包學(xué)專業(yè)的文憑不是白給的,于是自信滿滿的對nino說,有什么需求盡管提!

nino: 我的需求并不多,就這么幾條~~

  1. 模塊化開發(fā)。最好能像寫nodejs一樣寫js,很舒服。css最好也能來個模塊化管理!
  2. 性能要好。模塊那么多,得有按需加載,請求不能太多。
  3. 組件化開發(fā)。一個組件的js、css、模板最好都在一個目錄維護,維護起來方便。
  4. 用?handlebars?作為前端模板引擎。這個模板引擎不錯,logic-less(輕邏輯)。
  5. 用?stylus?寫css挺方便,給我整一個。
  6. 圖片base64嵌入。有些小圖可能要以base64的形式嵌入到頁面、js或者css中使用。嵌入之前記得壓縮圖片以減小體積。
  7. js/css/圖片壓縮應(yīng)該都沒問題吧。
  8. 要能與公司的ci平臺集。工具里最好別依賴什么系統(tǒng)庫,ci的機器未必支持。
  9. 開發(fā)體驗要好。文件監(jiān)聽,瀏覽器自動刷新(livereload)一個都不能少。
  10. 我們用nodejs作為服務(wù)器,本地要能預(yù)覽,最好再能抓取線上數(shù)據(jù),方便調(diào)試。

我倒吸一口涼氣,但表面故作鎮(zhèn)定的說:恩,確實不多,讓我們先來看看第一個需求。。。

還沒等我說完,nino打斷我說

nino: 橋豆麻袋(稍等),還有一個最重要的需求!

松鼠公司的松鼠瀏覽器你知道吧,恩,它有很多個版本的樣子。
我希望代碼發(fā)布后能按照版本部署,不要彼此覆蓋。

舉個例子,代碼部署結(jié)構(gòu)可能是這樣的:

  release/
    - public/
      - 項目名
        - 1.0.0/
        - 1.0.1/
        - 1.0.2/
        - 1.0.2-alpha/
        - 1.0.2-beta/

讓歷史瀏覽器瀏覽歷史版本,沒事還能做個灰度發(fā)布,ABTest啥的,多好!

此外,我們將來會有多個項目使用這套開發(fā)模式,希望能共用一些組件或者模
塊,產(chǎn)品也會公布一些api模塊給第三方使用,所以共享模塊功能也要加上。

總的來說,還要追加兩個部署需求:

  1. 按版本部署,采用非覆蓋式發(fā)布
  2. 允許第三方引用項目公共模塊

nino: 怎么樣,不算復(fù)雜吧,這個項目很趕,3天搞定怎么樣?

我凝望著會議室白板上的這些需求,正打算爭辯什么,一扭頭發(fā)現(xiàn)nino已經(jīng)不見了。。。正在沮喪之際,小伙伴?@hinc?過來找我,跟他大概講了一下nino的需求,正想跟他抱怨工期問題時,hinc卻說

hinc: 恩,這正是我們需要的開發(fā)體系,不過我這里還有一個需求。。。

  1. 我們之前積累了一些業(yè)務(wù)可以共用的模塊,放在了公司內(nèi)的gitlab上,采用?component?作為發(fā)布規(guī)范,能不能再加上這個組件倉庫的支持?

3天時間,13項前端技術(shù)元素,靠譜么。。。

2014年02月13日 - 多云

一覺醒來,輕松了許多,但還有任務(wù)在身,不敢有半點怠慢。整理一下昨天的需求,我們來做一個簡單的劃分。

  • 規(guī)范
    • 開發(fā)規(guī)范
      • 模塊化開發(fā),js模塊化,css模塊化,像nodejs一樣編碼
      • 組件化開發(fā),js、css、handlebars維護在一起
    • 部署規(guī)范
      • 采用nodejs后端,基本部署規(guī)范應(yīng)該參考?express?項目部署
      • 按版本號做非覆蓋式發(fā)布
      • 公共模塊可發(fā)布給第三方共享
  • 框架
    • js模塊化框架,支持請求合并,按需加載等性能優(yōu)化點
  • 工具
    • 可以編譯stylus為css
    • 支持js、css、圖片壓縮
    • 允許圖片壓縮后以base64編碼形式嵌入到css、js或html中
    • 與ci平臺集成
    • 文件監(jiān)聽、瀏覽器自動刷新
    • 本地預(yù)覽、數(shù)據(jù)模擬
  • 倉庫
    • 支持component模塊安裝和使用

這樣一套規(guī)范、框架、工具和倉庫的開發(fā)體系,服從我之前介紹的?前端集成解決方案?的描述。前端界每天都團隊在設(shè)計和實現(xiàn)這類系統(tǒng),它們其實是有規(guī)律可循的。百度出品的?fis?就是一個能幫助快速搭建前端集成解決方案的工具。使用fis我應(yīng)該可以在3天之內(nèi)完成這些任務(wù)。

ps: 這不是一篇關(guān)于fis的軟文,如果這樣的一套系統(tǒng)基于grunt實現(xiàn)相信會有非常大量的開發(fā)工作,3天完成幾乎是不可能的任務(wù)。

不幸的是,現(xiàn)在fis官網(wǎng)所介紹的?并不是?fis,而是一個叫?fis-plus?的項目,該項目并不像字面理解的那樣是fis的加強版,而是在fis的基礎(chǔ)上定制的一套面向百度前端團隊的解決方案,以php為后端語言,跟smarty有較強的綁定關(guān)系,有著?19項?技術(shù)要素,密切配合百度現(xiàn)行技術(shù)選型。絕大多數(shù)非百度前端團隊都很難完整接受這19項技術(shù)選型,尤其是其中的部署、框架規(guī)范,跟百度前端團隊相關(guān)開發(fā)規(guī)范、部署規(guī)范、以及php、smarty等有著較深的綁定關(guān)系。

因此如果你的團隊用的不是?php后端?&&?smarty模板?&&?modjs模塊化框架?&&?bingo框架?的話,請查看?fis的文檔,或許不會有那么多困惑。

ps: fis是一個構(gòu)建系統(tǒng)內(nèi)核,很好的抽象了前端集成解決方案所需的通用工具需求,本身不與任何后端語言綁定。而基于fis實現(xiàn)的具體解決方案就會有具體的規(guī)范和技術(shù)選型了。

言歸正傳,讓我們基于?fis?開始實踐這套開發(fā)體系吧!

0. 開發(fā)概念定義

前端開發(fā)體系設(shè)計第一步要定義開發(fā)概念。開發(fā)概念是指針對開發(fā)資源的分類概念。開發(fā)概念的確立,直接影響到規(guī)范的定制。比如,傳統(tǒng)的開發(fā)概念一般是按照文件類型劃分的,所以傳統(tǒng)前端項目會有這樣的目錄結(jié)構(gòu):

  • js:放js文件
  • css:放css文件
  • images:放圖片文件
  • html:放html文件

這樣確實很直接,任何智力健全的人都知道每個文件該放在哪里。但是這樣的開發(fā)概念劃分將給項目帶來較高的維護成本,并為項目臃腫埋下了工程隱患,理由是:

  1. 如果項目中的一個功能有了問題,維護的時候要在js目錄下找到對應(yīng)的邏輯修改,再到css目錄下找到對應(yīng)的樣式文件修改一下,如果圖片不對,還要再跑到images目錄下找對應(yīng)的開發(fā)資源。
  2. images下的文件不知道哪些圖片在用,哪些已經(jīng)廢棄了,誰也不敢刪除,文件越來越多。

ps: 除非你的團隊只有1-2個人,你的項目只有很少的代碼量,而且不用關(guān)心性能和未來的維護問題,否則,以文件為依據(jù)設(shè)計的開發(fā)概念是應(yīng)該被拋棄的。

以我個人的經(jīng)驗,更傾向于具有一定語義的開發(fā)概念。綜合前面的需求,我為這個開發(fā)體系確定了3個開發(fā)資源概念:

  • 模塊化資源:js模塊、css模塊或組件
  • 頁面資源:網(wǎng)站html或后端模板頁面,引用模塊化框架,加載模塊
  • 非模塊化資源:并不是所有的開發(fā)資源都是模塊化的,比如提供模塊化框架的js本身就不能是一個模塊化的js文件。嚴格上講,頁面也屬于一種非模塊化的靜態(tài)資源。

ps: 開發(fā)概念越簡單越好,前面提到的fis-plus也有類似的開發(fā)概念,有組件或模塊(widget),頁面(page),測試數(shù)據(jù)(test),非模塊化靜態(tài)資源(static)。有的團隊在模塊之中又劃分出api模塊和ui模塊(組件)兩種概念。

1. 開發(fā)目錄設(shè)計

基于開發(fā)概念的確立,接下來就要確定目錄規(guī)范了。我通常會給每種開發(fā)資源的目錄取一個有語義的名字,三種資源我們可以按照概念直接定義目錄結(jié)構(gòu)為:

project
  - modules       存放模塊化資源
  - pages         存放頁面資源
  - static        存放非模塊化資源

這樣劃分目錄確實直觀,但結(jié)合前面hinc說過的,希望能使用component倉庫資源,因此我決定將模塊化資源目錄命名為components,得到:

project
  - components    存放模塊化資源
  - pages         存放頁面資源
  - static        存放非模塊化資源

而nino又提到過模塊資源分為項目模塊和公共模塊,以及hinc提到過希望能從component安裝一些公共組件到項目中,因此,一個components目錄還不夠,想到nodejs用node_modules作為模塊安裝目錄,因此我在規(guī)范中又追加了一個?component_modules?目錄,得到:

project
  - component_modules    存放外部模塊資源
  - components           存放項目模塊資源
  - pages                存放頁面資源
  - static               存放非模塊化資源

nino說過今后大多數(shù)項目采用nodejs作為后端,express是比較常用的nodejs的server框架,express項目通常會把后端模板放到?views?目錄下,把靜態(tài)資源放到?public?下。為了迎合這樣的需求,我將page、static兩個目錄調(diào)整為?views?和?public,規(guī)范又修改為:

project
  - component_modules    存放外部模塊資源
  - components           存放項目模塊資源
  - views                存放頁面資源
  - public               存放非模塊化資源

考慮到頁面也是一種靜態(tài)資源,而public這個名字不具有語義性,與其他目錄都有概念沖突,不如將其與views目錄合并,views目錄負責(zé)存放頁面和非模塊化資源比較合適,因此最終得到的開發(fā)目錄結(jié)構(gòu)為:

project
  - component_modules    存放外部模塊資源
  - components           存放項目模塊資源
  - views                存放頁面以及非模塊化資源

2. 部署目錄設(shè)計

托nino的福,咱們的部署策略將會非常復(fù)雜,根據(jù)要求,一個完整的部署結(jié)果應(yīng)該是這樣的目錄結(jié)構(gòu):

release
  - public
    - 項目名
      - 1.0.0    1.0.0版本的靜態(tài)資源都構(gòu)建到這里
      - 1.0.1    1.0.1版本的靜態(tài)資源都構(gòu)建到這里
      - 1.0.2    1.0.2版本的靜態(tài)資源都構(gòu)建到這里
      ...
  - views
    - 項目名
      - 1.0.0    1.0.0版本的后端模板都構(gòu)建到這里
      - 1.0.1    1.0.1版本的后端模板都構(gòu)建到這里
      - 1.0.2    1.0.2版本的后端模板都構(gòu)建到這里
      ...

由于還要部署一些可以被第三方使用的模塊,public下只有項目名的部署還不夠,應(yīng)改把模塊化文件單獨發(fā)布出來,得到這樣的部署結(jié)構(gòu):

release
  - public
    - component_modules   模塊化資源都部署到這個目錄下
      - module_a
        - 1.0.0
          - module_a.js
          - module_a.css
          - module_a.png
        - 1.0.1
        - 1.0.2
        ...
    - 項目名
      - 1.0.0    1.0.0版本的靜態(tài)資源都構(gòu)建到這里
      - 1.0.1    1.0.1版本的靜態(tài)資源都構(gòu)建到這里
      - 1.0.2    1.0.2版本的靜態(tài)資源都構(gòu)建到這里
      ...
  - views
    - 項目名
      - 1.0.0    1.0.0版本的后端模板都構(gòu)建到這里
      - 1.0.1    1.0.1版本的后端模板都構(gòu)建到這里
      - 1.0.2    1.0.2版本的后端模板都構(gòu)建到這里
      ...

由于?component_modules?這個名字太長了,如果部署到這樣的路徑下,url會很長,這也是一個優(yōu)化點,因此最終決定部署結(jié)構(gòu)為:

release
  - public
    - c                   模塊化資源都部署到這個目錄下
      - 公共模塊
        - 版本號
      - 項目名
        - 版本號
    - 項目名
      - 版本號             非模塊化資源都部署到這個目錄下
  - views
    - 項目名
      - 版本號             后端模板都構(gòu)建到這個目錄下

插一句,并不是所有團隊都會有這么復(fù)雜的部署要求,這和松鼠團隊的業(yè)務(wù)需求有關(guān),但我相信這個例子也不會是最復(fù)雜的。每個團隊都會有自己的運維需求,前端資源部署經(jīng)常牽連到公司技術(shù)架構(gòu),因此很多前端項目的開發(fā)目錄結(jié)構(gòu)會和部署要求保持一致。這也為項目間模塊的復(fù)用帶來了成本,因為代碼中寫的url通常是部署后的路徑,遷移之后就可能失效了。

解耦開發(fā)規(guī)范和部署規(guī)范是前端開發(fā)體系的設(shè)計重點。

好了,去吃個午飯,下午繼續(xù)。。。

3. 配置fis連接開發(fā)規(guī)范和部署規(guī)范

我準備了一個樣例項目:

project
  - views
    - logo.png
    - index.html
  - fis-conf.js
  - README.md

fis-conf.js是fis工具的配置文件,接下來我們就要在這里進行構(gòu)建配置了。雖然開發(fā)規(guī)范和部署規(guī)范十分復(fù)雜,但好在fis有一個非常強大的?roadmap.path?功能,專門用于分類文件、調(diào)整發(fā)布結(jié)構(gòu)、指定文件的各種屬性等功能實現(xiàn)。

所謂構(gòu)建,其核心任務(wù)就是將文件按照某種規(guī)則進行分類(以文件后綴分類,以模塊化/非模塊化分類,以前端/后端代碼分類),然后針對不同的文件做不同的構(gòu)建處理。

閑話少說,我們先來看一下基本的配置,在?fis-conf.js?中添加代碼:

fis.config.set('roadmap.path', [
    {
        reg : '**.md',   //所有md后綴的文件
        release : false  //不發(fā)布
    }
]);

以上配置,使得項目中的所有md后綴文件都不會發(fā)布出來。release是定義file對象發(fā)布路徑的屬性,如果file對象的release屬性為false,那么在項目發(fā)布階段就不會被輸出出來。

在fis中,roadmap.pah是一個數(shù)組數(shù)據(jù),數(shù)組每個元素是一個對象,必須定義?reg?屬性,用以匹配項目文件路徑從而進行分類劃分,reg屬性的取值可以是路徑通配字符串或者正則表達式。fis有一個內(nèi)部的文件系統(tǒng),會給每個源碼文件創(chuàng)建一個?fis.File?對象,創(chuàng)建File對象時,按照roadmap.path的配置逐個匹配文件路徑,匹配成功則把除reg之外的其他屬性賦給File對象,fis中各種處理環(huán)節(jié)及插件都會讀取所需的文件對象的屬性值,而不會自己定義規(guī)范。有關(guān)roadmap.path的工作原理可以看這里?以及?這里。

ok,讓md文件不發(fā)布很簡單,那么views目錄下的按版本發(fā)布要求怎么實現(xiàn)呢?其實也是非常簡單的配置:

fis.config.set('roadmap.path', [
    {
        reg : '**.md',   //所有md后綴的文件
        release : false  //不發(fā)布
    },
    {
        //正則匹配【/views/**】文件,并將views后面的路徑捕獲為分組1
        reg : /^\/views\/(.*)$/i,
        //發(fā)布到 public/proj/1.0.0/分組1 路徑下
        release : '/public/proj/1.0.0/$1'
    }
]);

roadmap.path數(shù)組的第二元素據(jù)采用正則作為匹配規(guī)則,正則可以幫我們捕獲到分組信息,在release屬性值中引用分組是非常方便的。正則匹配 + 捕獲分組,成為目錄規(guī)范配置的強有力工具:

fis release

執(zhí)行?fis release -d ../release?之后,得到構(gòu)建后的內(nèi)容為:

<!doctype html>
<html>
<head>
    <title>hello</title>
</head>
<body>
    <script type="text/javascript" src="https://atts.w3cschool.cn/attachments/image/cimg/scrat.js"></script>
    <script type="text/javascript">
        require.config({
            "deps": {
                "components/bar/bar.js": [
                    "components/bar/bar.css"
                ],
                "components/foo/foo.js": [
                    "components/bar/bar.js",
                    "components/foo/foo.css"
                ]
            }
        });
        require.async('components/foo/foo.js', function(foo){
            //todo
        });
    </script>
</body>
</html>

在調(diào)用?require.async('components/foo/foo.js')?之際,模塊化框架已經(jīng)知道了這個foo.js依賴于bar.js、bar.css以及foo.css,因此可以發(fā)起兩個combo請求去加載所有依賴的js、css文件,完成后再執(zhí)行回調(diào)。

現(xiàn)在模塊的id有一些問題,因為模塊發(fā)布會有版本號信息,因此模塊id也應(yīng)該攜帶版本信息,從前面的依賴樹生成配置代碼中我們可以看到模塊id其實也是文件的一個屬性,因此我們可以在roadmap.path中重新為文件賦予id屬性,使其攜帶版本信息:

fis.config.set('roadmap.path', [
    {
        reg : '**.md',
        release : false,
        isHtmlLike : true
    },
    {
        reg : /^\/component_modules\/(.*)$/i,
        //追加id屬性
        id : '$1',
        release : '/public/c/$1'
    },
    {
        reg : /^\/components\/(.*)$/i,
        //追加id屬性,id為【項目名/版本號/文件路徑】
        id : '${name}/${version}/$1',
        release : '/public/c/${name}/${version}/$1'
    },
    {
        reg : /^\/views\/(.*)$/,
        //給views目錄下的文件加一個isViews屬性標記,用以標記文件分類
        //我們可以在插件中拿到文件對象的這個值
        isViews : true,
        release : '/public/${name}/${version}/$1'
    },
    {
        reg : '**',
        useStandard : false,
        useOptimizer : false
    }
]);

重新構(gòu)建項目,我們得到了新的結(jié)果:

<!doctype html>
<html>
<head>
    <title>hello</title>
</head>
<body>
    <img src="https://atts.w3cschool.cn/attachments/image/cimg/logo.png"/>
    <script type="text/javascript" src="https://atts.w3cschool.cn/attachments/image/cimg/scrat.js"></script>
    <script type="text/javascript">
        require.config({
            "deps": {
                "proj/1.0.4/bar/bar.js": [
                    "proj/1.0.4/bar/bar.css"
                ],
                "proj/1.0.4/foo/foo.js": [
                    "proj/1.0.4/bar/bar.js",
                    "proj/1.0.4/foo/foo.css"
                ]
            }
        });
        require.async('proj/1.0.4/foo/foo.js', function(foo){
            //todo
        });
    </script>
</body>
</html>

you see?所有id都會被修改為我們指定的模式,這就是以文件為中心的編譯系統(tǒng)的威力。

以文件對象為中心構(gòu)建系統(tǒng)應(yīng)該通過配置指定文件的各種屬性。插件并不自己實現(xiàn)某種規(guī)范規(guī)定,而是讀取file對象的對應(yīng)屬性值,這樣插件的職責(zé)單一,規(guī)范又能統(tǒng)一起來被用戶指定,為完整的前端開發(fā)體系設(shè)計奠定了堅實規(guī)范配置的基礎(chǔ)。

接下來還有一個問題,就是模塊名太長,開發(fā)中寫這么長的模塊名非常麻煩。我們可以借鑒流行的模塊化框架中常用的縮短模塊名手段——別名(alias)——來降低開發(fā)中模塊引用的成本。此外,目前的配置其實會針對所有文件生成依賴關(guān)系表,我們的開發(fā)概念定義只有components和component_modules目錄下的文件才是模塊化的,因此我們可以進一步的對文件進行分類,得到這樣配置規(guī)范:

fis.config.set('roadmap.path', [
    {
        reg : '**.md',
        release : false,
        isHtmlLike : true
    },
    {
        reg : /^\/component_modules\/(.*)$/i,
        id : '$1',
        //追加isComponentModules標記屬性
        isComponentModules : true,
        release : '/public/c/$1'
    },
    {
        reg : /^\/components\/(.*)$/i,
        id : '${name}/${version}/$1',
        //追加isComponents標記屬性
        isComponents : true,
        release : '/public/c/${name}/${version}/$1'
    },
    {
        reg : /^\/views\/(.*)$/,
        isViews : true,
        release : '/public/${name}/${version}/$1'
    },
    {
        reg : '**',
        useStandard : false,
        useOptimizer : false
    }
]);

然后我們?yōu)橐恍┠Kid建立別名:

var createFrameworkConfig = function(ret, conf, settings, opt){
    var map = {};
    map.deps = {};
    //別名收集表
    map.alias = {};
    fis.util.map(ret.src, function(subpath, file){
        //添加判斷,只有components和component_modules目錄下的文件才需要建立依賴樹或別名
        if(file.isComponents || file.isComponentModules){
            //判斷一下文件名和文件夾是否同名,如果同名則建立一個別名
            var match = subpath.match(/^\/components\/(.*?([^\/]+))\/\2\.js$/i);
            if(match && match[1] && !map.alias.hasOwnProperty(match[1])){
                map.alias[match[1]] = file.id;
            }
            if(file.requires && file.requires.length){
                map.deps[file.id] = file.requires;
            }
        }
    });
    var stringify = JSON.stringify(map, null, opt.optimize ? null : 4);
    fis.util.map(ret.src, function(subpath, file){
        if(file.isViews && (file.isJsLike || file.isHtmlLike)){
            var content = file.getContent();
            content = content.replace(/\b__FRAMEWORK_CONFIG__\b/g, stringify);
            file.setContent(content);
        }
    });
};
fis.config.set('modules.postpackager', [createFrameworkConfig]);

再次構(gòu)建,在注入的代碼中就能看到alias字段了:

require.config({
    "deps": {
        "proj/1.0.5/bar/bar.js": [
            "proj/1.0.5/bar/bar.css"
        ],
        "proj/1.0.5/foo/foo.js": [
            "proj/1.0.5/bar/bar.js",
            "proj/1.0.5/foo/foo.css"
        ]
    },
    "alias": {
        "bar": "proj/1.0.5/bar/bar.js",
        "foo": "proj/1.0.5/foo/foo.js"
    }
});

這樣,代碼中的?require('foo');?就等價于?require('proj/1.0.5/foo/foo.js');了。

還剩最后一個小小的需求,就是希望能像寫nodejs一樣開發(fā)js模塊,也就是要求實現(xiàn)define的自動包裹功能,這個可以通過文件編譯的?postprocessor?插件完成。配置為:

//在postprocessor對所有js后綴的文件進行內(nèi)容處理:
fis.config.set('modules.postprocessor.js', function(content, file){
    //只對模塊化js文件進行包裝
    if(file.isComponents || file.isComponentModules){
        content = 'define("' + file.id + 
                  '", function(require,exports,module){' +
                  content + '});';
    }
    return content;
});

所有在components目錄和component_modules目錄下的js文件都會被包裹define,并自動根據(jù)roadmap.path中的id配置進行模塊定義了。

最煎熬的一天終于過去了,睡一覺,擁抱一下周末。

2014年02月15日 - 超晴

周末的天氣非常好哇,一覺睡到中午才起,這么好的天氣寫碼豈不是很loser?!

2014年02月16日 - 小雨

居然浪費了一天,剩下的時間不多了,今天要抓緊啊?。?!

讓我們來回顧一下已經(jīng)完成了哪些工作:

  • 規(guī)范
    • 開發(fā)規(guī)范
      • 模塊化開發(fā),js模塊化,css模塊化,像nodejs一樣的模塊化開發(fā)
      • 組件化開發(fā),js、css、handlebars維護在一起
    • 部署規(guī)范
      • 采用nodejs后端,基本部署規(guī)范應(yīng)該參考?express?項目部署
      • 按版本號做非覆蓋式發(fā)布
      • 公共模塊可發(fā)布給第三方共享
  • 框架
    • js模塊化框架,支持請求合并,按需加載等性能優(yōu)化點
  • 工具
    • 可以編譯stylus為css
    • 支持js、css、圖片壓縮
    • 允許圖片壓縮后以base64編碼形式嵌入到css、js或html中
    • 與ci平臺集成
    • 文件監(jiān)聽、瀏覽器自動刷新
    • 本地預(yù)覽、數(shù)據(jù)模擬
  • 倉庫
    • 支持component模塊安裝和使用

剩下的幾個需求中有些是fis默認支持的,比如base64內(nèi)嵌功能,圖片會先經(jīng)過編譯流程,得到壓縮后的內(nèi)容fis再對其進行base64化的內(nèi)嵌處理。由于fis的內(nèi)嵌功能支持任意文件的內(nèi)嵌,所以,這個語言能力擴展可以同時解決前端模板和圖片base64內(nèi)嵌需求,比如我們有這樣的代碼:

project
  - components
    - foo
      - foo.js
      - foo.css
      - foo.handlebars
      - foo.png

無需配置,既可以在js中嵌入資源,比如 foo.js 中可以這樣寫:

//依賴聲明
var bar =  require('../bar/bar.js');
//把handlebars文件的字符串形式嵌入到j(luò)s中
var text = __inline('foo.handlebars');
var tpl = Handlebars.compile(text);
exports.render = function(data){
    return tpl(data);
};

//把圖片的base64嵌入到j(luò)s中
var data = __inline('foo.png');
exports.getImage = function(){
    var img = new Image();
    img.src = data;
    return img;
};

編譯后得到:

define("proj/1.0.5/foo/foo.js", function(require,exports,module){
//依賴聲明
var bar =  require('proj/1.0.5/bar/bar.js');
//把handlebars文件的字符串形式嵌入到j(luò)s中
var text = "<h1>{{title}}</h1>";
var tpl = Handlebars.compile(text);
exports.render = function(data){
    return tpl(data);
};

//把圖片的b
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號