webpack + gulp 在前端中的應用

概述

從去年短期內對現有系統的改造到現在穩定實施,已經好幾個月,這套流程知足了平常前端開發的流程。因爲以前項目組的模塊化自己作的不是很好,基本算是推到一半重來,雖然陣痛,但回顧起來確實很是值得。webpack,簡單來講就是前端靜態資源的打包工具,確實好用,原理也很簡單,比以AMD、CMD爲標準的模塊加載器好用多了,難怪玉伯說要給seajs、requirejs立一塊墓碑了。在講webpack這以前簡單說下前端模塊化歷程。css

模塊化1.0

// a.js
(function(){
// todo a
})();

// b.js
(function(){
// todo b
})();

// index.html
<script src="a.js"></script>
<script src="b.js"></script>

以上是在CommonJS規範出來以前的編碼方式,你們應該很是熟悉。另外還有經過命名空間的方式來進行模塊化,其實也沒有真正的解決問題。html

模塊化2.0

// a.js
define(function(require, exports, module){
    // todo a
})

// b.js
define(function(require, exports, module){
    var a = require('./a');
    // todo b
})

// index.html
<script src="sea.js"></script>
<script>
    sea.use(['b.js'], function(b){
    // todo
    });
</script>

這就是過去幾年你們都很是熟悉的模塊化方式,在發佈上線時經過構建工具,提取模塊id、以及模塊的依賴,合併壓縮代碼等等構建工做。前端

可是用requirejs或者seajs,仍是有些問題:webpack

  • 只能模塊化加載js、css,並且還不幫你合併,須要本身寫插件去作合併的邏輯,組件化並不簡單。git

  • 異步加載也很差用,尤爲是部署時,hash後的資源路徑自動替換還比較麻煩。github

  • 程序仍是要依賴seajs這個幾kb的庫,總感受有點多餘。web

模塊化3.0

webpack出來後,優雅的解決了不少問題,而且簡單好用。它能把各類資源,例如JS(含JSX)、coffee、樣式(含less/sass)、html、圖片等靜態資源都做爲模塊來處理。同時擁有異步加載的能力,很是適用於大型複雜的webapp的場景。webpack有如下特色:ajax

  • 兼容AMD/CMD的模塊加載npm

  • js模塊的寫法遵循CommonJS規範gulp

  • 模塊化全部靜態資源(JS、CSS、html、圖片、字體等)

  • 開發、部署便捷,能替代大部分 grunt/gulp 的工做,好比打包、壓縮混淆、圖片轉base64等

經過一個簡單的配置文件便可搞定這些。

實戰

結合項目自己的特色,部分功能webpack沒法作到,最終選用gulp+webpack方式來支撐前端的工做流,項目需求:

  • 最小化配置項(webpack.config.js)

  • 全部資源使用增量發佈策略,文件名所有 md5 版本化

  • 支持多種模塊化策略,使用 webpack 進行模塊化打包

  • 自動替換 html/js 內部資源引用路徑,替換爲 cdn/md5 版本化路徑

  • 輕鬆支持 js資源內嵌到頁面

  • 開發時監聽文件,自動上傳到開發機

因爲css是由重構同窗寫,雪碧圖,壓縮、發佈等等都由他們來作,因此沒有考慮加入到構建流程中來。

項目目錄結構以下:

src/
    js/
    widget/
    css/
    img/
    project_tpl/
    gulpfile.js
    app/
        js/
        css/
        img/
        index.html
        tpl/
        webpack.config.js
        gulpfile.js
app/

src目錄下是項目的源代碼,每一個目錄(app)即爲一個項目,爲了儘可能避免衝突,一我的開發並維護一個項目下的代碼。

project_tpl爲項目的模板,經過gulp新建項目,完成一些初始化配置,初始化後基本無需配置便可進行項目開發。

js/css/img爲站點的一些公共資源模塊,widget爲公共基礎組件。

app項目下爲業務的css、js、tpl目錄,tpl爲前端模板目錄,能夠經過webpack的html-loader插件加載。

gulpfile.js、webpack.config.js 爲項目的構建工具配置文件。

這樣一個項目的腳手架搭建完成,能夠開始爲項目添磚加瓦了。能夠點擊這裏看github上的例子

webpack配置文件

var webpack = require('webpack');
var globalConfig = require('../global.config');
var commonJSEntry = globalConfig.jsCommon;
var path          = require('path');

