什么是 webpack
?? webpack 是德國開發(fā)者 Tobias Koppers 開發(fā)的模塊加載器。
??在 webpack 中所有的文件都將被當做模塊使用。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關系圖(dependency graph),其中包含應用程序需要的每個模塊,然后將所有的這些模塊打包成一個或多個 bundle。
與 Gulp/Grunt 對比
??webpack 與 Gulp/Grunt 是沒有對比性的,因為 Gulp/Grunt 是一種能夠優(yōu)化前端的開發(fā)流程的工具,而 webpack 是一種模塊化的解決方案。不過 Webpack 的優(yōu)點使得 Webpack 在很多場景下可以替代 Gulp/Grunt 類的工具。
??Grunt 和 Gulp 的工作方式是:在一個配置文件中,指明對某些文件進行類似編譯,組合,壓縮等任務的具體步驟,工具之后可以自動替你完成這些任務。
??webpack 的工作方式是:把你的項目當做一個整體,通過一個給定的主文件(如:index.js),Webpack 將從這個文件開始找到你的項目的所有依賴文件,使用 loaders 處理它們,最后打包為一個(或多個)瀏覽器可識別的 JavaScript 文件。
webpack 的安裝及使用
- 通過 npm 全局安裝 webapck
$ npm install -g webpack
- 創(chuàng)建項目并初始化 package.json 文件
$ mkdir demo1 && cd demo1
$ npm init
- 在項目中安裝 webpack
$ npm install webpack --save-dev
--save-dev 是開發(fā)時候依賴的東西,--save 是發(fā)布之后還依賴的東西
-
在項目中創(chuàng)建如下文件結構
<pre>
.
├── index.html // 顯示的網頁
├── main.js // webpack 入口
└── bundle.js // 通過 webpack 命令生成的文件,無需創(chuàng)建
</pre> 通過命令對項目中依賴的 js 文件進行打包
# webpack 要打包的 js 文件名 打包后生成的js文件名
$ webpack main.js bundle.js
??在 webpack 命令后面還可以加入以下參數(shù)
-
--watch實時打包 -
--progress顯示打包進度 -
--display-modules顯示打包的模塊 -
--display-reasons顯示模塊包含在輸出中的原因
??更多參數(shù)可以通過命令 webpack --help 查看
webpack 中的四個核心概念
-
Entry入口 -
Output輸出 Loaders-
Plugins插件
??webpack 中默認的配置文件名稱是 webpack.config.js,因此我們需要在項目中創(chuàng)建如下文件結構:
<pre>
.
├── index.html // 顯示的頁面
├── main.js // webpack 入口
├── webpack.config.js // webpack 中默認的配置文件
└── bundle.js // 通過 webpack 命令生成的文件,無需創(chuàng)建
</pre>
entry 入口
??入口起點(entry point)指示 webpack 應該使用哪個模塊,來作為構建其內部依賴圖的開始。進入入口起點后。 webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。
??可以在 webpack.config.js 中 配置 entry 屬性,來指定一個入口或多個起點入口,代碼如下:
moudle.exports = {
entry: './path/file.js',
};
output 輸出
?? output 屬性告訴 webpack 在哪里輸出它所創(chuàng)建的 bundles,以及如何命名這些文件。你可以通過在配置指定一個 output 字段,來配置這些過程:
const path = require('path');
moudle.exports = {
entry: './path/file.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'my-webpack.bundle.js',
},
};
??其中 output.path 屬性用于指定生成文件的路徑,output.filename 用于指定生成文件的名稱。
Loaders
?? Loaders 讓 webpack 能夠去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以將所有類型的文件轉換為 webpack 能夠處理的有效模塊,然后可以利用 webpack 的打包能力,對它們進行處理。
??本質上,webpack loader 將所有類型的文件,轉換為應用程序的依賴圖可以直接引用模塊。在更高層面上,在 webpack 的配置中 loader 有兩個目標:
- 識別應該被對應的
loader進行轉換的那些文件(使用test屬性) - 轉換這些文件,從而使其能夠被添加到依賴圖中(并且最終添加到
bundle中)(use屬性)
??在開始下面的代碼之前,我們需要安裝 style-loader 和 css-loader
$ npm install --save-dev style-loader css-loader
并在項目中創(chuàng)建 style.css 樣式文件:
h1 {
color: red;
}
??然后在 webpack.config.js 中輸入以下代碼:
const path = require('path');
module.export = {
entry: './main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
},
],
},
};
Plugins 插件
?? Loaders 被用于轉換某些類型的模塊,而插件則可以用于執(zhí)行范圍更廣的任務。插件的范圍包括,從打包優(yōu)化和壓縮,一直到重新定義環(huán)境中的變量。
??想要使用一個插件,需要 require() 它,然后把它添加到 Plugins 數(shù)組中,多數(shù)插件可以通過選項自定義。也可以在一個配置文件中因為不同目的而多次使用同一個插件,這時需要通過使用 new 操作符來創(chuàng)建它的實例。
??在開始下面的代碼之前,我們需要安裝 html-webpack-plugin 插件:
$ npm install html-webpack-plugin --save-dev
它可以簡化 HTML 文件的創(chuàng)建,為您的 webpack 包提供服務。
??然后在 webpack.config.js 中輸入以下代碼:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const config = {
entry: './main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/,
use: [{ loader: 'style-loader' }, { loader: 'css-loader' }],
},
],
},
plugins: [new HtmlWebpackPlugin({ template: './index.html' })],
};
module.exports = config;
運行與配置
?? 最后我們可以直接通過 webpack 命令編譯打包,如果想要在其命令后加入?yún)?shù),可以通過配置 package.json 文件中的 scripts 屬性:
{
"scripts": {
"build": "webpack --config webpack.config.js --progress --display-modules"
}
}
當然如果你想要更改默認的配置文件名稱,可以將 --config 后面的 webpack.config.js 配置文件名改為你自定義的名稱。
??通過以下命令執(zhí)行:
$ npm run build
多入口設置與 html-webpack-pugin 插件詳解
??我們可以為 entry 指定多個入口。在開始代碼之前,我們需要創(chuàng)建如下目錄解構
<pre>
.
├── index.html // 顯示的頁面
├── main1.js // webpack 入口1
├── main1.js // webpack 入口2
├── style.css // 樣式文件
└── webpack.config.js // webpack 中默認的配置文件
</pre>
??我們在 index.html 文件中輸入以下內容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>demo3</title>
</head>
<body></body>
</html>
??我們在 main1.js 文件中輸入以下內容:
improt './style.css'
var h1 = document.createElement('h1');
h1.innertHTML = '這是 main1.js 中的內容';
document.body.appendChild(h1);
??我們在 main2.js 文件中輸入以下內容:
improt './style.css'
var h2 = document.createElement('h2');
h2.innertHTML = '這是 main2.js 中的內容';
document.body.appendChild(h2);
??我們在 style.css 文件中輸入以下內容:
h1 {
color: red;
}
h2 {
color: blue;
}
??我們在 webpack.config.js 文件中輸入以下內容:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const config = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
module: {
rules: [{ test: /\.css$/, loader: 'style-loader!css-loader' }],
},
pugins: [new HtmlWebpackPlugin({ template: './index.html' })],
};
module.exports = config;
??完成上面的代碼工作后,運行 webapck 命令,我們打開 dist 文件中的 index.html。
運行的結果并不是我們預期的那樣展示 h1 的內容在前,h2 內容在后,打開生成后的 index.html 源碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>demo3</title>
</head>
<body>
<script type="text/javascript" src="bundle2.js"></script>
<script type="text/javascript" src="bundle1.js"></script>
</body>
</html>
從源碼中便可得知,先引入的 bundle2.js 文件,也就是 main2.js 的內容,后引入的 bundle1.js 文件,也就是 main1.js 的內容。
??我們并沒有在 index.html 中輸入任何引入 JavaScript 文件的代碼,那么使用 webpack 打包后生成的文件,是怎么引入 JavaScript 文件的呢。事實上就是通過 html-webpack-plugin 為我們生成的 index.html 。
html-webpack-plugin 中的參數(shù)詳解
??通過 npm 中的介紹,html-webpack-plugin 是一個 webpack 插件,可以簡化 HTML 文件的創(chuàng)建,為我們的 webpack 包提供服務,它包含了一個改變每個編譯的文件名參數(shù)。使用 lodash 模板提供我們自己的模板或者使用自己的 loader。
??我們可以配置以下參數(shù)傳遞給 HtmlWebpackPlugin:
-
title: 用于生成的HTML文檔的標題。 -
filename: 要寫入HTML的文件。默認為index.html。你也可以在這里指定一個子目錄(例如:assets / admin.html)。 -
template: 引入的模板文件,具體內容可以查看文檔。 -
inject:true | 'head' | 'body' | false,指定引入JavaScript腳本文件,在生成的HTML中的位置。默認為 true,指JavaScript腳本文件在<body>元素中引入;head,指JavaScript腳本文件在<head>元素中引入,body與true值相同;false指只生成HTML文件,不引入任何JavaScript腳本文件。 -
favicon: 生成的HTML文件中的圖標路徑。 -
minify:{...} | false是否對生成的HTML文件壓縮,默認為false,具體配置可查看 html-minifier -
hash:true | false,如果為true,給生成的 js 文件一個獨特的 hash 值,該 hash 值是該次 webpack 編譯的 hash 值,這對緩存清除非常有用。默認值為false。 -
cache:true | false, 如果為true則只編譯生成更改的內容將文件,默認值為true。 -
showErrors:true | false,如果為true,則將錯誤內容添加到HTML中,默認值為true。 -
chunks: 指定引入的JavaScript腳本文件(例如:[ 'bundle1', 'bundle2' ])。 -
chunksSortMode:'none' | 'auto' | 'dependency' |'manual' | {function} - default: 'auto',對引入的chunks進行排序,具體可以查看該文檔。 -
excludeChunks: 排除掉指定的JavaScript腳本文件(例如:[ 'bundle1', 'bundle2' ])。 -
xhtml:true | false,默認值是false,如果為true,則以兼容xhtml的模式引用文件。
??現(xiàn)在我們知道了 html-webpack-plugin 中的參數(shù),下面我們就來修改 webpack.config.js 中的內容:
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const config = {
entry: {
bundle1: path.resolve(__dirname, 'main1.js'),
bundle2: path.resolve(__dirname, 'main2.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
module: {
rules: [{ test: /\.css$/, loader: 'style-loader!css-loader' }],
},
plugins: [
new HtmlWebpackPlugin({
title: '多文件引入', // 生成 html 的標題
filename: 'index.html', // 生成 html 文件的名稱
template: path.resolve(__dirname, 'index.html'), // 根據(jù)自己的指定的模板文件來生成特定的 html 文件
// inject: true, // 注入選項 有四個值 ture: 默認值,script標簽位于html文件的 body 底部, body: 同 true, head: script標簽位于html文件的 head 底部,false:不注入script標簽
favicon: path.resolve(__dirname, 'favicon.ico'), // 生成的 html 文件設置 favicon
minify: {
caseSensitive: false, //是否大小寫敏感
collapseBooleanAttributes: true, //是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫為disabled
collapseWhitespace: true, //是否去除空格
},
hash: true, // hash選項的作用是 給生成的 js 文件一個獨特的 hash 值,該 hash 值是該次 webpack 編譯的 hash 值。默認值為 false
cache: true, // 默認值是 true。表示只有在內容變化時才生成一個新的文件
showErrors: true, // showErrors 的作用是,如果 webpack 編譯出現(xiàn)錯誤,webpack會將錯誤信息包裹在一個 pre 標簽內,屬性的默認值為 true
chunks: ['bundle1', 'bundle2'], // 指定引入的 js 文件
//excludeChunks:[ 'bundle1' ], // 排除掉某些 js 文件
/**
* script 標簽的引用順序
* 'dependency' 按照不同文件的依賴關系來排序
* 'auto' 默認值,插件的內置的排序方式
* 'none'
* 'manual'
* funciton 自定義排序,與JS中自定義數(shù)組的sort回調一個含義, 具體可以看 https://github.com/jantimon/html-webpack-plugin/issues/481
*/
chunksSortMode: function(chunk1, chunk2) {
var orders = ['bundle1', 'bundle2'];
var order1 = orders.indexOf(chunk1.names[0]);
var order2 = orders.indexOf(chunk2.names[0]);
return order1 - order2;
},
xhtml: false, // 一個布爾值,默認值是 false ,如果為 true ,則以兼容 xhtml 的模式引用文件
}),
],
};
module.exports = config;
??完成上面的代碼工作后,運行 webapck 命令,我們打開 dist 文件中的 index.html。
??Nice!與我們的預期效果顯示一致。在對 html-webpack-plugin 的介紹中,提到了 lodash 模板, 那么該怎么用呢?我們再次修改 webpack.config.js 中的內容,為 HtmlWebpackPlugin 傳入 Date 參數(shù):
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const config = {
entry: {
bundle1: path.resolve(__dirname, 'main1.js'),
bundle2: path.resolve(__dirname, 'main2.js'),
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
},
module: {
rules: [{ test: /\.css$/, loader: 'style-loader!css-loader' }],
},
plugins: [
new HtmlWebpackPlugin({
date: new Date(),
title: '多文件引入', // 生成 html 的標題
filename: 'index.html', // 生成 html 文件的名稱
template: path.resolve(__dirname, 'index.html'), // 根據(jù)自己的指定的模板文件來生成特定的 html 文件
// inject: true, // 注入選項 有四個值 ture: 默認值,script標簽位于html文件的 body 底部, body: 同 true, head: script標簽位于html文件的 head 底部,false:不注入script標簽
favicon: path.resolve(__dirname, 'favicon.ico'), // 生成的 html 文件設置 favicon
minify: {
caseSensitive: false, //是否大小寫敏感
collapseBooleanAttributes: true, //是否簡寫boolean格式的屬性如:disabled="disabled" 簡寫為disabled
collapseWhitespace: true, //是否去除空格
},
hash: true, // hash選項的作用是 給生成的 js 文件一個獨特的 hash 值,該 hash 值是該次 webpack 編譯的 hash 值。默認值為 false
cache: true, // 默認值是 true。表示只有在內容變化時才生成一個新的文件
showErrors: true, // showErrors 的作用是,如果 webpack 編譯出現(xiàn)錯誤,webpack會將錯誤信息包裹在一個 pre 標簽內,屬性的默認值為 true
chunks: ['bundle1', 'bundle2'], // 指定引入的 js 文件
//excludeChunks:[ 'bundle1' ], // 排除掉某些 js 文件
/**
* script 標簽的引用順序
* 'dependency' 按照不同文件的依賴關系來排序
* 'auto' 默認值,插件的內置的排序方式
* 'none'
* 'manual'
* funciton 自定義排序,與JS中自定義數(shù)組的sort回調一個含義, 具體可以看 https://github.com/jantimon/html-webpack-plugin/issues/481
*/
chunksSortMode: function(chunk1, chunk2) {
var orders = ['bundle1', 'bundle2'];
var order1 = orders.indexOf(chunk1.names[0]);
var order2 = orders.indexOf(chunk2.names[0]);
return order1 - order2;
},
xhtml: false, // 一個布爾值,默認值是 false ,如果為 true ,則以兼容 xhtml 的模式引用文件
}),
],
};
module.exports = config;
更改 index.html 中的內容,lodash 模板默認支持的是 ejs 模板的語法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>demo3</title>
</head>
<body>
<%= htmlWebpackPlugin.options.date %>
</body>
</html>
??完成上面的代碼工作后,運行 webapck 命令,我們打開 dist 文件中的 index.html。
??通過運行結果,我們可以發(fā)現(xiàn)在頂部輸出了當前時間,也就是 HtmlWebpackPlugin 傳入的參數(shù),實際上 HtmlWebpackPlugin 中的參數(shù)都可以通過 htmlWebpackPlugin.options.參數(shù)名稱 輸出,我就不一一列舉。
Babel
??Babel 是一個工具鏈,主要用于在舊的瀏覽器或環(huán)境中將 ECMAScript 2015+ 代碼轉換為向后兼容版本的 JavaScript 代碼。
安裝依賴包
npm i -D babel-loader @babel/core @babel/preset-env
-
babel-loader:用于 babel 在 webapck 中的加載模塊。 -
@babel/core:babel 編譯工具; -
@babel/preset-env:babel 生成指定支持瀏覽器版本的編譯工具。
babel 支持兩種配置,一種是在項目根目錄下創(chuàng)建 .babelrc 配置文件,另一種是通過 webpack loader options 的方式配置
- .babelrc
{
"presets": [
[
"@babel/preset-env",
{
// 用于指定瀏覽器版本號
"targets": {
"browsers": "last 2 version"
}
}
]
]
}
- webpack loader options 配置
{
"module": {
"rules": [
{
"test": /\.js$/,
"exclude": /(node_modules)/,
"loader": "babel-loader",
"options": {
"presets": [
[
"@babel/preset-env",
{
// 用于指定瀏覽器版本號
"targets": {
"browsers": "last 2 version"
}
}
]
]
}
}
]
}
}
Babel 默認只轉換語法,而不轉換新的 API,如 Set、Promise、Map 等或是 ES6 對 Array、String 等擴展,如果需要使用新的 API 還需要使用對相應的 轉換插件 或 polyfill
- Babel Polyfill
npm i --save @babel/polyfill
只需在入口頭部引入即可
import '@babel/polyfill';
// 或
require('@babel/polyfill');
上面的使用方法,會導致打包后的文件過大,由于是在入口文件直接引入 polyfill,從而將會導入 polyfill 整個包,增加了無用代碼。
@babel/preset-env 中 useBuiltIns: 'usage' 按需引入,就是用于解決上面的問題。
{
"presets": [
[
"@babel/preset-env",
{
// 用于指定瀏覽器版本號
"targets": {
"browsers": "last 2 version"
},
"useBuiltIns": "usage"
}
]
]
}
@babel/polyfill 是通過改寫全局 prototype 的方式對新的 API 支持,比較適合單獨運行的項目。
- Babel RunTime Transform
npm i @babel/runtime --save
npm i @babel/plugin-transform-runtime --save-dev
{
"presets": [
[
"@babel/preset-env",
{
// 用于指定瀏覽器版本號
"targets": {
"browsers": "last 2 version"
}
}
]
],
"plugins": ["@babel/plugin-transform-runtime"]
}
@babel/runtime 的 polyfill 對象是臨時構造并 import/require 的,因此并不是真正的全局引用,由于不是全局引用,對于實例化對象的方法,并不能生效。比較適合編寫 第三方類庫。
文件操作
file-loader 可以解析項目中的 url 引入(不禁限于 CSS),根據(jù)配置,將圖片拷貝到相應路徑,并修改打包后文件的引用路徑。
{
"test": /\.(png|jpe?g|gif)$/,
"loader": "file-loader",
"options": {
// 輸出文件名稱,默認為:'[hash].[ext]'
"name": "[name].[hash:8].[ext]",
// 指定輸出文件存放路徑,默認為當前目錄
"outputPath": "assets/images",
// 指定輸出文件 公共路徑
// publicPath: '../',
// 如果是 true,生成一個文件(向文件系統(tǒng)寫入一個文件)。 如果是 false,loader 會返回 public UR不會生成文件
"emitFile": true
}
}
url-loader 與 file-loader 的作用是一樣的,但是 url-loader 可以通過 limit 配置限制輸出大小,如果小于 limit 字節(jié)的文件會被轉成 Base64 編碼,大于則會拷貝文件。
{
"test": /\.(png|jpe?g|gif)$/,
"loader": "url-loader",
"options": {
// 限制輸出文件大小,如果輸出文件小于該限制,則被轉成 base64 編碼,大于則會拷貝文件
"limit": 1024,
// 輸出文件名稱,默認為:'[hash].[ext]'
"name": "[name].[hash:8].[ext]",
// 指定輸出文件存放路徑,默認為當前目錄
"outputPath": "assets/images"
// 指定輸出文件 公共路徑
// publicPath: '../',
}
}
img-loader 可以壓縮圖片,也可以使用 image-webpack-loader 代替。
npm i -D img-loader imagemin imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo
插件具體參數(shù)可以查看 github
{
"loader": "img-loader",
"options": {
"plugins": [
require("imagemin-gifsicle")({}),
require("imagemin-mozjpeg")({}),
require("imagemin-pngquant")({}),
require("imagemin-svgo")({})
]
}
}
html-loader 可以設置 html 中的某些片段是要交給 webpack 處理的。
{
"test": /\.html$/,
"loader": "html-loader",
"options": {
// 設置交由 webpack 處理的屬性
"attrs": ["img:src", "img:data-src"]
}
}
當我們在使用第三方庫時,如果每個模塊都用到了它,但我們并不想在每個模塊中重復去寫 import 或 require
,我們可以使用下面兩種方式:
webpack.ProvidePlugin
{
"resolve": {
"alias": {
// 定義別名 指定本地文件路徑,$ 表示將 jquery 關鍵字解析到某個目錄的文件下,而不是解析某個目錄
"jquery$": path.join(__dirname, "./lib/jquery.min.js")
}
},
"plugins": [
new webpack.ProvidePlugin({
// "$": "jquery", // 導入 npm 依賴包
// "$": ["./lib/jquery.min.js"] // 指定本地文件路徑
"$": "jquery"
})
]
}
imports-loader
npm i imports-loader
{
"test": /\.js$/,
"loader": "imports-loader",
"options": {
"$": "jquery"
}
}
CSS
mini-css-extract-plugin 能夠將 CSS 提取到單獨的文件中,它為每個包含 CSS 的 JS 文件創(chuàng)建了一個 CSS 文件。
npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
{
"module": {
"rules": {
"test": /\.css$/,
"use": [
// mini-css-extract-plugin 只能用于生產環(huán)境,因此在 loader 中需要加入一個判斷
process.env.NODE_ENV === "development"
? "style-loader"
: {
"loader": MiniCssExtractPlugin.loader,
"options": {
// CSS 樣式用的背景圖片 會與抽取后的 CSS 文件路徑有沖突,在此設置公共相對路徑
"publicPath": "../../"
}
}
]
}
},
"plugins": [
new MiniCssExtractPlugin({
// 用于設置提取的 CSS 文件名稱與存儲路徑
"filename": "assets/styles/[name].[hash:8].css",
// 用于設置異步加載 CSS 文件名稱與存儲路徑,例如: import('./assets/styles/test.js')
"chunkFilename": "assets/styles/[id].[chunkhash:8].css"
})
]
}
optimize-css-assets-webpack-plugin 能夠壓縮 CSS 文件大小。
npm install --save-dev optimize-css-assets-webpack-plugin
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
{
"plugins": [
new OptimizeCssAssetsPlugin({
// 匹配需要壓縮的 CSS 文件正則,默認為 /\.css$/g
// assetNameRegExp: /\.optimize\.css$/g,
// 設置 CSS 壓縮工具 默認使用的是 cssnano
// cssProcessor: require('cssnano'),
// 設置 CSS 壓縮工具的 options ,默認 {}
// cssProcessorOptions: {},
// 設置 CSS 壓縮工具插件的 options, 默認 {}
// cssProcessorPluginOptions: {},
// 插件輸出信息是否輸出在控制臺,默認 true
// canPrint: true,
})
]
}
PostCSS 是一個允許使用 JS 插件轉換樣式的工具。
npm install postcss postcss-loader --save-dev
-
autoprefixer添加了 vendor 瀏覽器前綴,它使用 Can I Use 上面的數(shù)據(jù)。
npm install autoprefixer --save-dev
{
"module": {
"rules": [
{
"test": /\.css$/,
"use": [
"style-loader",
"css-loader",
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
plugins:[
require('autoprefixer')({
// 設置兼容瀏覽器版本
"browsers": ['last 2 versions'],
})
]
"sourceMap": true
}
}
]
}
]
}
}
不難發(fā)現(xiàn) autoprefixer 中需要配置 browsers,Babel 中也需要配置,我們可以通過下面兩種方法配置:
- package.json
"browserslist": [
"last 2 version"
]
- .browserslistrc
last 2 version
-
postcss-preset-env能夠轉換還未兼容 CSS 例如--color 變量。
postcss-preset-env 中已經引入了 autoprefixer,因此使用它的時候可以不用引入 autoprefixer。
npm install postcss-preset-env --save-dev
{
"module": {
"rules": [
{
"test": /\.css$/,
"use": [
"style-loader",
"css-loader",
{
"loader": "postcss-loader",
"options": {
"ident": "postcss",
plugins:[
require('postcss-preset-env')
]
"sourceMap": true
}
}
]
}
]
}
}
Webpack 打包優(yōu)化
- 提取公共代碼:減少代碼冗余,提高用戶下載代碼帶寬。
Webpack 4 提供了內置插件 webpack.SplitChunksPlugin
{
"optimization": {
"splitChunks": {
/**
* 拆分塊的名稱:boolean: true | function (module, chunks, cacheGroupKey) | string
* 如果為 true 將自動生成基于塊和緩存組密鑰的名稱,生產環(huán)境下建議 false 避免更改名稱
* 如果是一個函數(shù)允許自定義名稱,不推薦,容易造成代碼下載冗余
*/
"name": false,
"automaticNameDelimiter": "~", // 用于指定生成名稱的分隔符
"chunks": "initial", // 用于指定那些模塊需要分割, 可選項 "initial"(初始化) | "all"(默認) | "async"(動態(tài)加載)
"maxAsyncRequests": 1, // 按需加載時的最大并行請求數(shù)。
"maxInitialRequests": 1, // 入口點處的最大并行請求數(shù)。
"minChunks": 1, // 分割前必須共享模塊的最小塊數(shù)。
// 緩存組會繼承splitChunks的配置,但是test、priorty和reuseExistingChunk只能用于配置緩存組。
"cacheGroups": {
// 自定義緩存組屬性
"vendors": {
"test": /[\\/]node_modules[\\/]/, // 控制此緩存組選擇的模塊,使用正則表達式匹配
"filename": "venders", // 拆分出塊的名稱
"reuseExistingChunk": true, // 可設置是否重用已用chunk 不再創(chuàng)建新的chunk
"priority": "0" // 優(yōu)先級高的chunk為被優(yōu)先選擇,優(yōu)先級一樣的話,size大的優(yōu)先被選擇
// enforce: true, // 忽略外部 maxAsyncRequests、maxInitialRequests、minSize、minChunks 始終以 緩存組配置執(zhí)行
}
}
}
}
}
-
代碼分割與懶加載:能夠在最短的時間內,展示頁面。通過動態(tài)加載模塊的方式實現(xiàn)。
-
webpack 內置 methods
/** * 動態(tài)引入 模塊 * dependencies: [] 需要加載的模塊,并不會執(zhí)行,需要在回調函數(shù)中 require * callback: 回調函數(shù),執(zhí)行動態(tài)加載模塊的方法 * errorCallback: 錯誤回調,可以省略 * chunkName: 拆分塊名稱 */ require.ensure(dependencies, callback, errorCallback, chunkName) // 只接收引入的模塊 而不執(zhí)行,當兩個子模塊同時依賴一個模塊,可以通過這種引用將公共模塊提前引入到父模塊中 require.include(dependencies) -
ES2015 Loader 規(guī)范
import(/* webpackChunkName: 生成的 chunk 名稱 */).then() // 此時引入的 模塊已經執(zhí)行
-