從去年短期內對現有系統的改造到現在穩定實施,已經好幾個月,這套流程知足了平常前端開發的流程。因爲以前項目組的模塊化自己作的不是很好,基本算是推到一半重來,雖然陣痛,但回顧起來確實很是值得。webpack,簡單來講就是前端靜態資源的打包工具,確實好用,原理也很簡單,比以AMD、CMD爲標準的模塊加載器好用多了,難怪玉伯說要給seajs、requirejs立一塊墓碑了。在講webpack這以前簡單說下前端模塊化歷程。css
// a.js (function(){ // todo a })(); // b.js (function(){ // todo b })(); // index.html <script src="a.js"></script> <script src="b.js"></script>
以上是在CommonJS規範出來以前的編碼方式,你們應該很是熟悉。另外還有經過命名空間的方式來進行模塊化,其實也沒有真正的解決問題。html
// 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
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上的例子。
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") ] };
因爲項目的特性,沒有用到熱插拔,因此就不進行講解了。
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文件走內部發布系統發佈。