近期總結:generator-web,前端自動化構建的解決方案

本文結合最近的工做經驗,總結出一個較簡潔的前端自動化構建方案,主張css和js的模塊化,並經過grunt的自動化構建,有效地解決css合併,js合併和圖片優化等問題,對於提升前端性能和項目代碼質量有必定參考價值,歡迎閱讀和點評:)php

github地址:https://github.com/liuyunzhuge/generator-webcss

demo地址:https://liuyunzhuge.github.io/generator-web/html

有興趣的同窗,在閱讀文章,學習或使用demo的過程當中,有任何的疑惑或者發現的問題儘管在評論中與我討論。前端

1. 概述

本項目是一個腳手架,應用於先後端結合型項目,能夠爲這種項目提供以下服務:java

  • 圖片壓縮,支持png,jpg,gif格式圖片
  • css模塊化,不過要求使用less編寫css
  • js模塊化,要求使用requirejs定義模塊
  • css壓縮合並,可作到一個頁面僅包含一個css文件
  • js壓縮合並混淆,可作到一個頁面僅包含一個script標籤,僅發出2個script請求,其中一個是require.js,另外一個則是頁面相關的js,依賴的js都經過requirejs的優化工具,跟頁面js合併成一個
  • 以上任務所有可經過grunt自動構建,而且在開發期間,主動觸發編譯和文件更新,以便在瀏覽器中刷新能看到最新的效果。

先後端結合型項目是指javaweb,asp.net這種非先後端徹底分離的項目,頁面一般是jsp,aspx,php等模板,部署時的更新包同時包含先後端的文件,屬於傳統的項目類型。node

本腳手架應用場景受限於先後端結合型項目,它僅能解決這類項目的前端構建的工做,對於後端構建以及項目打包發佈的工做須要由後端來完成,若是你的是javaweb類型,後端發佈能夠用maven,它可同時完成後端構建和打包的任務。之因此有本腳手架的產生,主要仍是由於目前先後端結合型項目的開發方式仍是很是常見,畢竟不是每一個公司都有資源去作到徹底的先後端分離這類的架構,尤爲是小公司或者是項目型公司,沒有那麼的人和時間等資源能讓你玩高級的東西,快速開發纔是王道。可是對於任何一個web項目來講,前端部分的基礎服務都是同樣的,好比圖片優化,css和js合併壓縮等,並且這都是在團隊資源可行的狀況下最好作的,對於項目質量,只有好處沒有壞處。本腳手架的產生,正是從工做中總結出來的一套開發架子,基於這個架子,你能夠很方便獲得由它給你帶來的如下好處:jquery

  • css和js的模塊化,有助於提供項目的代碼質量,便於未來的維護
  • 圖片的優化,css和js的壓縮合並,極大地減小了請求的數據量和請求數,十分利於項目的訪問性能
  • 前端任務全自動構建,不用擔憂編譯和文件拷貝等之類的問題,遵循本項目的約定,你會發現css和js的開發結構是如此清晰

2. 結構

WEB-INF/
html/
img/
js/
less/
Gruntfile.js
bower.json
optimize.json
package.json

由於本腳手架是結合demo一塊兒發佈到github上的,demo是基於javaweb的,因此你會看到裏面有個WEB-INF/文件夾,這個是javaweb必須的,html/裏面的頁面都是jsp模板,這個也是javaweb必須的;因此若是你想把這個腳手架應用於asp.net和php須要你刪掉WEB-INF這個文件夾,而後將html/裏的jsp改爲aspx或php模板。linux

另外,無論你想把這個腳手架用於什麼類型的項目,最好的使用方式是先把demo跑起來,而後在demo的基礎上開發,這樣可以減小出錯~git

html/ 用來存放頁面的模板(jsp,aspx,php)或html文件
img/ 存放圖片
js/ 存放js
less/ 存放less
Gruntfile.js 這是grunt任務的配置文件
bower.json 這是bower的配置文件
optimize.json 這是requirejs的優化工具的配置文件
package.json 這是grunt依賴的配置文件

注:html/,img/,js/,less/這三個文件夾裏面還有子文件夾,各自都有相關的約定,後續逐個介紹。在demo運行成功以前,那幾個配置文件請先不要改動。github

2.1 DEMO

查看DEMO:https://liuyunzhuge.github.io/generator-web/ 支持IE9+,chrome,firefox。

該DEMO是gh-pages搭建,你能夠經過查看本項目的gh-pages分支看到demo的文件內容,因爲github提供的這種服務僅支持靜態網頁,因此demo的首頁實際上是由html/index.jsp轉化過來的,另外位置也變了一下,index.jsp原來是放置在html/下面的,gh-pages分支裏的index.html放在了跟html/同級的位置,不這麼幹的話,gh-pages就看不到效果了。

3. 安裝

因爲本項目是結合javaweb一塊兒發佈的,因此如下的使用步驟均是在java的開發環境下說明的,IDE爲Intellij IDEA。若是你的項目也是javaweb項目,那麼推薦你用這個IDE,這是本人用過的最好的java web開發工具,集成了衆多前端工具,好比less,emmet和grunt還有bower等。若是你用它來開發項目,你會發現編寫less和使用grunt是如此順暢!純粹的後臺開發可能不挑剔開發工具,可是前端開發若是要結合後臺一塊兒弄一下的話, 最好仍是使用高級一點的IDE。

3.1 第一步

安裝nodejs,git,bower,grunt。windows下安裝便可,不須要linux。其中:

注:這一步與IDE和後端語言沒有任何關係。無論什麼語言的項目,這個都是使用本腳手架的基礎。

3.2 第二步

新建web項目,好比我用IDEA新建一個項目名爲generator-web-demo,它的項目結構以下:

.idea/
src/
web/
generator-web-demo.iml

其中.iead/和generator-web.iml都是IDEA建完項目之後建立的,能夠不用管。src是java源文件的目錄,web文件夾是項目的web根目錄。

3.3 第三步

在github上,download zip或者用git clone本項目,複製本項目的如下文件夾或文件粘貼到你項目的web根目錄(前一步提到的web文件夾):

html/
img/
js/
less/
WEB-INF/
bower.json
Gruntfile.js
optimize.json
package.json

WEB-INF直接覆蓋原來的WEB-INF便可。若是原來的web/下有一個index.jsp,能夠把它刪掉,個人習慣是把頁面都放一塊,html/已經提供一個index.jsp了,因此原來的index.jsp多餘了。

最後你的項目結構應該以下:

.idea/
src/
web/
    html/
    img/
    js/
    less/
    WEB-INF/
    bower.json
    Gruntfile.js
    optimize.json
    package.json
generator-web-demo.iml

3.4 第四步

使用bower安裝bower.json中配置的庫(jquery,iCheck,requirejs,bootstrap)

bower install --save

注:以上庫除requirejs是腳手架必須的外,其它都可根據實際項目須要進行添加和刪除,不過爲了把demo先跑起來,仍是別去改它,看懂了構建的原理再來根據項目須要修改也不遲。

安裝grunt和grunt插件:

npm install --save

若是npm安裝速度慢,能夠按下面網址提供的方式安裝,速度會快一些: http://npm.taobao.org/

3.5 第五步

執行grunt的default任務:

grunt default

若是執行grunt這個任務報錯,通常都是grunt-contrib-imagemin插件報的錯,你能夠用下面的命令重裝grunt-contrib-imagemin

npm uninstall grunt-contrib-imagemin --save 
npm install grunt-contrib-imagemin --save

注:以上是兩個命令,分開執行。若是還報一樣錯誤,可將以上命令多試幾回。

最後啓動你的web服務器,好比tomcat。打開瀏覽器訪問應該就能看到跟demo一致的首頁效果:)。

4. 開發

本部分介紹如何在demo的基礎上進行開發。

4.1 開發頁面

每新增一個頁面都放在html/下面,目前demo內包含:

html/
   base/
      body_end.jsp
      head_end.jsp
      head_start.jsp
   index.jsp
   ltIE9.html

其中:base/下的是一些公共的jsp頁面,你看下index.jsp裏面的那些inclue你就明白了。 ltIE9.html是一個提示頁面,若是用戶以IE8及如下的IE瀏覽器訪問就會跳到這個頁面提示更新瀏覽器或者下其它的好用的瀏覽器。

若是你的項目是asp.net的項目,請把這些jsp都替換成aspx。

這個html/的思路是:base/放公共的,其它頁面按模塊分,若是一個模塊只有一個頁面,那麼就把它直接放在html/下面,若是一個模塊有多個頁面,能夠在html/以模塊名建一個文件夾,相關頁面都放那裏面。

你在開發頁面的時候,建議:html/base/下的公共jsp保留,新頁面以index.jsp爲參考進行開發,能夠往base/內添加更多公共頁面,也可按模塊對新頁面進行分類。

4.2 baseUrl的問題

這個問題是這樣的,打比方說:

  • 你若是用eclispe開發項目,啓動tomcat之後,打開瀏覽器訪問,必須以[http://主機名:端口/工程名/]的方式才能訪問,以generator-web-demo舉例,就必須用[http://localhost:8080/generator-web-demo/]才能訪問,固然這個能改,我這裏是拿通常狀況舉例
  • 若是是IDEA開發,就不用考慮這個工程名的問題,由於它的tomcat默認配置,就是省略工程名的方式,它能夠直接經過http://localhost:8080/訪問
  • 這個工程名的配置就是contextpath,由於這個contextpath的存在可能致使你頁面裏的css,img,和script加載時出現加載不到資源的狀況,因此一般在先後端結合型項目中,必定得把這個contextpath統一塊兒來,讓全部的資源加載都是相對同一個路徑進行解析
  • 常見的處理方式就是把全部資源的url的contextpath及以前的那一段設置到html的base元素上,因爲base元素的做用,能夠保證腳本和css還有圖片的加載都相對於這個base的href屬性進行解析

base元素設置的代碼以下<head_start.jsp>:

<%
    String base = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
    base += base.endsWith("/") ? "" : "/";
    request.setAttribute("base", base);
    request.setAttribute("rnd", "?v=0.0.1");
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <base href="${base}">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!--[if lt IE 9]>
    <meta http-equiv="Refresh" content="0; url=${base}html/ltIE9.html"/>
    <![endif]-->

引用css,圖片和js時,直接使用相對web根目錄的路徑進行引用:

<link href="css/index.css" rel="stylesheet">
<img src="img/dist/logo.png" alt="LOGO">
<script data-main="js/dist/mod/index" src="js/dist/lib/require.js"></script>

還記得web根目錄的這個結構吧:

web/ 
   img/ 
   css/ 
   js/

4.3 圖片

約定項目相關的圖片放在img文件夾下,img/的結構爲:

img/
   dist/
   src/
   temp/

這個目錄結構最好是不改變。其中:

  • dist/存放通過grunt圖片優化任務以後的圖片,也是代碼中圖片的引用路徑
  • src/存放待優化的圖片源文件
  • temp/按雪碧圖存放各個雪碧圖的原圖片文件,好比你製做了一張雪碧圖,你把它命名爲common.png,那麼就在temp/裏面,新建一個common的文件夾,把你製做雪碧圖時用到的源圖都放進去,這樣未來你須要調整雪碧圖的時候還能從這裏找到源圖,雪碧圖製做推薦這個工具:http://alloyteam.github.io/gopng/ ,使用它還能夠把你每次製做雪碧圖的配置導出到本地,下次要修改的時候,從新導入以前的配置,就能在上次的狀態下繼續編輯

每增長一張圖片或雪碧圖,就把它丟到img/src裏面,頁面或css中引用圖片時,直接使用img/dist/這個路徑去引用便可。

4.4 less

約定css都用less來編寫,less文件夾的結構爲:

less/
    app/
       icon/
       mixin/
       widget/
    mod/
    sprite/

其中:

  • app/icon/ 存放字體圖標文件,以及字體圖標的less,好比你若是用iconfont,那麼就要把下載下來的iconfont的字體文件複製到app/icon/,把iconfont.css另存成iconfont.less也存到這個文件夾
  • app/mixin/存放一些定義的less mixin,demo中不少mixin是從bootstrap的源碼中提取的
  • app/wifdget/存放一些公共組件的less,對於一些公共的模塊,好比等header,footer,搜索框,購物車等均可以定義成單個的模塊,哪一個頁面的用到了這個模塊,import一下就行了
  • sprite/存放雪碧圖相關的less,製做雪碧圖時,gopng這個工具會給你提供它生成的css,你能夠把它另存爲less,個人作法是把雪碧圖裏每一個背景圖的css都定義成了mixin,這樣html元素在用到某哥背景圖時,只要引入這個less文件,調一下mixin就能夠了,具體的方式,請參考demo裏面sprite/下的兩個less文件的定義方式,以及app/mod/index.less中的使用方式
  • app/mod/存放頁面的less文件,基本上每一個頁面一個less,也按模塊再建文件夾進行分類,跟開發新頁面同樣的道理

grunt的less任務,會把app/icon/下的字體拷貝到web/css/下,把app/mod/下的less編譯成的css也放到web/css/下,因此頁面引入css的時候,要相對css/這個文件夾引用

<link href="css/index.css" rel="stylesheet">

在這一塊須要注意的問題是字體文件和圖片文件的路徑問題,記住less編譯後的css是放在css/下面的,而圖片優化後是放在img/dist/下面的;字體文件跟css默認都是直接位於css/目錄下的,具體請多參考demo中app/mod/index.less。

4.5 js

約定js源碼都在js/src/下定義,js/src/的結構爲

js/
   js/src/
       app/
          mod/
          widget/
       lib/
       mod/
       common.js

其中:

  • common.js是requirejs的配置文件
  • js/src/mod/存放各個頁面的main.js
  • js/src/lib/存放依賴的js庫,好比jquery,icheck,bootstrap的transition都會經過grunt的任務,從bower下載的位置拷貝到這裏,具體請看gruntfile.js的copy任務,同時這個文件夾也是requirejs的baseUrl的位置
  • js/src/app/mod/存放各個頁面真正執行邏輯的js
  • js/src/app/widget存放組件相關的js,好比demo中定義了四個組件,tab,carousel,icheck,radioToggle

在沒有優化以前,grunt任務會把js/src/的文件所有拷貝到js/dist/下,在優化以後,grunt任務會把js/dist/mod/下的每一個js依賴的全部js,都跟它合併成爲一個js。無論有沒有優化,頁面引用js文件時,都要相對js/dist這個目錄引用!

一個頁面相關的js整個加載流程,以demo中的index.js爲例:

  • 首先頁面中先經過:<script data-main="js/dist/mod/index" src="js/dist/lib/require.js"></script>加載js/dist/mod/index.js
  • 因爲index.js的源碼爲:
requirejs(['../common'], function (common) {
    requirejs(['app/mod/index']);
});
  • 它會先加載common.js讀取配置,而後再加載js/dist/app/mod/index.js執行頁面的邏輯
  • 到了js/dist/app/mod/index.js後,就是加載各個依賴的模塊處理頁面邏輯的過程了,整個js的異步依賴跟加載狀況就結束了。

因此開發一個js的步驟爲,假設要開發一個userCenter.js:

  • 首先在js/src/mod/下新建一個userCenter.js
  • 在js/src/app/mod/下也新建一個userCenter.js
  • 將js/src/mod下的userCenter.js的源碼改成:
requirejs(['../common'], function (common) {
    requirejs(['app/mod/userCenter']);
});
  • 由於js/src/mod/下的js都僅是一個橋樑,因此這個文件夾下的每一個js都只有三行。
  • 在js/src/app/mod/userCenter.js中編寫你的頁面邏輯

另外還要在optimize.json中增長一個對userCenter.js的配置項,以便生產環境構建時能把它依賴的js都跟它合併成一個js 。

4.5.1 如何配置optimize.json

簡單點來講,照着這個模板就能夠了:

[
    {
        "name": "../mod/index",
        "include": [
            "app/mod/index"
        ]
    }
]

好比若是userCenter.js,那麼就該配置成:

[
    {
        "name": "../mod/index",
        "include": [
            "app/mod/index"
        ]
    },
    {
        "name": "../mod/userCenter",
        "include": [
            "app/mod/userCenter"
        ]
    }    
]

具體的原理牽扯的細節就比較多了,可參考如下兩個網址去研究一下:

5. 構建

5.1 開發環境

grunt default

使用以上任務就會啓動構建,包括:

  1. 優化圖片,並將優化後的圖片存放至img/dist/
  2. 編譯less/mod下的less,並存放至css/
  3. 將less/app/icon下的字體文件,拷貝到css/
  4. 將js/src/下全部內容,拷貝至js/dist/
  5. 監控img/src/,less/,js/src/內的文件變化,一有變化,自動執行相應的1,2,3

這個構建不會壓縮css,不會合並壓縮混淆js。

5.2 生產環境

grunt release

使用以上任務,完成生產環境構建:

  1. 在開發環境構建基礎上,進行css壓縮
  2. 使用requirejs的優化工具優化頁面的js,即將它所依賴的模塊所有合併成一個js,並作壓縮混淆處理

5.3 optimize

grunt optimize

這個任務可用於測試requirejs的合併是否正確,實際使用方式以下:

  • 先執行grunt default
  • 再執行grunt optimize
  • 看看js/dist/mod/下的js,刷新瀏覽器測試下功能

之因此沒跟default任務合併,是由於這個優化任務比較費時,不用每次開發的時候都執行優化

6. 打包

打包前,先執行下grunt release,並將修改提交至代碼服務器。另外bower_components和node_modules這兩個文件夾千萬別傳到代碼服務器上去,前端用的東西,打包的後臺同事怎麼會要你這個呢;還要記得提醒他們把如下文件夾從工程中排除出去:

img/src/
img/temp/
js/src/
less/

這些文件是沒有必要發佈出去的。至於具體怎麼打包,就是後臺同事的責任了。

7. 計劃

目前已知的未解決的問題:

  • requirejs異步加載js時的緩存特別嚴重,目前開發環境可經過配置requirejs的urlArgs解決,可是生產環境時這個urlArgs要去掉,在js/src/common.js中能夠去掉,下一步應該把這個作到grunt release任務中去
  • 靜態資源更新的問題,好比js和css更新了,可是客戶端還有緩存
  • 圖片和字體文件自動base64編碼

下一步計劃就是要解決以上問題。

相關文章
相關標籤/搜索