前言
本質(zhì)上,webpack 是一個現(xiàn)代 JavaScript 應用程序的靜態(tài)模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 bundle。
在開始使用 webpack 前,需要了解 webpack 的四個核心概念:
- 入口(entry):入口起點(entry point)指示了 webpack 要解析的源碼模塊,webpack 會解析這些模塊并構建出其內(nèi)部 依賴圖,最后將完整的依賴輸出到 bundle 文件中。
?? 可以通過在 webpack 中配置 entry 屬性,來指定一個入口起點(或多個入口起點)。默認值為 ./src。
單入口配置:
// webpack.config.js
module.exports = {
entry: './path/to/my/entry/file.js'
};
多入口配置:
// webpack.config.js
module.exports = {
entry: {
pageOne: './src/pageOne/index.js', // 入口1
pageTwo: './src/pageTwo/index.js', // 入口2
pageThree: './src/pageThree/index.js' // 入口3
}
};
更多配置內(nèi)容,請參考:入口起點
-
輸出(output):output 屬性指定 webpack 輸出 bundles 路徑,以及命名規(guī)則。默認值為
./dist。
?? output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件,默認值為 ./dist
?? 可以通過配置 output 字段來配置輸出的 bundle 文件:
//webpack.config.js
const path = require('path'); // 使用 nodejs 的 path 庫
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
path: path.resolve(__dirname, 'dist'), // 工程根目錄的 dist 文件夾
filename: 'my-first-webpack.bundle.js' // 輸出 bundle 名字
}
};
更多配置選擇,請參考:輸出
- loader:webpack 本身只能處理 JavaScript 文件,對于那些非 JavaScript 的文件,則可以通過各種相應的 loader 來將其轉換成 webpack 能夠識別并進行處理的模塊。
?? 比如,在 webpack 打包前,loader 可以將文件從不同的語言(如 TypeScript)轉換為 JavaScript,或?qū)?nèi)聯(lián)圖像轉換為 data URL。loader 甚至允許你直接在 JavaScript 模塊中 import CSS文件!
?? 本質(zhì)上,loader 將所有類型的文件,轉換為應用程序的依賴圖(和最終的 bundle)可以直接引用的模塊。
在 webpack 中, 配置 loader 主要使用以下兩個選項:
-
test屬性:標識 loader 要處理的文件類型; -
use屬性:表示進行轉換時,應該使用哪個 loader;
//webpack.config.js
const path = require('path');
const config = {
output: {
filename: 'my-first-webpack.bundle.js'
},
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
}
};
module.exports = config;
上述配置中,指定了 webpack 編譯器在遇到 require() 或 import 導入 txt 文件時,先使用 raw-loader 進行轉換,然后再打包。
注:loader 支持鏈式傳遞。一組鏈式的 loader 將按照相反的順序執(zhí)行。loader 鏈中的第一個 loader 返回值給下一個 loader(簡而言之,loader 的執(zhí)行順序與配置相反,最后配置的 loader 最先執(zhí)行)。
更多 loader 的配置,請參考:loader
更多內(nèi)置 loader,請查看:loaders
更多第三發(fā) loader,請查看:awesome-webpack
- 插件(plugins):loader 是在打包構建過程中用于處理某些類型的模塊轉換,而插件的能力更大,其在整個構建過程中起作用,可以執(zhí)行范圍更廣的任務,比如打包優(yōu)化和壓縮 bundle 文件,甚至于重新定義環(huán)境中的變量。
?? 這樣說吧,loader 就是在構建前對某些特定類型的文件進行預處理,而插件是在構建過程中能做任何事情(應該可以這樣認為,loader 其實就是一個小型的插件,其只能對某些特定文件進行預處理。而插件的目的就在于解決 loader 無法處理的事情)。
?? 想要使用一個插件,你只需要 require() 它,然后把它添加到 plugins 數(shù)組中。多數(shù)插件可以通過選項(option)自定義配置。你也可以在一個配置文件中因為不同目的而多次使用同一個插件,這時需要通過使用 new 操作符來創(chuàng)建它的一個實例。
// webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通過 npm 安裝
const webpack = require('webpack'); // 用于訪問內(nèi)置插件
const config = {
module: {
rules: [
{ test: /\.txt$/, use: 'raw-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
module.exports = config;
webpack 提供許多開箱可用的插件!查閱我們的 插件列表 獲取更多信息。
更多第三方插件,請查看 awesome-webpack 列表。
更多插件配置信息,請參考:插件(plugins)
安裝
npm install webpack --save-dev
npm install webpack-cli --save-dev
注:也可以全局安裝 webpack:npm install --global webpack。但是不推薦。
快速入門
例子:要求在 src/index.js 中為 document.body 添加一個 div 子節(jié)點,該 div 子節(jié)點內(nèi)容為:"Hello,Webpack"(要求使用第三方模塊 lodash 實現(xiàn)字符串拼接)。最后使用 webpack 將 src/index.js 模塊內(nèi)容打包到 dist/index.js 中,dist/index.html 引用該生成的 bundle 實現(xiàn)頁面動態(tài)添加 div 子節(jié)點功能。
實踐:
- 首先創(chuàng)建目錄結構,如下所示:
webpackDemo
+ |- /dist
+ |- index.html
+ |- /src
+ |- index.js
- 創(chuàng)建一個
package.json文件:
npm init -y
- 本地安裝 webpack
npm install webpack webpack-cli --save-dev
- 根目錄下創(chuàng)建 webpack 默認配置文件:webpack.config.js,并配置其入口起點為:
src/index.js,打包生產(chǎn)的 bundle 文件為:dist/index.js:
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist')
}
};
- 安裝
lodash
npm install lodash --save-dev
- 編寫
src/index.js代碼:
import _ from 'lodash'
function createDiv(){
let div = document.createElement('div');
div.innerHTML = _.join(['Hello',',','Webpack']);
return div;
}
document.body.appendChild(createDiv());
- 進行打包,打包完成后即可看到
dist/index.js生成:
npx webpack
- 手動將生成的
dist/index.js引入到dist/index.html中,打開瀏覽器,即可看到效果:
<!DOCTYPE html>
<html>
<head>
<title>Webpcak Demo</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
一些有用配置
-
模式(mode):提供
mode配置選項,區(qū)分生成環(huán)境,告知 webpack 使用相應模式的內(nèi)置優(yōu)化。其選擇有兩個值可選:development,production
module.exports = {
mode: 'production'
};
- 基礎配置:生產(chǎn)環(huán)境 + 入口 + 出口
// webpack.config.js
var path = require('path');
module.exports = {
mode: 'development',
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js'
}
};
-
resolve.alias:創(chuàng)建
import或require的別名,來確保模塊引入變得更簡單。例如,一些位于src/文件夾下的常用模塊:
// webpack.config.js
module.exports = {
resolve: {
alias: {
Utilities: path.resolve(__dirname, 'src/utilities/'),
Templates: path.resolve(__dirname, 'src/templates/')
}
}
}
像未配置前需要使用全路徑或相對路徑引入:
// index.js
import Utility from '../../utilities/utility';
在配置后,就可以使用別名進行導入:
// index.js
import Utility from 'Utilities/utility'; // 注意使用的是別名:Utilities
-
watch:webpack 可以監(jiān)聽文件變化,當它們修改后會重新編譯。
watch默認為關閉。
watch: true
還可以通過 watchOptions 來進一步控制 watch 模式的選項:
watchOptions: {
aggregateTimeout: 300, // 文件更改后,300毫秒后進行重新構建
poll: 1000, // 輪詢時間,每 1s 進行一次檢測
ignored: /node_modules/ // 不監(jiān)聽 node_modules 目錄
}
-
外部擴展(externals):防止將某些
import的包(package)打包到 bundle 中,而是在運行時(runtime)再去從外部獲取這些擴展依賴(external dependencies)。
?? 例如,從 CDN 引入 jQuery,而不是把它打包:
<!-- index.html -->
<script
src="https://code.jquery.com/jquery-3.1.0.js"
integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
crossorigin="anonymous">
</script>
// webpack.config.js
externals: {
jquery: 'jQuery'
}
這樣就剝離了那些不需要改動的依賴模塊,換句話,下面展示的代碼還可以正常運行:
import $ from 'jquery';
$('.my-element').animate(...);
像上述配置了 jquery 為外部依賴后,webpack 編譯器在遇到:import $ from 'jquery'; 時,就知道了應該排除掉 jquery 模塊,不將其打包進 bundle 中。
-
構建目標(targets):使用
target屬性可以配置 webpack 生成服務器端或者是瀏覽器端的 JavaScript 代碼。
// webpack.config.js
module.exports = {
target: 'node' // 生成服務器端代碼
};
如果想同時生成多個 Target,可以如下配置:
// webpack.config.js
var path = require('path');
var serverConfig = {
target: 'node', // 生成服務器端代碼
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.node.js'
}
//…
};
var clientConfig = {
target: 'web', // <=== 默認是 'web',可省略
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'lib.js'
}
//…
};
module.exports = [ serverConfig, clientConfig ]; // 導出兩個 bundle
管理資源
- 加載 CSS 文件:需要使用 style-loader 和 css-loader。
安裝:
npm install style-loader css-loader --save-dev
配置:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ use: [
+ 'style-loader',
+ 'css-loader'
+ ]
+ }
+ ]
+ }
};
使用:
- 現(xiàn)在
src/目錄下新建一個 style.css 文件:
// style.css
.container {
color: red;
}
- 修改入口文件:
src/index.js:
import _ from 'lodash';
+ import './style.css'
function createDiv(){
let div = document.createElement('div');
div.innerHTML = _.join(['Hello','Webpack'],',');
+ div.classList.add('container')
return div;
}
document.body.appendChild(createDiv());
- 最后重新打包即可看到效果:
npx webpack
-
抽離 CSS 文件為單獨文件:前面使用 style-loader 最終會將 css 樣式注入到頁面的
<head>內(nèi)的style節(jié)點中,即為內(nèi)部樣式表。我們更希望使用的是外部樣式表,即通過<link>標簽進行引入,那么通過使用 mini-css-extract-plugin 插件即可實現(xiàn)。
安裝:
npm install mini-css-extract-plugin --save-dev
配置:
const path = require('path');
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const devMode = process.env.NODE_ENV !== 'production'; // 判斷當前環(huán)境是開發(fā)環(huán)境還是 部署環(huán)境,主要是 mode屬性的設置值。
module.exports = {
module: {
rules: [
+ {
+ test: /\.(sa|sc|c)ss$/,
+ use: [MiniCssExtractPlugin.loader, 'css-loader']
+ },
]
},
+ plugins: [
+ new MiniCssExtractPlugin({
+ filename: devMode ? '[name].css' : '[name].[hash].css', // 設置最終輸出的文件名
+ chunkFilename: devMode ? '[id].css' : '[id].[hash].css'
+ })
+ ]
};
使用:重新打包后,可在瀏覽器查看是否以外部鏈接引入樣式表。
注:由于抽取了樣式,因此不再使用 style-loader 注入到 html 中了。
- 加載 Sass 文件:需要使用 sass-loader
安裝:
npm install sass-loader node-sass webpack --save-dev
配置:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
+ use: [MiniCssExtractPlugin.loader, 'css-loader',sass-loader]
},
]
},
};
- 加載圖片:需要使用 file-loader。
安裝:
npm install file-loader --save-dev
配置:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
+ {
+ test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
+ use: [
+ 'file-loader'
+ ]
+ }
]
}
};
使用:只需向項目中增加一張圖片。比如,在 style.css 中,引入一張圖片:
.container {
width: 400px;
height: 400px;
color: red;
background: url('../static/img/horse.png');
}
最后,重新打包一下即可看到效果:npx webpack
- 圖片壓縮和優(yōu)化:需要使用 image-webpack-loader。
安裝:
npm install image-webpack-loader --save-dev
配置:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
'file-loader',
+ {
+ loader: 'image-webpack-loader',
+ options: {
+ mozjpeg: {
+ progressive: true,
+ quality: 65
+ },
+ optipng: {
+ enabled: false,
+ },
+ pngquant: {
+ quality: '65-90',
+ speed: 4
+ },
+ gifsicle: {
+ interlaced: false,
+ },
+ webp: {
+ quality: 75
+ }
+ }
+ },
]
}
]
}
};
使用:重新打包一下,可以看到生成一張已被壓縮的圖片。
- 將圖片轉換為 base64 編碼:需要使用 url-loader,同時 url-loader 可以替換 file-loader,其比 file-loader 多了將圖片編碼為 base64 的功能。
安裝:
npm install url-loader --save-dev
配置:
module.exports = {
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|jpeg|ico|woff|woff2|eot|ttf|otf)$/,
use: [
+ {
+ loader: 'url-loader', // 根據(jù)圖片大小,把圖片優(yōu)化成base64
+ options: {
+ limit: 10000 // 圖片小于 10000 字節(jié)時,進行 base64 編碼
+ }
+ },
{
loader: 'image-webpack-loader', // 先進行圖片優(yōu)化
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false
},
webp: {
quality: 75
}
}
}
]
}
]
}
};
使用:重新打包后,只要圖片小于 10000 字節(jié)的,就會被轉換成 base64 編碼的 DataURL。
注:file-loader 和 url-loader 可以接收并加載任何文件,然后將其輸出到構建目錄。這就是說,我們可以將它們用于任何類型的文件,包括字體。
管理輸出
前面的操作我們都是手動地對 index.html 文件進行管理,但隨著項目的增大,手動管理方式將繁瑣且容易出錯,因此急需一個自動注入資源的方法,這種操作可以通過插件進行完成。
-
自動注入資源:可以使用 html-webpack-plugin 將打包生成的 bundle 自動注入到
index.html中。
安裝:
npm install html-webpack-plugin --save-dev
配置:
// webpack.config.js
const path = require('path');
+ const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
+ app: './src/index.js' // 為入口起點增加命名
},
output: {
filename: '[name]-[hash].js',
path: path.resolve(__dirname, 'dist')
},
+ plugins: [
+ new HtmlWebpackPlugin({
+ title: 'Output Management', // 默認值:Webpack App
+ filename: 'index.html', // 默認值: 'index.html'
+ template: path.resolve(__dirname, 'src/index.html'), // 以 src/index.htm 為模板
+ minify: {
+ collapseWhitespace: true,
+ removeComments: true,
+ removeAttributeQuotes: true // 移除屬性的引號
+ }
+ })
+ ]
};
使用:安裝上述配置,打包完成后,html-webpack-plugin 插件就會自動把生成的 js 文件插入到 dist/index.html 文件中。
-
清理
/dist文件夾:通常,在每次構建前清理/dist文件夾,是比較推薦的做法,因此只會生成用到的文件。需要用到插件 clean-webpack-plugin。
安裝:
npm install clean-webpack-plugin --save-dev
配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
app: './src/index.js'
},
output: {
filename: '[name]-[hash].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
+ new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
...
})
]
};
使用:上述配置完成后,以后每次打包,/dist 目錄都會先進行清理,打包后就只生成需要使用到的文件了。
- JS 啟用 babel 轉碼:雖然現(xiàn)代的瀏覽器已經(jīng)兼容了 96% 以上的 ES6 的語法了,但是為了兼容老式的瀏覽器(IE8、9)我們需要把最新的 ES6 的語法轉成 ES5 的。使能該功能需要使用 babel。
安裝:
npm install -D babel-loader @babel/core @babel/preset-env webpack
配置:
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
使用:經(jīng)過上述配置后,就可以在入口文件中使用 ES6 語法,打包后,該 ES6 語法內(nèi)容就會被轉換成 ES5 語法內(nèi)容。
- ESLint校驗代碼格式規(guī)范:需要 eslint 支持。
安裝:
npm install eslint --save-dev
npm install eslint-loader --save-dev
# 以下是用到的額外的需要安裝的eslint的解釋器、校驗規(guī)則等
npm i -D babel-eslint standard
配置:
- webpack 配置文件進行配置:
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
+ {
+ loader: 'eslint-loader',
+ options: {
+ // eslint options (if necessary)
+ fix: true
+ }
+ }
]
},
};
- 項目根目錄下創(chuàng)建一個文件:
.eslintrc.js。往里面寫入以下配置:
// .eslintrc.js
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true
},
extends: [
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
globals: {
NODE_ENV: false
},
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
// 添加,分號必須
semi: ['error', 'always'],
'no-unexpected-multiline': 'off',
'space-before-function-paren': ['error', 'never'],
// 'quotes': ["error", "double", { "avoidEscape": true }]
quotes: [
'error',
'single',
{
avoidEscape: true
}
]
}
};
- 項目根目錄下創(chuàng)建一個文件:
.eslintignore。該文件配置要忽略 eslint 檢測的目錄:
/dist/
/node-modules/
# 忽略根目錄的 js 文件(即配置文件)
/*.js
開發(fā)環(huán)境搭建
在開發(fā)過程中,我們編寫的源代碼一般有多個,最后交由 webpack 打包成單獨一個 bundle。因此,如果出現(xiàn)錯誤,錯誤顯示在 bundle 中,就無法直接定位到我們自己的錯誤代碼中。所以,在開發(fā)環(huán)境(development)中,需要對 webpack 進行一些配置,方便我們進行開發(fā)。
-
使用 source map:為了更容易地追蹤錯誤和警告,JavaScript 提供了 source map 功能,將編譯后的代碼映射回原始源代碼。如果一個錯誤來自于
b.js,source map 就會明確的告訴你。
配置:開啟 source map
module.exports = {
devtool: 'inline-source-map',
};
-
使用觀察模式:每次我們修改源碼后,都需要使用
npx webpack進行手動打包,不免繁瑣了點??梢酝ㄟ^配置 webpack "watch" 自動監(jiān)視文件更改,自動進行編譯打包。
配置:啟動 "watch" 的方法為控制臺輸入:npx webpack --watch,也可以將該命令配置到package.json的scripts中,啟動一個腳本命令:
// package.json
{
···
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
+ "watch": "npx webpack --watch"
},
···
}
使用:控制臺輸入:npm run watch
- 使用 webpack-dev-server:webpack "watch" 可以自動進行打包,但是瀏覽器端我們還是需要手動進行刷新才能看到修改效果。如果我們也想讓瀏覽器實時監(jiān)測文件修改,自動刷新,則可以使用 webpack-dev-server。
安裝:
npm install webpack-dev-server --save-dev
配置:
module.exports = {
devServer: {
contentBase: './dist' // 監(jiān)聽目錄變化,實時重載
},
};
使用:通過在 package.json 中配置一個腳本命令進行啟動:npm run hotload
// package.json
{
···
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"watch": "npx webpack --watch",
+ "hotload": "webpack-dev-server --open"
},
···
}
生產(chǎn)環(huán)境搭建
在生產(chǎn)環(huán)境(production)中,我們的目標是傾向于生成更小的 bundle,更輕量的 source map,以及更優(yōu)化的資源,以改善加載時間。
因此,可通過以下配置來輸出更好匹配生產(chǎn)環(huán)境的 bundle。
- 壓縮 CSS:需要使用插件:optimize-css-assets-webpack-plugin
安裝:
npm i -D optimize-css-assets-webpack-plugin
配置:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+ const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
+ mode: 'production',
+ optimization: {
+ minimizer: [new OptimizeCSSAssetsPlugin({})]
+ },
...
};
-
壓縮 JavaScript:需要的插件:uglifyjs-webpack-plugin,此插件只在
mode: ’production’下起作用。
安裝:
npm i -D uglifyjs-webpack-plugin
配置:
+ const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
mode: 'production',
optimization: {
minimizer: [
+ new UglifyJsPlugin({
+ cache: true,
+ parallel: true,
+ sourceMap: true // set to true if you want JS source maps
+ }),
new OptimizeCSSAssetsPlugin({})
]
}
};
區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境
開發(fā)環(huán)境(development)和生產(chǎn)環(huán)境(production)的構建目標差異很大。兩者的構建目標既存在相同部分,也存在不同之處。因此,通常會為每個環(huán)境編寫 彼此獨立的 webpack 配置,對于兩者皆有的配置,則抽取到一個公共的配置文件中即可。這種分文件配置方法可通過插件 webpack-merge 完成。
安裝:
npm install webpack-merge --save-dev
配置:具體配置詳情如下:
- 新建一個文件:webpack.common.js。作為開發(fā)環(huán)境和生產(chǎn)環(huán)境公共配置文件:
// webpack.common.js
const path = require('path');
// 清理 /dist
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
},
{
loader: 'eslint-loader',
options: {
fix: true
}
}
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000
}
}
]
},
{
test: /\.(png|svg|jpg|gif|jpeg|ico)$/,
use: [
{
loader: 'url-loader', // 根據(jù)圖片大小,把圖片優(yōu)化成base64
options: {
limit: 10000
}
},
{
loader: 'image-webpack-loader', // 先進行圖片優(yōu)化
options: {
mozjpeg: {
progressive: true,
quality: 65
},
optipng: {
enabled: false
},
pngquant: {
quality: '65-90',
speed: 4
},
gifsicle: {
interlaced: false
},
webp: {
quality: 75
}
}
}
]
}
]
},
plugins: [new CleanWebpackPlugin()]
};
- 新建一個文件:webpack.dev.js。作為開發(fā)環(huán)境的配置文件:
const path = require('path');
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
// 注入資源到 html
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 抽離 CSS 文件為單獨文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// merge 第二參數(shù)的配置會覆蓋第一個參數(shù)相同的配置
module.exports = merge(common, {
mode: 'development',
// 使能 source map
devtool: 'inline-source-map',
// 使能 webpack-dev-server,瀏覽器自動重載
devServer: {
contentBase: './dist' // 監(jiān)聽目錄內(nèi)容改變
},
watchOptions: {
// 監(jiān)視文件相關的控制選項
poll: true, // webpack 使用文件系統(tǒng)(file system)獲取文件改動的通知。在某些情況下,不會正常工作。例如,當使用 Network File System (NFS) 時。Vagrant 也有很多問題。在這些情況下,請使用輪詢. poll: true。當然 poll也可以設置成毫秒數(shù),比如: poll: 1000
ignored: /node_modules/, // 忽略監(jiān)控的文件夾,正則
aggregateTimeout: 300 // 默認值,當?shù)谝粋€文件更改,會在重新構建前增加延遲
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'Development',
filename: 'index.html', // 默認值: 'index.html',
template: path.resolve(__dirname, 'src/index.html'),
minify: {
collapseWhitespace: false,
removeComments: false,
removeAttributeQuotes: true // 移除屬性的引號
}
}),
new MiniCssExtractPlugin({
filename: '[name].css', // 設置最終輸出的文件名
chunkFilename: '[id].css'
})
]
});
- 新建一個文件:webpack.prod.js。作為生產(chǎn)環(huán)境的配置文件:
const path = require('path);
const merge = require('webpack-merge');
// 壓縮 js 文件
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
// 注入資源到 html
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 壓縮 CSS
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
// 壓縮 JS
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// 抽離 CSS 文件為單獨文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// merge 第二參數(shù)的配置會覆蓋第一個參數(shù)相同的配置
module.exports = merge(common, {
mode: 'production',
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true // set to true if you want JS source maps
}),
new OptimizeCSSAssetsPlugin({})
]
},
module: {
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'sass-loader',
options: {
sourceMap: true
}
}
]
}
]
},
plugins: [
new UglifyJSPlugin(),
new HtmlWebpackPlugin({
title: 'Production', // 默認值:Webpack App
filename: 'index.html', // 默認值: 'index.html'
template: path.resolve(__dirname, 'src/index.html'),
minify: {
collapseWhitespace: true,
removeComments: true,
removeAttributeQuotes: true // 移除屬性的引號
}
}),
new MiniCssExtractPlugin({
filename: '[name].[hash].css', // 設置最終輸出的文件名
chunkFilename: '[id].[hash].css'
})
]
});
- 最后,在
package.json中配置如下腳本,可分別啟動開發(fā)環(huán)境和生產(chǎn)環(huán)境:
// package.json
{
···
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"watch": "npx webpack --watch",
"hotload": "webpack-dev-server --open",
+ "start": "webpack-dev-server --open --config webpack.dev.js",
+ "build": "webpack --config webpack.prod.js"
},
···
}
通過 npm run start 啟動開發(fā)環(huán)境,
通過 npm run build 啟動生產(chǎn)環(huán)境。