前端自動化構建是當下的熱門,我記得2014年的時候,前端的自動化構建,大多是用在javascript的合並、壓縮、語法檢查、coffeescript,Sass,LESS轉換上,構建工具也有很多,比如ant,grunt,gulp等,二次封裝的工具也有很多,比如百度的FIS,國外的Yeoman。2016年以后,隨着es6,es7,Node的興起,前端又發生了翻天覆的變化,特別是移動端的H5最為明顯,以前切個圖,在PC上預覽測試就可以發布的時代,在移動端就不靈驗了,在手機端預覽至少要搭建一個http服務器,比如http://192.168.0.2/index.html。在手機端輸入網址不方全,通常會將網址做成一個二維碼,然后用手機掃一下就可以打開預覽。我們每改一下樣式,就在手機上點一下刷新或電腦上按一下F5,這在最初的時候,也不覺得有什么問題,因為拿到我手上的靜態頁,通常由切片的同事做好了兼容性測試,需要一邊刷新瀏覽器,一邊改樣式的機會不多。隨着我們嘗試用Less,stylus,這樣的css工具,一方面,需要用到gulp這樣的工具在后台自動監聽我們的樣式改動,另一方面,手動刷新的時候,gulp的腳本未必轉換完了。這時候迫切需要瀏覽器自動刷新。
總的來說,需求就兩點,一是需要一個http服務器,來供手機訪問靜態資源,另一個是監聽代碼的改動並自動刷新瀏覽器。要滿足這兩個需求的第三方工具,應當不難找,事實上像fis,yeoman,vuecli這樣的工具應當都可以做到。可是我覺得它們都太復雜了,雖然我只用到其中一點點功能,但是我不得不仔細的通讀他們的文檔,找到自己需要的功能。有時候,官方說三分鍾入門,可是我花三十分鍾了還沒有入門。或許我幾天之后,我好不容易入門了,結果周圍同事又給我推薦另一個工具,說比我手上這好一千倍,於是我又去學另一個工具。如此,很容易陷入不同工具之間的學習。更要命的是,轉了一圈回來,其實我用的那一點功能,用哪一個工具都差不多,既不像A同學說的那么差勁,也不像B同學說的那么好。
2015年6月份的時候,我們的項目開始用express+React.js做服務端渲染,Redux做狀態管理。我們搭建了一套開發框架,以前那些自動化的工具,都不能完全滿足我的需求了。js和css的改動越來越頻繁,而且node不像php代碼那樣,改動之后,服務器會自動更新,它需要手動重啟node進程,另一方面,自動刷新瀏覽器,會導致redux的action日志看不到。這個時候,瀏覽器的自動刷新已經滿足不了我們的需求。我們需要瀏覽器在不刷新的情況下,局部更新我改過的代碼。這也就是“熱替換”(HMR)這個概念的來由。很多新同事搞不清什么是自動刷新,什么是熱替換。熱替換聽起來,有點像是ajax的效果,不過,ajax是點擊某個動作或觸發某個事件之后由js腳本觸發,而“熱替換"是在我們改動了代碼的時候觸發(也就是CTRL+S保存的時候). 自動刷新就是指不用手動去按F5.
要實現自動刷新和靜態服務器,最簡的就是用webpack-dev-server . 如果之前用過webpack,那么webpack-dev-server則很容易接受,如果還沒有用過webpack,那么我覺得很有必要去看看。網上有很多介紹webpack及webpack-dev-server的文章,我覺得要這實現這個需求,只要兩步就可以:
1.安裝webpack-dev-server
2. 運行webpack-dev-server --inline --hot
webpack.config.js的配置:
var path = require("path");
var webpack = require('webpack');
//var ExtractTextPlugin = require("extract-text-webpack-plugin");
var node_modules_dir = path.resolve(__dirname, 'node_modules');
module.exports = {
entry:[
//'webpack-dev-server/client?http://localhost:8081',
//'webpack/hot/dev-server',
'./src/app.jsx','./src/app.css'
],
module: {
loaders: [{
test: /\.es6|jsx$/,
exclude: [node_modules_dir],
loaders: ['react-hot','babel-loader'],
},
{
test:/\.css$/,
loaders:['style', 'css']
}]
},
output: {
path: path.resolve(__dirname, "dist"),
publicPath:"/assets/",
filename: 'bundle.js'
},
resolve: {
extensions: ['', '.js','.es6','.jsx']
},
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"development"'
}),
//new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
}
通過命令行的方式執行webpack-dev-server , 重點要說的是--hot這個參數,它就是啟動熱替換用的。它會自動給plugins插入new webpack.HotModuleReplacementPlugin() 因此不需要人為的再配置這個插件了。所以我在webpack.config.js中注釋掉了這一行。 然后entry中也不需要加webpack/hot/dev-server和webpack-dev-server/client,記住:命令行方式運行webpack-dev-server會自動替你完成這些工作。不要人雲亦雲的去添加這些注釋的內容,我看到甚至還有用express配合webpack-hot-middleware,webpack-dev-middleware 之類的用法,那更加復雜了,對於只想要實現自動刷新和熱替換來說,命令行方式運行,配合 --hot --inline 這兩個參數是最簡單有效的做法。其它復雜的配置和用法,有它的特殊的適用場景,比如同時需要兩個服務器,一個充當數據接口,一個充當靜態文件服務器, 我們希望webpack-dev-server通過node API的方式運行。這個有點復雜,用到這么復雜的情形的,一般都是能自己搞定這個配置的。有這方面需求的,參考demo8的代碼。
填坑記
extract-text-webpack-plugin這個插件,它可以將模塊中的樣式部分提出來,單獨打包成文件,但是它只適用於生產模式。開始我也沒有注意,在開發模式下也用了,還覺得很爽,直到有一次,我發現更改樣式的時候,瀏覽器居然自動刷新了,而我期望是熱替換。這時我才理解,只適用於生產模式,不是說它在開發模式下,就不生效了,而是它配合 --hot參數的時候,不會有熱替換的效果。
簡單的一行小字,沒有細看,結果走了很多彎路。俗話說,方向反了,停止就是進步。 對於普通的js模塊來說,熱替換需要自己寫loader插件,如果是react,vue則官方會提供熱替換的loader,比如react的 react-hot-loader 。如果沒有用這些可以支持熱替換的插件,那么默認的就是瀏覽器的自動刷新效果。css的話,只要加上css-loader,style-loader就可以了。最后再補一刀,output:中要配publicPath
output: {
path: path.resolve(__dirname, "dist"),
publicPath:"/assets/",
filename: 'bundle.js'
},
在html中也要寫publickPath的地址:

/assets/這個虛擬目錄中的文件實際上是保存在內存中的,這樣就為模塊的熱替換提供了可能。如果寫成實際的產出目錄,是怎么也不會看到熱替換的效果的,甚至連自動刷新都不會出現.
源碼地址:https://github.com/bjtqti/how_to_use_webpack/tree/master/demo18
驗證是否成功開啟HMR
首先看控制台的提示:
出現這個HMR說明配置方面是對的,但是光有這個還不行,如果模塊沒有熱替換的loader,那么就會觸發失敗,失敗的結果就是導致瀏覽器刷新。這也是為什么有時候明明出現這個熱替換的標志了,還是會出現瀏覽器刷新。如果添加了支持熱替換的loader,那么當我們保存更改的時候,瀏覽器只會默默的更新變化區域,而不會產生刷新。同時,控制台的log也會累積,不會被清空。
手機預覽
辛苦做出來的H5頁面,在電腦上模擬顯示的效果,都未必可靠,最后都需要放到手機上進行真機檢測。通過webpack-dev-server生成的http服務器,可以實現通過手機訪問。比如電腦的ip是192.168.1.122, 默認情況下,那么通過http://191.168.1.122:8080就可以在手機上打開我們的H5頁面。如果打不開,請檢查兩個地方:
1. webpack-dev-server 添加 --host 0.0.0.0 參數
2. 保證電腦和手機處於同一個無線網絡,即電腦可以ping通手機。
打包發布
在手機上檢測完好之后,就需要把代碼進行合並,壓縮,添加hash標記,小圖片,字體文件轉base64,提取共公代碼等等. 一方面可以簡單的使用
webpack -p 也可自行配置webpack.config.js,演示:
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': '"production"'
}),
new ExtractTextPlugin('css/[name].css'),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
output: {
comments: false
},
sourceMap: false
}),
new webpack.optimize.CommonsChunkPlugin('vendor','js/vendor.js'),
new webpack.optimize.OccurenceOrderPlugin(true),
new webpack.NoErrorsPlugin()
]
到此,整個構建工作就完成了。
小結
關於webpack及webpack-der-server的教程很多,但是很多文章發布的較早,不一定適用你現在用的版本。遇到問題的時候,我建議先看看錯誤提示,然后查看官方文檔。先從簡單的用法開始去動手嘗試,一定要嘗試,不要覺得很簡單,只在腦海里想當然的運行一遍就以為學會了,俗話說,實踐出真知,同樣的問題,不同的環境(windows/mac/node版本...),會有不同的情況,只有自己一一去試過了,遇到問題心中有數,才不會慌。最后,要把學到的內容結合實際的項目去運用看看,有哪些功能對目前的開發有幫助,哪些功能還不是很了解,需要更深入的學習。遇到實在有自己找不到解決辦法的問題,再去網上查,個人覺得stockoverflow還算比較靠譜。最后,要記得把自己遇到的坑整理成章,分享是一種美德。
