
[前言]:因為最近在搞百度地圖API的時候用到了webpack的externals,才發現我之前都只是用webpack做一些搭建完項目后的“收尾工作”——即打包,而沒有把它納入到項目開發的“主體過程”中來,真是“物不盡其用”。於是就有了我今天的這篇學習文章:利用webpack-dev-server搭建一個webpack的服務器
參考資料:
webpack-dev-server的github地址:https://github.com/webpack/webpack-dev-server
webpack1官方文檔http://webpack.github.io/docs/webpack-dev-server.html(推薦看2的文檔)
webpack2官方文檔https://webpack.js.org/configuration/dev-server/#devserver(推薦讀這個)
提綱:
1.復習webpack的知識
2.詳解webpack-dev-server的配置屬性
3.webpack-dev-server的自動刷新和模塊熱替換機制
4.webpack下配置服務器的三種方式
復習一下webpack的知識
我將目錄結構簡化之后長這樣:

我的webpack.config.js:
var webpack = require('webpack') var path =require('path') module.exports = { entry:{ app:path.join(__dirname,'src','index.js') }, output:{ filename:'bundle.js', path:path.join(__dirname,'dist') } }
我的src/index.js:
require('./a.js') require('./b.js') console.log('我是index.js')
我的src/a.js:
console.log('我是a.js')
我的src/b.js:
console.log('我是b.js')
我的dist/index.html:
/*其他部分省略*/ <body> <script src="./bundle.js"></script> </body>
當我們在終端運行“webpack”命令后,目錄變為:

一張圖復習一下webpack的機制:

OK,下面,讓我們開始搭建一個服務器吧:
怎么用最簡單的方式搭建一個服務器?
1.你需要一個安裝一個模塊
在終端中進入項目目錄下,敲下npm install webpack-dev-server --save-dev回車
2.在終端運行一段命令:
node_modules/.bin/webpack-dev-server(網上有的說直接輸webpack-dev-server那是錯的)
成功!輸出的是這一段信息:

然后進入默認的localhost:8080頁面:

服務器的根目錄就是我們工程的目錄
到這里,我們要做的第一步就成功啦!
進入dist后,我們發現報了這樣一段錯誤:

what?沒有找到bundle.js?
所以我們要在webpack.config.js里寫一下配置:
module.exports = { /*這里省略entry和output,參照上面寫的內容*/ devServer: { contentBase: path.join(__dirname, "dist") } }
保存,后打開頁面看控制台,報錯已經消失了!正確打印了被打包的文件內容:

詳解webpack-dev-server的配置屬性
1.devServer.contentBase
contentBase是我們今天要講的第一個webpack-dev-server的配置屬性,那么,contentBase做了什么事情呢?——它指定了服務器資源的根目錄,如果不寫入contentBase的值,那么contentBase默認是項目的目錄。
在上面例子中產生錯誤和后來解決錯誤的原因:
產生錯誤:因為bundle.js被"放在了"我們的項目根目錄里,在dist/html里<script src="./bundle.js"></script>此時顯然不能根據路徑找到bundle.js
解決錯誤:通過配置contentBase: path.join(__dirname, "dist")將bundle.js"放在了"dist目錄下,此時bundle.js和dist/index.html位於同一目錄下,通過 src="./bundle.js"自然就找到bundle.js了
webpack打包和webpack-dev-server開啟服務的區別——
webpack輸出真實的文件,而webpack-dev-server輸出的文件只存在於內存中,不輸出真實的文件!(注意下面兩張圖的區別)
webpack:當我們在終端運行"webpack"后:

webpack-dev-server:當我們在終端運行"node_modules/.bin/webpack-dev-server后:

這也就是我在上面的闡述中將bundle.js"放在了"加上雙引號的原因
2.devServer.port
port配置屬性指定了開啟服務的端口號:
devServer: { port:7000 }
設置端口號為7000:
運行:node_modules/.bin/webpack-dev-server

這個時候就不是默認的8080的端口了,而是我們設置的7000
3.devServer.host
host設置的是服務器的主機號:
修改配置為:
devServer: { contentBase: path.join(__dirname, "dist"), port:7000, host:'0.0.0.0' }
此時localhost:7000和0.0.0.0:7000都能訪問成功

4.devServer.historyApiFallback
在文檔里面說的很清楚,這個配置屬性是用來應對返回404頁面時定向到特定頁面用的(the index.html page will likely have to be served in place of any 404 responses)
在dist目錄下新增一個HTML頁面:
/*剩下的都是很常規的HTML內容,故省略*/ <p>這里是404界面</p>
我們把webpack.config.js修改如下:
module.exports = { /*這里省略entry和output,參照上面寫的內容*/ devServer: { contentBase: path.join(__dirname, "dist"), historyApiFallback:{ rewrites:[ {from:/./,to:'/404.html'} ] } } }
打開頁面,輸入一個不存在的路由地址:

5.devServer.overlay
這個配置屬性用來在編譯出錯的時候,在瀏覽器頁面上顯示錯誤,默認是false,可設置為true
首先我們人為制造一個編譯錯誤:在我們尚未配置babel loader的項目里使用ES6寫法:
在src/index.js里寫入“const a”
在shell里提示編譯錯誤:

但在瀏覽器里沒有提示:

所以我們把webpack.config.js修改為:
module.exports = { /*這里省略entry和output,參照上面寫的內容*/ devServer: { contentBase: path.join(__dirname, "dist"), overlay: true } }
再重新運行node_modules/.bin/webpack-dev-server,瀏覽器上把錯誤顯示出來了

6.devServer.stats(字符串)
這個配置屬性用來控制編譯的時候shell上的輸出內容,
我們沒有設置devServer.stats時候編譯輸出是這樣子的:
(其中看起來有許多看似不重要的文件也被打印出來了)

stats: "errors-only"表示只打印錯誤:
我們把配置改成:
devServer: { contentBase: path.join(__dirname, "dist"), stats: "errors-only" }
因為只有錯誤才被打印,所以,大多數信息都略過了

除此之外還有"minimal","normal","verbose",這里不多加贅述
7.devServer.quiet
當這個配置屬性和devServer.stats屬於同一類型的配置屬性
當它被設置為true的時候,控制台只輸出第一次編譯的信息,當你保存后再次編譯的時候不會輸出任何內容,包括錯誤和警告
來做個對比吧:
quiet:false(默認):
第一次編譯:

第二次編譯(當你保存的時候)

quiet:true
第一次編譯同上
第二次編譯什么都不輸出
【吐槽】這樣看的話感覺這個配置好像只有負面作用呢.....
8.devServer.compress
這是一個布爾型的值,當它被設置為true的時候對所有的服務器資源采用gzip壓縮
采用gzip壓縮的優點和缺點:
優點:對JS,CSS資源的壓縮率很高,可以極大得提高文件傳輸的速率,從而提升web性能
缺點:服務端要對文件進行壓縮,而客戶端要進行解壓,增加了兩邊的負載
9.devServer.hot和devServer.inline
在這之前,首先要說一下的是webpack-dev-server的自動刷新和模塊熱替換機制
webpack-dev-server的自動刷新和模塊熱替換機制
這兩個機制是緊緊聯系在一起的
從外部角度看——自動刷新
當我們對業務代碼做了一些修改然后保存后(command+s),頁面會自動刷新,我們所做的修改會直接同步到頁面上,而不需要我們刷新頁面,或重新開啟服務
(The webpack-dev-server supports multiple modes to automatically refresh the page)
從內部角度看——模塊熱替換
在熱替換(HMR)機制里,不是重載整個頁面,HMR程序會只加載被更新的那一部分模塊,然后將其注入到運行中的APP中
(In Hot Module Replacement, the bundle is notified that a change happened. Rather than a full page reload, a Hot Module Replacement runtime could then load the updated modules and inject them into a running app.)
webpack-dev-server有兩種模式可以實現自動刷新和模塊熱替換機制
1. Iframe mode
(默認,無需配置)
頁面被嵌入在一個iframe里面,並且在模塊變化的時候重載頁面
2.inline mode(需配置)添加到bundle.js中
當刷新頁面的時候,一個小型的客戶端被添加到webpack.config.js的入口文件中
例如在我們的例子中,在使用inline mode的熱替換后,相當於入口文件從
entry:{ app:path.join(__dirname,'src','index.js') }
變成了:
entry:{ app:[path.join(__dirname,'src','index.js'), 'webpack-dev-server/client?http://localhost:8080/' ] }
從一個入口變成了兩個入口,並實現刷新
那怎么才能inline mode模式的刷新呢?
你需要做這些:
1在配置中寫入devServer.hot:true和devServer.inline:true
2增加一個插件配置webpack.HotModuleReplacementPlugin()
例如:
var webpack = require('webpack') module.exports = { /*省略entry ,output等內容*/ plugins:[ new webpack.HotModuleReplacementPlugin() ], devServer: { inline:true, hot:true } }
打開頁面:

如果有上面兩行輸出則表明你已經配置成功
現在還有一個問題,那就是每次直接輸入node_modules/.bin/webpack-dev-server來啟動服務器對我們來說簡直就是莫大的痛苦,那么怎么對這一過程進行簡化呢?
答案:把這個運行腳本寫到package.json里就行了!
這里我把我的package.json寫成:
{ "name": "webpackTest2", "dependencies": {}, "devDependencies": {}, "scripts": { "start": "node_modules/.bin/webpack-dev-server" } }
在終端運行npm start:

運行成功!
配置服務的三種方式
1在webpack.config.js輸出對象中的devServer屬性中寫配置(也就是我們上述所有例子的做法)
2寫在package.json中,寫在node 命令對應的腳本中,例如我們可以寫成:
"scripts": {
"start": "node_modules/.bin/webpack-dev-server --port 8000 --inline true "
}

(但這顯然並不是一個值得推薦的方式)
3使用純node的API實現,下面是一個官方給的例子
var config = require("./webpack.config.js"); config.entry.app.unshift("webpack-dev-server/client?http://localhost:8080/"); var compiler = webpack(config); var server = new WebpackDevServer(compiler, { /*我們寫入配置的地方*/ }); server.listen(8080);