1. contentBase
該配置項(xiàng)指定了服務(wù)器資源的根目錄,如果不配置contentBase的話,那么contentBase默認(rèn)是當(dāng)前執(zhí)行的目錄,一般是項(xiàng)目的根目錄。
可能如上解析還不夠清晰,沒(méi)有關(guān)系,我們下面還是先看下我整個(gè)項(xiàng)目的目錄結(jié)構(gòu),然后進(jìn)行相關(guān)的配置,使用contentBase配置項(xiàng)再來(lái)理解下:
### 目錄結(jié)構(gòu)如下:
demo1 # 工程名
| |--- dist # dist是打包后生成的目錄文件
| |--- node_modules # 所有的依賴(lài)包
| |--- js # 存放所有js文件
| | |-- demo1.js
| | |-- main.js # js入口文件
| |
| |--- webpack.config.js # webpack配置文件
| |--- index.html # html文件
| |--- styles # 存放所有的css樣式文件
| |--- .gitignore
| |--- README.md
| |--- package.json
index.html 代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="dist/main.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="app"></div>
<script src="dist/bundle.js"></script>
</body>
</html>
main.js 代碼如下:
require('../styles/main.css');
import demo1 from './demo1.js';
demo1.js 代碼如下:
console.log(111);
webpack配置代碼如下:
const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist'
},
mode: 'development',
module: {
rules: [
{
// 使用正則去匹配要用該loader轉(zhuǎn)換的css文件
test: /\.css$/,
loaders: ExtractTextPlugin.extract({
// 轉(zhuǎn)換 .css文件需要使用的Loader
use: ['css-loader']
})
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]'
}
}
]
},
resolve: {
// modules: ['plugin', 'js']
},
plugins: [
new ExtractTextPlugin({
// 從js文件中提取出來(lái)的 .css文件的名稱(chēng)
filename: `main.css`
})
]
};
package.json 配置代碼如下:
"scripts": {
"dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
"build": "webpack --progress --colors"
}
運(yùn)行 npm run dev后,一切正常成功后,在瀏覽器下 運(yùn)行 http://localhost:8080/ 即可在控制臺(tái)看到 打印出 111 了。
如上是沒(méi)有使用devServer配置的情況下。 下面我們來(lái)看下使用 devServer配置.
在webpack配置加上如下配置,即配置項(xiàng)指定了服務(wù)器資源的根目錄。比如我們打包后的文件放入 dist目錄下。
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist")
},
}
如上配置完成后,我們?cè)龠\(yùn)行 npm run dev, 再在地址欄中 運(yùn)行 http://localhost:8080/ 后看到如下信息:

也就是說(shuō) 配置了 contentBase后,服務(wù)器就指向了資源的根目錄,而不再指向項(xiàng)目的根目錄。因此再訪問(wèn) http://localhost:8080/index.html是訪問(wèn)不到的。但是訪問(wèn)http://localhost:8080/bundle.js 該js文件是可以訪問(wèn)的到的。
2. port
該配置屬性指定了開(kāi)啟服務(wù)器的端口號(hào),比如如下配置:
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 8081
},
}
配置完成后,再運(yùn)行打包命令 npm run dev 后,可以看到如下圖所示:

現(xiàn)在我們可以通過(guò) 如下地址 http://localhost:8081/也可以訪問(wèn)了,也就是說(shuō) 通過(guò)port配置,端口號(hào)從默認(rèn)的8080改成8081了。
3. host
該配置項(xiàng)用于配置 DevServer的服務(wù)器監(jiān)聽(tīng)地址。比如想讓局域網(wǎng)的其他設(shè)備訪問(wèn)自己的本地服務(wù),則可以在啟動(dòng)DevServer時(shí)帶上 --host 0.0.0.0.
host的默認(rèn)值是 127.0.0.1, 下面我們也簡(jiǎn)單的配置下 host 屬性。
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0'
}
}
配置完成后,再運(yùn)行打包命令 npm run dev 后,可以看到如下圖所示:

我們?cè)L問(wèn) http://0.0.0.0:8081/ 可以訪問(wèn)的到了,其他局域網(wǎng)的同學(xué)應(yīng)該也能訪問(wèn)的到吧。
4. headers
該配置項(xiàng)可以在HTTP響應(yīng)中注入一些HTTP響應(yīng)頭。 比如如下:
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
}
}
}
如上配置完成后,打包下,刷新下瀏覽器,可以看到請(qǐng)求頭加了上面的信息,如下所示:

5. historyApiFallback
該配置項(xiàng)屬性是用來(lái)應(yīng)對(duì)返回404頁(yè)面時(shí)定向跳轉(zhuǎn)到特定頁(yè)面的。一般是應(yīng)用在 HTML5中History API 的單頁(yè)應(yīng)用,比如在訪問(wèn)路由時(shí)候,訪問(wèn)不到該路由的時(shí)候,會(huì)跳轉(zhuǎn)到index.html頁(yè)面。
我們現(xiàn)在在dist目錄下 新建一個(gè)index.html, 代碼如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">歡迎你們來(lái)訪問(wèn)我</div>
</body>
</html>
為了使配置項(xiàng)生效,我們只需要設(shè)置該 屬性值為true即可; 如下配置:
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
historyApiFallback: true
},
}
現(xiàn)在我們來(lái)訪問(wèn) http://0.0.0.0:8081/home 這個(gè)不存在的路由時(shí),會(huì)發(fā)生什么?如下所示:

如上可以看到,當(dāng)不存在該路由的時(shí)候,通過(guò)該配置項(xiàng),設(shè)置屬性值為true的時(shí)候,會(huì)自動(dòng)跳轉(zhuǎn)到 index.html下。
當(dāng)然如上只是簡(jiǎn)單的配置下,當(dāng)然我們也可以手動(dòng)通過(guò) 正則來(lái)匹配路由,比如訪問(wèn) /user 跳轉(zhuǎn)到 user.html,訪問(wèn) /home 跳轉(zhuǎn)到 home.html, 如下配置:
當(dāng)然我們需要在 dist 目錄下 新建 home.html 和 user.html 了,如下基本配置:
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
historyApiFallback: {
// 使用正則來(lái)匹配路由
rewrites: [
{ from: /^\/user/, to: '/user.html' },
{ from: /^\/home/, to: '/home.html' }
]
}
},
}
重新運(yùn)行打包下, 繼續(xù)訪問(wèn)http://0.0.0.0:8081/home 和 http://0.0.0.0:8081/user即可看到能訪問(wèn)得到對(duì)應(yīng)的頁(yè)面了。
6. hot
該配置項(xiàng)是指模塊替換換功能,DevServer 默認(rèn)行為是在發(fā)現(xiàn)源代碼被更新后通過(guò)自動(dòng)刷新整個(gè)頁(yè)面來(lái)做到實(shí)時(shí)預(yù)覽的,
但是開(kāi)啟模塊熱替換功能后,它是通過(guò)在不刷新整個(gè)頁(yè)面的情況下通過(guò)使用新模塊替換舊模塊來(lái)做到實(shí)時(shí)預(yù)覽的。
我們可以在 devServer中 配置 hot: true 即可:如下配置代碼:
module.exports = {
devServer: {
contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
historyApiFallback: {
// 使用正則來(lái)匹配路由
rewrites: [
{ from: /^\/user/, to: '/user.html' },
{ from: /^\/home/, to: '/home.html' }
]
},
hot: true
}
}
當(dāng)然我們也可以在scripts命令行中配置,比如我項(xiàng)目中在package.json中的scripts配置如下:
"scripts": {
"dev": "webpack-dev-server --progress --colors --devtool source-map --hot --inline",
"build": "webpack --progress --colors"
}
7. inline
webpack-dev-server 有兩種模式可以實(shí)現(xiàn)自動(dòng)刷新和模塊熱替換機(jī)制。
1. iframe
頁(yè)面是被嵌入到一個(gè)iframe頁(yè)面,并且在模塊變化的時(shí)候重載頁(yè)面。
可能如上解釋?zhuān)覀冞€不能完全能理解到底是什么意思,沒(méi)有關(guān)系,我們繼續(xù)來(lái)看下配置和實(shí)踐效果。
module.exports = {
devServer: {
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
inline: false
},
}
如上代碼配置 inline: false 就是使用iframe模式來(lái)重載頁(yè)面了。我們的目錄結(jié)構(gòu)還是上面的那種結(jié)構(gòu),然后我們只需要在webpack中所有
配置如下:
const path = require('path');
// 提取css的插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './js/main.js',
output: {
filename: 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist'),
publicPath: '/dist'
},
mode: 'development',
module: {
rules: [
{
// 使用正則去匹配要用該loader轉(zhuǎn)換的css文件
test: /\.css$/,
loaders: ExtractTextPlugin.extract({
// 轉(zhuǎn)換 .css文件需要使用的Loader
use: ['css-loader']
})
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader',
options: {
limit: 10000,
name: '[name].[ext]'
}
}
]
},
resolve: {
// modules: ['plugin', 'js']
},
devServer: {
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
inline: false
},
plugins: [
new ExtractTextPlugin({
// 從js文件中提取出來(lái)的 .css文件的名稱(chēng)
filename: `main.css`
})
]
};
然后當(dāng)我們?cè)诿钚兄?,輸?webpack-dev-server 后 回車(chē),可以看到如下圖所示:

接著我們?cè)跒g覽器下 輸入 http://0.0.0.0:8081/webpack-dev-server/ 地址后 回車(chē),即可看到頁(yè)面,我們查看源代碼的時(shí)候,會(huì)看到嵌入了一個(gè)iframe頁(yè)面,如下圖所示:

當(dāng)我們重新修改main.js 或 它的依賴(lài)文件 demo1.js 的時(shí)候,保存后,它也會(huì)自動(dòng)重新加載頁(yè)面,這就是使用 iframe 模式來(lái)配置加載頁(yè)面的。
iframe 模式的特點(diǎn)有:
1. 在網(wǎng)頁(yè)中嵌入了一個(gè)iframe,將我們自己的應(yīng)用代碼注入到 這個(gè) iframe中去了。
2. 在頁(yè)面頭部會(huì)有一個(gè) App ready. 這個(gè)提示,用于顯示構(gòu)建過(guò)程的狀態(tài)信息。
3. 加載了 live.bundle.js文件,還同時(shí)包含了 socket.io的client代碼,進(jìn)行了 websocket通訊,從而完成了自動(dòng)編譯打包,頁(yè)面自動(dòng)刷新功能。
我們看下請(qǐng)求的所有文件有如下:

2. inline 模式
開(kāi)啟模式,只需要把上面的配置代碼變?yōu)?inline: true即可,它在構(gòu)建變化后的代碼會(huì)通過(guò)代理客戶端來(lái)控制網(wǎng)頁(yè)刷新。
如上配置后,我們運(yùn)行 webpack-dev-server 命令后,如下所示:

接著我們?cè)诘刂窓谥?http://0.0.0.0:8081/運(yùn)行下 就可以訪問(wèn)到 項(xiàng)目中的根目錄 index.html了,當(dāng)我們修改入口文件的代碼保存也一樣
能實(shí)時(shí)刷新,其實(shí)效果是一樣的。
inline模式的特點(diǎn)有:
1. 構(gòu)建的消息在控制臺(tái)中直接顯示出來(lái)。
2. socket.io的client代碼被打包進(jìn)bundle.js當(dāng)中,這樣就能和websocket通訊,從而完成自動(dòng)編譯工作,頁(yè)面就能實(shí)現(xiàn)自動(dòng)刷新功能。
3. 以后的每一個(gè)入口文件都會(huì)插入上面的socket的一段代碼,這樣會(huì)使的打包后的bundle.js文件變得臃腫。
8. open
該屬性用于DevServer啟動(dòng)且第一次構(gòu)建完成時(shí),自動(dòng)使用我們的系統(tǒng)默認(rèn)瀏覽器去打開(kāi)網(wǎng)頁(yè)。
如下配置:
module.exports = {
devServer: {
// contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
// hot: true,
inline: true,
open: true
}
}
設(shè)置 open: true 即可,當(dāng)我們運(yùn)行完成 npm run dev 打包的時(shí)候,會(huì)自動(dòng)打開(kāi)默認(rèn)的瀏覽器來(lái)查看網(wǎng)頁(yè)。
9. overlay
該屬性是用來(lái)在編譯出錯(cuò)的時(shí)候,在瀏覽器頁(yè)面上顯示錯(cuò)誤。該屬性值默認(rèn)為false,需要的話,設(shè)置該參數(shù)為true。
為了演示下,我們來(lái)在main.js 代碼內(nèi)使用ES6的語(yǔ)法來(lái)編寫(xiě)代碼,ES6是使用babel-loader 這樣的來(lái)轉(zhuǎn)化的,但是目前我們的項(xiàng)目先不安裝該loader,應(yīng)該會(huì)報(bào)錯(cuò)的。比如在main.js 代碼加如下一句代碼:
const a;
配置 overlay: true即可:如下配置:
module.exports = {
devServer: {
// contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
// hot: true,
inline: true,
open: true,
overlay: true
}
}
運(yùn)行 npm run dev 后,自動(dòng)打開(kāi)網(wǎng)頁(yè),顯示如下所示:

10. stats(字符串)
該屬性配置是用來(lái)在編譯的時(shí)候再命令行中輸出的內(nèi)容,我們沒(méi)有設(shè)置 stats的時(shí)候,輸出是如下的樣子:如下所示:

該屬性值可以有如下值:
stats: 'errors-only' 表示只打印錯(cuò)誤,我們添加下這個(gè)配置到devServer中;如下代碼配置:
module.exports = {
devServer: {
// contentBase: path.join(__dirname, "dist"),
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
// hot: true,
inline: true,
open: true,
overlay: true,
stats: 'errors-only'
}
}
現(xiàn)在我們繼續(xù) 運(yùn)行 npm run dev 后,會(huì)看到命令行中顯示如下:

該配置的含義是 只有錯(cuò)誤的才會(huì)被打印,沒(méi)有錯(cuò)誤就不打印,因此多余的信息就不會(huì)顯示出來(lái)了。
該屬性值還有 'minimal', 'normal', 'verbose' 等。
11. compress
該屬性是一個(gè)布爾型的值,默認(rèn)為false,當(dāng)他為true的時(shí)候,它會(huì)對(duì)所有服務(wù)器資源采用gzip進(jìn)行壓縮。
12. proxy 實(shí)現(xiàn)跨域
有時(shí)候我們使用webpack在本地啟動(dòng)服務(wù)器的時(shí)候,由于我們使用的訪問(wèn)的域名是http://localhost:8081 這樣的,但是我們服務(wù)端的接口是其他的,
那么就存在域名或端口號(hào)跨域的情況下,但是很幸運(yùn)的是 devServer有一個(gè)叫proxy配置項(xiàng),可以通過(guò)該配置來(lái)解決跨域的問(wèn)題,那是因?yàn)?dev-server 使用了 http-proxy-middleware 包
假如現(xiàn)在我們本地訪問(wèn)的域名是 http://localhost:8081, 但是我現(xiàn)在調(diào)用的是百度頁(yè)面中的一個(gè)接口,該接口地址是:http://news.baidu.com/widget?ajax=json&id=ad。現(xiàn)在我們只需要在devServer中的proxy的配置就可以了:
如下配置:
proxy: {
'/api': {
target: 'http://news.baidu.com', // 目標(biāo)接口的域名
// secure: true, // https 的時(shí)候 使用該參數(shù)
changeOrigin: true, // 是否跨域
pathRewrite: {
'^/api' : '' // 重寫(xiě)路徑
}
}
}
因此所有的配置如下:
module.exports = {
devServer: {
// contentBase: path.join(__dirname, "dist"),
headers: {
'X-foo': '112233'
},
// hot: true,
port: '8081',
inline: true,
open: true,
overlay: true,
stats: 'errors-only',
proxy: {
'/api': {
target: 'http://news.baidu.com', // 目標(biāo)接口的域名
// secure: true, // https 的時(shí)候 使用該參數(shù)
changeOrigin: true, // 是否跨域
pathRewrite: {
'^/api' : '' // 重寫(xiě)路徑
}
}
}
}
}
然后我們?cè)趍ain.js里面編寫(xiě)如下代碼:
import axios from 'axios';
axios.get('/api/widget?ajax=json&id=ad').then(res => {
console.log(res);
});
在這里請(qǐng)求我使用 axios 插件,其實(shí)和jquery是一個(gè)意思的。為了方便就用了這個(gè)。
下面我們來(lái)理解下上面配置的含義:
1. 首先是百度的接口地址是這樣的:http://news.baidu.com/widget?ajax=json&id=ad;
2. proxy 的配置項(xiàng) '/api' 和 target: http://news.baidu.com 的含義是,匹配請(qǐng)求中 /api 含有這樣的域名 重定向 到http://news.baidu.com來(lái)。因此我在接口地址上 添加了前綴 '/api', 如: axios.get('/api/widget?ajax=json&id=ad'); 因此會(huì)自動(dòng)補(bǔ)充前綴,也就是說(shuō),url: /api/widget?ajax=json&id=ad 等價(jià)
于 url: http://news.baidu.com/api/widget?ajax=json&id=ad.
3. changeOrigin: true/false 還參數(shù)值是一個(gè)布爾值,含義是 是否需要跨域。
4. secure: true, 如果是https請(qǐng)求就需要改參數(shù)配置,需要ssl證書(shū)吧。
5. pathRewrite: {'^/api' : ''}的含義是重寫(xiě)url地址,把url的地址里面含有 '/api' 這樣的 替換成 '',
因此接口地址就變成了 http://news.baidu.com/widget?ajax=json&id=ad; 因此就可以請(qǐng)求得到了,最后就返回
接口數(shù)據(jù)了。
如下圖所示:
