Webpack(二):DevServer配置項(xiàng)

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/homehttp://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ù)了。

如下圖所示:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容