module.exports = {
// 若是項目有多個HTML,或者多個入口
// 配置js入口文件,base是公共庫配置,除了打包工具自動化抽取共用的模塊,也能夠自定義配置哪些模塊爲共用的。
entry:{
    index: './js/index',
    base: commonJSEntry,
},
// 文件產出目錄
output:{
    filename:'../test/js/[name].js',
    // 異步加載的chunk,命名規則,chunk我暫時理解爲從合併的代碼裏分離出來的代碼塊,在處理非首屏邏輯,或者異步加載邏輯能夠用這個。只要在js代碼中用require.ensure來異步加載模塊便可。
    chunkFilename: '../test/js/[chunkhash:8]_chunk.js',
    // 資源文件的CDN前綴
    publicPath: debug ? "" : '//cdn.xxx.com/webpack/test/'
},
resolve: {
    // 模塊的別名,一般能夠爲第三方模塊
    alias: {
        ajax: "../../js/base/ajax",
        dom: "../../js/base/dom"
    },
    // root模塊的根路徑,能夠指定從哪裏找模塊,能夠爲數組[]
    // 這樣在模塊依賴的時候就不要寫require(../../../xx.js)
    // 直接爲require(xx)
    root: path.resolve('../../js')
},
// 模塊加載器,加載不一樣類型的文件,須要下載或者開發loader插件,如下爲加載html模塊的加載器
loader: [
    {test: /\.html$/,   loader: 'html'}
],
/*
一、能夠經過配置文件指定哪位模塊爲公共模塊,這樣功能模塊能夠長期緩存。
解釋下這個插件的意思,就是提取公共的chunk,base對應了entry中的配置,"../test/js/common.js"是產出的路徑,也就是將commonJSEntry中的配置模塊合併成一個common.js文件。
二、webpack也可自動提取頁面之間公用的代碼做爲公共部分。下面的代碼便是自動提取公共代碼了。
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin("../test/js/common.js");
*/
plugins: [
    new webpack.optimize.CommonsChunkPlugin("base", "../test/js/common.js")
]
};

因爲項目的特性,沒有用到熱插拔,因此就不進行講解了。

gulp跑起來

webpack最終是當作gulp的一個插件來運行,讀取的上述的webpack配置文件。

gulp.task('watch-html', function() {
    // upload
});

gulp.task('watch-module', function() {
    var watchPath = [
        '../js/**',
        '../css/**',
        '../widget/**',
        'js/**',
        'css/**',
        'tpl/**'
    ];
    gulp.watch(watchPath, function(event){
        gulp.src(watchPath)
            .pipe(webpack(require('webpack.config')))
            .pipe(gulp.dest(releaseRelativePath + projectName))
            .pipe(upload(opt, function(err, data){})
    })
});

gulp.task('default', ['watch-html', 'watch-module']);

// release build webpack module
gulp.task('release-module', function() {
    var releasePath = [
        '../js/**',
        '../css/**',
        '../widget/**',
        'js/**',
        'css/**',
        'tpl/**'
    ];
    return gulp.src(watchPath)
                .pipe(webpack(require('webpack.config')))
                .pipe(uglify())
                .pipe(hash())
                .pipe(rename(function(path) {
                // 獲取當前的日期,將發佈文件已日期歸類,更方便查找文件
                    path.dirname = path.dirname + '/' + year + month + day;
                })
                .pipe(gulp.dest(releaseRelativePath + projectName))
                .pipe(upload(opt, function(err, data){})
    })
});

// release build html
gulp.task('release', ['release-module'], function(){
    gulp.src(['**/*.html'])
        .pipe(parseHtml(releaseRelativePath + projectName, CDN_URL))
        .pipe(gulp.dest(releaseRelativePath + projectName))
        .pipe(upload(opt, function(err, data){})
        .pipe(uploadToCDN())
})

(以上用到的部分npm模塊是自定義的)。

一、項目開始前,經過gulp init -p YourProjectName 來初始化項目
二、開發和發佈兩套命令,開發:gulp,發佈:gulp release
三、須要自行編寫gulp插件來替換html中引用資源的路徑,原理也很簡單,在構建webpack模塊後,將產出的文件列表與原文件的映射關係保存在數組,查找html中引用的js路徑,替換成hash後就能夠了。

經過以上方法,就能夠知足咱們項目以前的需求,基本上作到自動化,自動構建,自動發佈腳本,html文件走內部發布系統發佈。

相關文章
相關標籤/搜索