我們日常使用的前端開發環境應該是怎樣的?我們可以嘗試著把基本前端開發環境的需求列一下:
* 構建我們發布需要的 HTML、CSS、JS 文件
* 使用 CSS 預處理器來編寫樣式
* 處理和壓縮圖片
* 使用 Babel 來支持 ES 新特性
* 本地提供靜態服務以方便開發調試
上述幾項應該可以滿足比較簡單的前端項目開發環境需求了,下面會一一介紹如何配置 webpack 來滿足這些需求。
## 關聯 HTML
webpack 默認從作為入口的 .js 文件進行構建(更多是基于 SPA 去考慮),但通常一個前端項目都是從一個頁面(即 HTML)出發的,最簡單的方法是,創建一個 HTML 文件,使用 `script` 標簽直接引用構建好的 JS 文件,如:
```
<script src="./dist/bundle.js"></script>
```
但是,如果我們的文件名或者路徑會變化,例如使用 `[hash]` 來進行命名,那么最好是將 HTML 引用路徑和我們的構建結果關聯起來,這個時候我們可以使用 [html-webpack-plugin](https://doc.webpack-china.org/plugins/html-webpack-plugin/)。
html-webpack-plugin 是一個獨立的 node package,所以在使用之前我們需要先安裝它,把它安裝到項目的開發依賴中:
```
npm install html-webpack-plugin -D
# 或者
yarn add html-webpack-plugin -D
```
然后在 webpack 配置中,將 html-webpack-plugin 添加到 plugins 列表中:
```
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin(),
],
}
```
這樣配置好之后,構建時 html-webpack-plugin 會為我們創建一個 HTML 文件,其中會引用構建出來的 JS 文件。實際項目中,默認創建的 HTML 文件并沒有什么用,我們需要自己來寫 HTML 文件,可以通過 html-webpack-plugin 的配置,傳遞一個寫好的 HTML 模板:
```
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html', // 配置輸出文件名和路徑
template: 'assets/index.html', // 配置文件模板
}),
],
}
```
這樣,通過 html-webpack-plugin 就可以將我們的頁面和構建 JS 關聯起來,回歸日常,從頁面開始開發。如果需要添加多個頁面關聯,那么實例化多個 html-webpack-plugin, 并將它們都放到 `plugins` 字段數組中就可以了。
更多配置這里就不展開講解了,參考文檔 [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) 以及官方提供的例子 [html-webpack-plugin/examples](https://github.com/jantimon/html-webpack-plugin/tree/master/examples)。
## 構建 CSS
我們編寫 CSS,并且希望使用 webpack 來進行構建,為此,需要在配置中引入 loader 來解析和處理 CSS 文件:
```
module.exports = {
module: {
rules: [
// ...
{
test: /\.css/,
include: [
path.resolve(__dirname, 'src'),
],
use: [
'style-loader',
'css-loader',
],
},
],
}
}
```
> style-loader 和 css-loader 都是單獨的 node package,需要安裝。
我們創建一個 index.css 文件,并在 index.js 中引用它,然后進行構建。
```
import "./index.css"
```
可以發現,構建出來的文件并沒有 CSS,先來看一下新增兩個 loader 的作用:
* css-loader 負責解析 CSS 代碼,主要是為了處理 CSS 中的依賴,例如 `@import` 和 `url()` 等引用外部文件的聲明;
* style-loader 會將 css-loader 解析的結果轉變成 JS 代碼,運行時動態插入 `style` 標簽來讓 CSS 代碼生效。
經由上述兩個 loader 的處理后,CSS 代碼會轉變為 JS,和 index.js 一起打包了。如果需要單獨把 CSS 文件分離出來,我們需要使用 [extract-text-webpack-plugin](https://doc.webpack-china.org/plugins/extract-text-webpack-plugin) 插件。
extract-text-webpack-plugin 這個插件在筆者寫作時并未發布支持 webpack 4.x 的正式版本,所以安裝的時候需要指定使用它的 alpha 版本:`npm install extract-text-webpack-plugin@next -D` 或者 `yarn add extract-text-webpack-plugin@next -D`。如果你用的是 webpack 3.x 版本,直接用 extract-text-webpack-plugin 現有的版本即可。
看一個簡單的例子:
```
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
// 因為這個插件需要干涉模塊轉換的內容,所以需要使用它對應的 loader
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: 'css-loader',
}),
},
],
},
plugins: [
// 引入插件,配置文件名,這里同樣可以使用 [hash]
new ExtractTextPlugin('index.css'),
],
}
```
## CSS 預處理器
在上述使用 CSS 的基礎上,通常我們會使用 Less/Sass 等 CSS 預處理器,webpack 可以通過添加對應的 loader 來支持,以使用 Less 為例,我們可以在官方文檔中找到對應的 [loader](https://doc.webpack-china.org/loaders/less-loader)。
我們需要在上面的 webpack 配置中,添加一個配置來支持解析后綴為 .less 的文件:
```
module.exports = {
// ...
module: {
rules: [
{
test: /\.less$/,
// 因為這個插件需要干涉模塊轉換的內容,所以需要使用它對應的 loader
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'less-loader',
],
}),
},
],
},
// ...
}
```
## 處理圖片文件
在前端項目的樣式中總會使用到圖片,雖然我們已經提到 css-loader 會解析樣式中用 `url()` 引用的文件路徑,但是圖片對應的 jpg/png/gif 等文件格式,webpack 處理不了。是的,我們只要添加一個處理圖片的 loader 配置就可以了,現有的 file-loader 就是個不錯的選擇。
file-loader 可以用于處理很多類型的文件,它的主要作用是直接輸出文件,把構建后的文件路徑返回。配置很簡單,在 `rules`中添加一個字段,增加圖片類型文件的解析配置:
```
module.exports = {
// ...
module: {
rules: [
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {},
},
],
},
],
},
}
```
更多關于 file-loader 的配置可以參考官方文檔 [file-loader](https://webpack.js.org/loaders/file-loader/)。
## 使用 Babel
[Babel](http://babeljs.io/) 是一個讓我們能夠使用 ES 新特性的 JS 編譯工具,我們可以在 webpack 中配置 Babel,以便使用 ES6、ES7 標準來編寫 JS 代碼。
```
module.exports = {
// ...
module: {
rules: [
{
test: /\.jsx?/, // 支持 js 和 jsx
include: [
path.resolve(__dirname, 'src'), // src 目錄下的才需要經過 babel-loader 處理
],
loader: 'babel-loader',
},
],
},
}
```
Babel 的相關配置可以在目錄下使用 .babelrc 文件來處理,詳細參考 Babel 官方文檔 [.babelrc](http://babeljs.io/docs/usage/babelrc/)。
## 啟動靜態服務
至此,我們完成了處理多種文件類型的 webpack 配置。我們可以使用 [webpack-dev-server](https://github.com/webpack/webpack-dev-server) 在本地開啟一個簡單的靜態服務來進行開發。
在項目下安裝 webpack-dev-server,然后添加啟動命令到 package.json 中:
```
"scripts": {
"build": "webpack --mode production",
"start": "webpack-dev-server --mode development"
}
```
> 也可以全局安裝 webpack-dev-server,但通常建議以項目開發依賴的方式進行安裝,然后在 npm package 中添加啟動腳本。
嘗試著運行 `npm run start` 或者 `yarn start`,然后就可以訪問 http://localhost:8080/ 來查看你的頁面了。默認是訪問 index.html,如果是其他頁面要注意訪問的 URL 是否正確。
## 小結
我們現在已經可以使用 webpack 來完成日常中需要的基礎前端構建需求:構建 HTML、CSS、JS 文件、使用 CSS 預處理器來編寫樣式、處理和壓縮圖片、使用 Babel、方便開發調試的靜態服務,接下來的小節會在這個基礎上,深入 webpack 配置細節,結合實際工作中的一些需要,更進一步地了解 webpack 的使用。
## 例子
本小節提及的一些簡單的 Demo 可以在 [webpack-examples](https://github.com/teabyii/webpack-examples) 找到。