1. 介紹
-
webpack是一個模塊打包(module bundler)工具。
A bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows to load parts for the application on demand. Through "loaders", modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and you custom stuff.
- 大版本變化
(1)Webpack V1.0.0-2014.2.20
編譯打包、HMR(模塊熱更新)、代碼分割、文件處理。
(2)Webpack V2.2.0-2017.1.18
Tree Shaking、ES6 modules(無需babel)、動態(tài)Import、新的文檔。
(3)Webpack V3.0.0-2017.6.19
Scope Hoisting(作用域提升)、Magic Comments(魔法注釋,配合動態(tài)import使用)
(4)Webpack V4.0.0-2018.2.25
支持零配置、開發(fā)與生產(chǎn)模式、SplitChunksPlugin替換CommonChunkPlugin。
2. 初始化
- 創(chuàng)建項目文件夾
webpack-operate - 創(chuàng)建
package.json文件
? webpack-operate npm init - 設置當前
npm項目為私有項目
在package.json中添加"private": true - 安裝
webpack以及webpack-cli
? webpack-operate npm i webpack webpack-cli --save-dev
業(yè)務邏輯中需要使用的包通過
--save方式安裝;只有打包過程需要使用的包通過--save-dev方式安裝。
- 命令介紹
- 獲取
Webpack信息
npm info webpack - 安裝指定版本
Webpack
npm i webpack@4.16.5 - 獲取全局環(huán)境中
webpack版本
webpack -v - 獲取當前項目中
webpack版本
npx webpack -v
3. 模塊打包
- 創(chuàng)建打包配置文件
webpack.dev.config.js - 創(chuàng)建入口文件
./src/index.js - 編輯打包配置文件
webpack.dev.config.js
const path = require('path')
module.exports = {
entry: './src/index.js',
output: {
filename: "main.js",
path: path.resolve(__dirname, 'dist')
}
}
'./src/index.js'是{"main": './src/index.js'}的簡寫。其中,main為chunk name。
如果希望將腳本打包到dist/js/文件夾中,可以修改filename值為js/main.js。即filename不僅可以設置文件名,還可以設置路徑。
- 創(chuàng)建打包命令
package.json文件
"scripts": {
"bundle": "webpack --config ./webpack.dev.config.js --mode development"
}
-
mode的含義
mode的值為development或production。當取值為production時,輸出文件會被壓縮。 - 打包配置
如果沒有配置文件,則使用默認配置,入口文件為./src/index.js,輸出文件為dist/main.js。
如果有./webpack.config.js文件,則默認使用該文件作為配置文件。
如果使用其它配置文件,則需要通過--config指定配置文件。 -
webpack版本
如果直接通過webpack命令打包,則是使用全局安裝的webpack打包。
如果通過npx webpack/npm run xxx(scripts內(nèi)部定義命令),則使用當前項目安裝的webpack打包。
? webpack-demo webpack -v
3.8.1
? webpack-demo npx webpack -v
4.29.6
? webpack-demo npm run version
> webpack-demo@1.0.0 ver /Users/nimengwei/Code/mycode/webpack/webpack-demo
> webpack -v
4.29.6
webpack3.X版本不支持零配置(沒有配置文件),除非在命令行中指定入口文件和出口文件(webpack entry output)。
webpack3.X版本不支持通過mode定義development(開發(fā)模式)或production(生產(chǎn)模式)。
- 執(zhí)行打包命令
npm run bundle
此時,生成dist文件夾,內(nèi)部有打包后文件main.js。
? webpack-operate npm run bundle
> webpack-operate@1.0.0 bundle /Users/nimengwei/Code/mycode/webpack/webpack-operate
> webpack --config ./webpack.dev.config.js --mode development
Hash: 5b90c629ada6fed12c59 //當前打包的唯一標識
Version: webpack 4.29.6 //webpack版本
Time: 75ms //打包用時
Built at: 04/02/2019 7:29:52 PM
//輸出文件 大小 輸入chunks 輸入chunks名稱
Asset Size Chunks Chunk Names
main.js 3.77 KiB main [emitted] main
Entrypoint main = main.js
[./src/index.js] 0 bytes {main} [built]
這里的
chunk名稱可以在entry入口中設置。
4. 清理打包文件
- 安裝依賴
? webpack-operate npm i clean-webpack-plugin -D - 編輯打包配置文件
webpack.dev.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
//...
plugins: [
new CleanWebpackPlugin(),
]
//...
}
-
plugin的作用是當打包進行到某一個時刻時,執(zhí)行某些操作。例如,clean-webpack-plugin就是在打包前清除上次打包生成文件。
5. html文件
5.1 html模板
-
html-webpack-plugin文檔 - 安裝依賴
? webpack-operate npm i html-webpack-plugin --save-dev - 創(chuàng)建
html模板文件
./index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack操作指南</title>
</head>
<body>
<h1>Webpack操作指南</h1>
</body>
</html>
- 編輯打包配置文件
webpack.dev.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//...
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
})
]
//...
}
- 執(zhí)行打包
npm run bundle
此時,打包結(jié)束后,打包文件夾中會以./index.html為模板,自動生成一個html文件,并將打包生成的js文件自動引入到該html文件中。 -
html-webpack-plugin就是在打包結(jié)束時根據(jù)指定模板生成html文件并插入打包生成的js文件。 - 其它配置項
new HtmlWebpackPlugin({
template: './index.html', //模板文件
chunks: ['app'], //生成html文件添加哪些chunks,默認全部添加
minify: {
collapseWhitespace: true //壓縮html
}
})
5.2 html中引入圖片
- 準備工作
配置5.1 html模板;
配置9. 加載圖片; - 修改
./index.html
...
<img src="./assets/controls.png" data-src="./assets/controls.png">
<img src="${require('./assets/controls.png')}" data-src="${require('./assets/controls.png')}">
...
- 執(zhí)行打包
npm run bundle
發(fā)現(xiàn)第一種圖片引入方式無法顯示,第二種圖片引入方式可以顯示。
第二種圖片引入方式可以顯示的前提是需要在配置中為圖片指定
loader(推薦file-loader或url-loader)。
- 安裝依賴
npm i -D html-loader - 編輯打包配置文件
webpack.dev.config.js
{
test: /\.html$/,
use: [
{
loader: "html-loader",
options: {
attrs: [':src', ':data-src']
}
}
]
}
- 執(zhí)行打包
npm run bundle
此時第一種圖片引入方式可以顯示,第二種圖片引入方式無法顯示。
6. 模塊熱更新
6.1 webpack --watch
- 編輯打包命令
package.json文件
"scripts": {
//...
"watch": "webpack --watch --config ./webpack.dev.config.js --mode development",
//...
}
- 執(zhí)行打包
npm run watch
webpack會監(jiān)聽打包文件,當文件內(nèi)容發(fā)生變化時自動重新執(zhí)行打包操作。
例如,修改src/index.js文件并保存(webpack會自動重新打包),刷新dist/index.html文件,頁面執(zhí)行修改后的js文件。
webpack --watch命令僅僅實現(xiàn)了自動打包操作,無法自動打開瀏覽器,無法開啟一個本地服務,無法自動刷新。
-
webpack命令配置參數(shù)
語法:webpack [--config webpack.config.js]
webpack --json: 以JSON格式輸出webpack的運行結(jié)果
--config: 配置文件的路徑
--progress:打印出編譯進度的百分比值
--watch, -w:觀察文件系統(tǒng)的變化
--color, --colors:開啟/關閉控制臺的顏色
--display-reasons:顯示模塊包含在輸出中的原因
6.2 webpack-dev-server
6.2.1 快速開發(fā)應用程序
- 安裝依賴
? webpack-operate npm i webpack-dev-server --save-dev - 編輯打包配置文件
webpack.dev.config.js
module.exports = {
//...
devServer: {
open: true, //瀏覽器自動打開
port: 9000, //指定端口號
contentBase: './dist' //啟動服務的目錄
}
//...
}
默認瀏覽器不自動打開,默認端口為
8080。
- 編輯打包命令
package.json文件
"scripts": {
"bundle": "webpack --config ./webpack.dev.config.js --mode development",
"dev": "webpack-dev-server --config ./webpack.dev.config.js --mode development"
},
webpack3.X版本不支持--mode development/production設置打包模式。
- 執(zhí)行打包命令
npm run dev
此時瀏覽器自動打開http://localhost:9000,可以看到html-webpack-plugin在打包結(jié)束后生成的index.html文件(由于html-webpack-plugin的功能,該文件以./index.html為模板,以./release/bundle.js為JavaScript腳本)。且修改./src/index.js文件時,瀏覽器自動刷新。 -
webpack-dev-server依賴于webpack-cli。安裝webpack-cli會同時安裝webpack-dev-server。對于webpack3.X版本來說,使用最新版本的webpack-dev-server會報錯,可通過降低版本解決,例如2.9.7版本。
使用
webpack-dev-server打包會在內(nèi)存中生成打包文件,提升打包效率。 并不會實際生成dist文件夾以及打包輸出文件。如果需要打包生成文件,仍然需要使用webpack命令。
當前配置雖然實現(xiàn)了代碼修改自動打包瀏覽器自動刷新。但瀏覽器刷新之前的頁面操作無法保存,尚未實現(xiàn)不刷新瀏覽器在運行時更新所修改模塊。
6.2.2 devServer.historyApiFallback
- 當使用 HTML5 History API 時,任意的
404響應都可能需要被替代為index.html。通過傳入以下啟用:
historyApiFallback: true
- 通過傳入一個對象,比如使用
rewrites這個選項,此行為可進一步地控制:
historyApiFallback: {
rewrites: [
{ from: /^\/$/, to: '/views/landing.html' },
{ from: /^\/subpage/, to: '/views/subpage.html' },
{ from: /./, to: '/views/404.html' }
]
}
- 可以通過正則表達式匹配通用參數(shù)
historyApiFallback: {
rewrites: [
{
from: /^\/([a-zA-Z0-9]+\/?)([a-zA-Z0-9]+)/,
to: function(context) {
console.log(context.match[1], context.match[2])
return '/' + context.match[1] + context.match[2] + '.html'
}
},
]
}
6.2.3 代理
- 文檔
devserver-proxy
http-proxy-middleware - 配置代理
編輯打包配置文件
webpack.dev.config.js
module.exports = {
//...
devServer: {
open: true, //瀏覽器自動打開
port: 9000,
proxy: {
'/api': {
changeOrigin: true, //changes the origin of the host header to the target URL
target: 'http://localhost:8880'
//訪問hocalhost:9000/api/...時,會被代理到hocalhost:8880/api/...
}
}
}
}
此時,訪問hocalhost:9000/api/...時,會被代理到hocalhost:8880/api/...。
proxy代理只在開發(fā)環(huán)境中使用。
- 支持
https
默認情況下,不接受運行在HTTPS上,且使用了無效證書的后端服務器。如果你想要接受,修改配置如下:
proxy: {
'/api': {
target: 'http://localhost:8880',
secure: false
}
}
6.3 模塊熱更新(HMR)
-
模塊熱替換(
hot module replacement或HMR)是webpack提供的最有用的功能之一。它允許在運行時更新所有類型的模塊,而無需完全刷新。
模塊熱更新為無刷新瀏覽器更新。
- 編輯打包配置文件
webpack.dev.config.js
//...
devServer: {
open: true, //瀏覽器自動打開
port: 9000,
contentBase: './dist',
hot: true,
hotOnly: true,
}
//...
plugins: [
new webpack.HotModuleReplacementPlugin(),
//...
]
//...
hotOnly為true,表示當html文件失效時,不自動刷新瀏覽器。
- 執(zhí)行打包命令
npm run dev
此時實現(xiàn)了CSS文件的模塊熱更新。 - 監(jiān)聽
js文件變化回調(diào)
if (module.hot) {
module.hot.accept('./print.js', function() {
console.log('Accepting the updated printMe module!');
printMe();
})
}
./print.js為當前打包中引入的模塊。
js模塊熱更新需要module.hot.accept監(jiān)聽文件變化的方式實現(xiàn)。
css-loader已經(jīng)幫我們做了module.hot.accept監(jiān)聽CSS文件變化的代碼。
在react和vue項目開發(fā)時,第三方loader也已經(jīng)實現(xiàn)了module.hot.accept監(jiān)聽。
6.4 自定義打包腳本
- 安裝依賴
? webpack-operate npm i express webpack-dev-middleware -D - 編輯打包命令
package.json文件
"scripts": {
//...
"server": "node server.js"
//...
}
- 編輯打包配置文件
webpack.dev.config.js
output: {
//...
publicPath: './',
//...
}
- 創(chuàng)建并編輯打包腳本
./server.js
const express = require('express')
const webpack = require('webpack')
const webpackDevMiddleware = require('webpack-dev-middleware')
const config = require('./webpack.dev.config')
//根據(jù)config配置,返回一個編譯器
//在node中直接使用Webpack
const complier = webpack(config)
//創(chuàng)建一個express服務
const app = express();
//使用webpackDevMiddleware中間件
app.use(webpackDevMiddleware(complier, {
publicPath: config.output.publicPath
}))
//開啟服務,監(jiān)聽8080端口
app.listen(8080, () => {
console.log("server is running!")
})
- 執(zhí)行打包命令
npm run server
此時,開啟了一個本地服務,手動打開http://localhost:8080/地址,可以看到html-webpack-plugin在打包結(jié)束后生成的index.html文件。與webpack --watch類似,修改代碼后會重新打包,但沒有實現(xiàn)自動打開瀏覽器,沒有實現(xiàn)自動刷新。可以通過修改server.js的方式添加這些功能。
7. 開發(fā)調(diào)試
7.1 devtool
-
SourceMap
保存打包前后代碼位置信息,方便調(diào)試。 - 編輯打包配置文件
webpack.dev.config.js
module.exports = {
//...
devtool: 'inline-source-map',
//...
}
-
devtool字段值含義
image.png
① source-map:完整映射到原始源代碼。
② inline: sourceMappingURL指向base64字符串,否則打包生成map文件。
③ cheap:代碼只映射到行信息,只映射業(yè)務代碼,不映射第三方模塊代碼。
④ module: 不僅映射業(yè)務代碼,而且映射第三方模塊代碼。
⑤ eval:以eval(string)的方式運行代碼,打包速度最快。
eval打包速度最快。
-
devtool舉例介紹
①source-map表示完整映射到原始源代碼;sourceMappingURL指向打包生成的map文件。
②inline-source-map表示完整映射到原始源代碼;sourceMappingURL指向base64字符串。
③inline-cheap-source-map表示代碼只映射到行信息;sourceMappingURL指向base64字符串。
④cheap-module-source-map表示代碼只映射到行信息;不僅映射業(yè)務代碼,而且映射第三方模塊代碼;sourceMappingURL指向打包生成的map文件。 -
devtool適用環(huán)境
開發(fā)環(huán)境:eval、eval-source-map、cheap-eval-source-map、cheap-module-eval-source-map。
生產(chǎn)環(huán)境:source-map、hidden-source-map、nosources-source-map。 -
devtool最佳實踐
開發(fā)(development)環(huán)境:cheap-module-eval-source-map/cheap-module-source-map
生產(chǎn)(production)環(huán)境:source-map
7.2 插件
- 插件實現(xiàn)了對
source map生成,進行更細粒度的控制。它可以替代devtool選項。 - 文檔
eval-source-map-dev-tool-plugin
source-map-dev-tool-plugin
7.3 CSS SourceMap
- 準備工作
10. 加載樣式 - 編輯打包配置文件
webpack.dev.config.js
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {
//singleton: true,
sourceMap: true
}
},
{
loader: 'css-loader',
options: {
sourceMap: true
}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [ require('autoprefixer')()],
sourceMap: true
}
}
]
}
-
CSS開啟SourceMap配置總結(jié)
(1) 關閉style-loader中的singleton選項。
(2) 處理樣式的所有loader(style-loader、css-loader、postcss-loader)都開啟sourceMap選項。
8. 加載腳本
8.1 ES6語法
- 安裝依賴
npm i -D babel-loader @babel/core @babel/preset-env
打開babel文檔,可以看到在不同工具中使用
babel的方法。
- 編輯打包配置文件
//...
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
plugins: []
}
}
]
}
//...
- 執(zhí)行打包命令
npm run bundle
此時生成的app.bundle.js大小為28.8 KB。
@babel/preset-env只支持stage-4范圍內(nèi)的JavaScript語法。
stage的包含順序是:左邊包含右邊全部特性。
stage-0 > stage-1 > stage-2 > stage-3 > stage-4。
8.2 添加polyfill
-
babel-polyfill文檔 -
babel-polyfill介紹
Babel默認只轉(zhuǎn)換新的JavaScript句法(syntax),而不轉(zhuǎn)換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉(zhuǎn)碼。
舉例來說,ES6在Array對象上新增了Array.from方法。Babel就不會轉(zhuǎn)碼這個方法。如果想讓這個方法運行,必須使用babel-polyfill,為當前環(huán)境提供一個墊片。 - 安裝依賴
npm install --save @babel/polyfill
@babel/polyfill依賴安裝到dependencies,而不是devDependencies。 - 編輯入口文件
src/index.js
import "@babel/polyfill";
//...
- 執(zhí)行打包命令
npm run bundle
由于將所有的polyfill都打包輸出,此時生成的app.bundle.js大小為534 KB。 -
polyfill按需打包
(1) 安裝依賴
npm i core-js@2 --save
(2) 編輯打包配置文件
webpack.dev.config.js
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
[ "@babel/preset-env", {"useBuiltIns": "usage", "corejs": 2}]
],
plugins: []
}
}
useBuiltIns可選值為一個布爾值,默認為false。當設置為true時,表示使用瀏覽器內(nèi)置的語法,而不是對任意插件進行全量polyfill。
(3) 編輯入口文件
src/index.js
//import "@babel/polyfill";
//...
When setting "useBuiltIns": "usage", polyfills are automatically imported when needed. Please remove the import '@babel/polyfill' call or use "useBuiltIns": 'entry' instead.
(4) 執(zhí)行打包命令
npm run bundle
此時生成的app.bundle.js大小為84.6 KB。
- 指定項目支持的瀏覽器版本
(1) 編輯打包配置文件
webpack.dev.config.js
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
presets: [
[
"@babel/preset-env",
{
targets: {
//browsers: [ ">1%"],
chrome: "67",
safari: "11.1"
},
"useBuiltIns": "usage",
"corejs": 2
}
]
],
plugins: []
}
}
當指定
targets瀏覽器版本時,@babel/preset-env和@babel/polyfill只對指定瀏覽器版本不支持的語法進行轉(zhuǎn)碼和添加墊片。
(2) 執(zhí)行打包命令
npm run bundle
此時生成的app.bundle.js大小為28.7 KB。
8.3 transform-runtime
-
transform-runtime文檔 -
transform-runtime與babel-polyfill對比
運行環(huán)境中并沒有實現(xiàn)的API,babel-polyfill會給其做兼容。 這樣做有一個缺點,就是會污染全局變量。不推薦在一些方法類庫中去使用。為了不污染全局對象和內(nèi)置的對象原型,又想使用新的API,就可以使用transform-runtime。
transform-runtime可以以閉包的形式實現(xiàn)babel-polyfill的功能,不污染全局變量。src/index.js文件中無需引入import "@babel/polyfill";。
- 安裝依賴
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
npm install --save @babel/runtime-corejs2 - 編輯打包配置文件
webpack.dev.config.js
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
options: {
plugins: [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
}
corejs如果設置為false,則不需要安裝@babel/runtime-corejs2。
- 執(zhí)行打包命令
npm run bundle
此時生成的app.bundle.js大小為116 KiB。 - 簡化打包配置文件
創(chuàng)建.babelrc文件,并將options值剪切到該文件中。
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
}
如果你寫的是業(yè)務代碼,則使用
babel-polyfill。如果你寫的是第三方類庫,則需要使用plugin-transform-runtime。
8.4 Stage-0語法支持
- 安裝依賴
? webpack-operate npm i @babel/plugin-proposal-function-bind @babel/plugin-proposal-export-default-from @babel/plugin-proposal-logical-assignment-operaors @babel/plugin-proposal-optional-chaining @babel/plugin-proposal-pipeline-operator @babel/plugin-proposal-nullish-coalescing-operator @babel/plugin-proposal-do-expressions @babel/plugin-proposal-decorators @babel/plugin-proposal-function-sent @babel/plugin-proposal-export-namespace-from @babel/plugin-proposal-numeric-separator @babel/plugin-proposal-throw-expressions @babel/plugin-syntax-dynamic-import @babel/plugin-syntax-import-meta @babel/plugin-proposal-class-properties @babel/plugin-proposal-json-strings -D
- 編輯
babel配置文件
"plugins": [
// Stage 0
"@babel/plugin-proposal-function-bind",
// Stage 1
"@babel/plugin-proposal-export-default-from",
"@babel/plugin-proposal-logical-assignment-operators",
["@babel/plugin-proposal-optional-chaining", { "loose": false }],
["@babel/plugin-proposal-pipeline-operator", { "proposal": "minimal" }],
["@babel/plugin-proposal-nullish-coalescing-operator", { "loose": false }],
"@babel/plugin-proposal-do-expressions",
// Stage 2
["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-function-sent",
"@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-proposal-numeric-separator",
"@babel/plugin-proposal-throw-expressions",
// Stage 3
"@babel/plugin-syntax-dynamic-import",
"@babel/plugin-syntax-import-meta",
["@babel/plugin-proposal-class-properties", { "loose": false }],
"@babel/plugin-proposal-json-strings"
]
8.5 React語法
-
babel-preset-react文檔 - 安裝依賴
? webpack-operate npm i react react-dom --save
? webpack-operate npm install --save-dev @babel/preset-react - 編輯
babel配置文件
{
"presets": [
[
"@babel/preset-env", {
"targets": {
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": 2
}
],
"@babel/preset-react"
]
}
presets的執(zhí)行順序為數(shù)組從后向前。@babel/preset-env和@babel/preset-react的順序不能變。
- 編輯入口文件
./src/index.js
import React, { Component } from 'react'
import ReactDom from 'react-dom'
class App extends Component {
render() {
return (
<div>
Hello world!!!
</div>
)
}
}
ReactDom.render(<App/>, document.getElementById('root'))
9. 加載圖片
9.1 file-loader
- 安裝依賴
npm install --save-dev file-loader - 編輯打包配置文件
webpack.dev.config.js
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}]
}
- 添加圖片
src/assets/weixin.jpg - 修改
src/index.js文件
var img = require('./assets/weixin.jpg')
var myLogo = new Image()
myLogo.src = img
document.body.appendChild(myLogo)
圖片資源作為
img元素。
- 修改
src/style.css文件
body {
background: url('./logo.png')
}
圖片資源作為元素背景。
- 修改圖片打包輸出路徑及文件名
webpack.dev.config.js
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader:'file-loader',
options: {
name: '[name]-[hash:5].[ext]',
outputPath: 'images/'
}
}
}]
}
file-loader中的placeholders文檔。
9.2 url-loader
- 安裝
url-loader
? webpack-operate npm i url-loader --save-dev - 修改打包配置文件
webpack.dev.config.js
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader:'url-loader',
options: {
name: '[name]-[hash:5].[ext]',
outputPath: 'images/'
}
}
}]
}
url-loader擁有file-loader的全部功能。
url-loader會將圖片轉(zhuǎn)化為base64字符串。
- 將大于
4KB的圖片資源打包輸出
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: {
loader:'url-loader',
options: {
name: '[name]-[hash:5].[ext]',
outputPath: 'images/',
limit: 4096
}
}
}]
}
9.3 其它loader
- 圖片壓縮
img-loader - 自動合成雪碧圖
postcss-sprites
10. 加載樣式
10.1 使用CSS樣式
- 安裝依賴
npm install --save-dev style-loader css-loader - 編輯打包配置文件
webpack.dev.config.js
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader", // 將 JS 字符串生成為 style 節(jié)點
"css-loader", // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
]
}
]
}
webpack中loader數(shù)組的執(zhí)行順序為從后向前。
- 創(chuàng)建
src/style.css文件
.img {
width: 300px;
height: 300px;
transform: translate(100px, 100px);
}
- 修改
src/index.js文件
import './style.css'
var logo = require('./assets/weixin.jpg')
var myLogo = new Image()
myLogo.src = logo
myLogo.classList.add('img')
document.body.appendChild(myLogo)
使用
file-loader或url-loader后才能支持weixin.jpg文件引入。
這種方式引入的CSS文件會被style標簽包裹并插入到頁面head內(nèi)部。
- 查詢參數(shù)importLoaders
用于配置css-loader作用于@import的資源之前有多少個loader。
10.2 style-loader配置項
-
style-loader配置項文檔 - 編輯打包配置文件
{
test: /\.css$/,
use: [
{
loader: "style-loader",
options: {
insertInto: () => document.querySelector("#root"),//插入到指定DOM
singleton: true, //是否只使用一個style標簽
transform: './src/css-transform.js'
}
},
'css-loader'
]
}
- 創(chuàng)建并編輯變形文件
module.exports = function(css) {
// Here we can change the original css
console.log(css);
if(window.innerWidth > 768) {
return css.replace('yellow', 'red');
} else {
return css.replace('yellow', 'pink');
}
};
10.3 style-loader/useable
- 編輯打包配置文件
webpack.dev.config.js
{
test: /\.css$/,
use: [
'style-loader/useable',
'css-loader'
]
}
- 修改
src/index.js文件
import base from './style.css'
let flag = true;
setInterval(() => {
if(flag) {
base.unuse()
} else {
base.use()
}
flag = !flag
}, 500);
./style.css樣式每500ms在生效和不生效兩種狀態(tài)之間切換一次。
使用
style-loader/useable后,樣式默認不生效。必須調(diào)用use()方法才會生效。
10.4 CSS模塊化
- 修改
webpack.dev.config.js
{
test: /\.css$/,
use: [
{
loader: 'css-loader',
options: {
modules: true,
localIdentName: '[path][name]__[local]--[hash:base64:5]'
}
}
]
}
- 修改
src/index.js文件
import style from './style.css'
var src = require('./assets/weixin.jpg')
var logo1 = new Image()
logo1.src = src
logo1.classList.add(style.img)
document.body.appendChild(logo1)
var logo2 = new Image()
logo2.src = src
logo2.classList.add('img')
document.body.appendChild(logo2)
./style.css并不會成為全局樣式。
style.img類會創(chuàng)建一個由[path][name]__[local]--[hash:base64:5]組成的類名,并添加./style.css中的.img樣式。
'img'類并不會添加./style.css中的.img樣式。
- 作用域
:local(.className)可以被用來在局部作用域中聲明className。局部的作用域標識符會以模塊形式暴露出去。
:global(.className)可以用來聲明一個明確的全局選擇器。
以上配置都是
css-loader@2.X版本的配置,css-loader@1.X版本配置與@2.X版本配置不同。
10.5 使用SCSS樣式
- 安裝依賴
npm install sass-loader node-sass --save-dev - 編輯打包配置文件
webpack.dev.config.js
{
test: /\.scss$/,
use: [
"style-loader", // 將 JS 字符串生成為 style 節(jié)點
"css-loader", // 將 CSS 轉(zhuǎn)化成 CommonJS 模塊
"sass-loader" // 將 Sass 編譯成 CSS,默認使用 Node Sass
]
}
- 創(chuàng)建
src/style.scss文件
body {
img {
width: 300px;
height: 300px;
}
}
- 引入
src/style.scss文件
import './style.scss'
10.6 添加樣式兼容前綴
- 安裝依賴
? webpack-operate npm i postcss-loader autoprefixer -D
-D等價與--save-dev
- 編輯打包配置文件
webpack.dev.config.js
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [ require('autoprefixer')()]
}
}
]
}
]
}
postcss-loader如果與sass-loader同時使用,則postcss-loader在loader數(shù)組中位于sass-loader之后。
10.7 使用字體圖標
- 進入阿里云圖標網(wǎng)站
- 創(chuàng)建項目并添加圖標
- 下載
Unicode至本地并解壓 - 拷貝文件
將iconfont.eot、iconfont.svg、iconfont.ttf、iconfont.woff文件拷貝到項目中。項目路徑如下:
./src/font/iconfont.XXX - 將
iconfont.css文件內(nèi)容拷貝到src/style.css并修改url路徑
@font-face {font-family: "iconfont";
src: url('./font/iconfont.eot?t=1554215262535'); /* IE9 */
src: url('./font/iconfont.eot?t=1554215262535#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAALoAAsAAAAABpQAAAKbAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCcAqBHIEcATYCJAMICwYABCAFhG0HMxvFBRHVk0tkXx9wcomVY8Ck6KrLNGtjkwhFT/9iuvM1Ch7cCAfnfTw87fft3Dfz/86KGyLaRCuNRKKSNVESGRqRrB2P4om6rwEKuABNzW1ibUkMt+iNRsSTWPbMSH8ks/pynaO0JXqChifQ1nSPpAg5c5zvkrXsITvJd+x8WEbQKUyFsAKgSxAl3/k81+QroAPJD3SOt6qgosQBjvc8QLvsaCAD3hg3jF3gEh4TaDNlQ5xZNJpUkVmrAnE7nQ+pkkvLMqsVoVlzMI/FkUr1qHJ5HP4+fu0KiUojsdou3apPqBNfsZekXb3Sni8IdoyEDWTiWm35giQYm7SVszn21Rx8Xaxr3yv26hLsr7MaO8AEVO5J42pvtVbBbU21iUOjPpfYvNt9hgdX+HwprZvevb4Q0sYkMrwR8lH7b63z+LyxOsJm/44P0PtN9uLlapBnxrYD/u7smF2/dxj+WHCwmMGTRa1+YXbxX60ql7Pck+SViyP1t7rUCdRb9T+Hk/PvXnP2eSl3U+5/6WXw7mVuqO8XRTlDJPhTXdl6i7zAdVa5ymiZHKhs3kFHmzbUULzPl7H2wduqh2JwRLTFZJpLirWMGTIbaGi3B03FcbRZd/54u353JJFbseYeQOjxFJUuL5D0eEtk5jMahvxBU0+waHMjuLDdUoCpNiUMGJ8Q/iCN6ko6Ptai5gvGryKgrC7kHkgqzYGlm8XaEiukObaod2wzS5BUl7AAz2FR1NBQnWHEesLcOIYh696kR3UpNDElDBifEP4gjepK+qNZK33/gvGrCKhloCZ/IKl0drB0swexNFe9Bu5FjXrHNrMESXUJCzAPi6KGpn5ehhHryYhi4xj2MNnXrK9vLb/uGLQJy1F17B1JV3FAXli8UAgA') format('woff2'),
url('./font/iconfont.woff?t=1554215262535') format('woff'),
url('./font/iconfont.ttf?t=1554215262535') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('./font/iconfont.svg?t=1554215262535#iconfont') format('svg'); /* iOS 4.1- */
}
:global(.iconfont) {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
:global(.iconstar_blue:before) {
content: "\e625";
}
由于
css-loader使用了css模塊化(modules), 所以這里需要使用:global(.className)聲明全局選擇器。
- 修改
src/index.js
import './style.css'
document.body.innerHTML = `<div>
<span class='iconfont iconstar_blue'></span>
<span class='iconfont'></span>
</div>`
- 編輯打包配置文件
{
test: /\.(eot|ttf|svg|woff)$/,
use: {
loader: "file-loader",
options: {
name: '[name]-[hash:5].[ext]',
outputPath: 'font/'
}
}
}
11. Entry與Output配置
11.1 多個輸出文件
- 創(chuàng)建
src/print.js文件
console.log('print')
- 修改打包配置文件
webpack.dev.config.js
entry: {
app: './src/index.js',
print: './src/print.js'
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
- 執(zhí)行打包命令
npm run dev
webpack打包輸出幾個bundle文件并不是由模塊依賴樹決定,而是根據(jù)打包配置文件中的output決定。即使src/index.js文件引入了src/print.js文件,仍會將后者打包輸出為單獨的bundle。
11.2 publicPath配置
- 修改打包配置文件
webpack.dev.config.js
//...
output: {
publicPath: 'http://cdn.com.cn',
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
//...
- 執(zhí)行打包命令
npm run dev
此時,通過html-webpack-plugin插件在dist目錄中生成的html文件中引入打包生成的js文件的src會添加http://cdn.com.cn公共路徑。
<script type="text/javascript" src="http://cdn.com.cn/app.bundle.js"></script>
<script type="text/javascript" src="http://cdn.com.cn/print.bundle.js"></script>
11.3 瀏覽器長緩存(Caching)
- 提取第三方庫的代碼、使用
contentHash、提取webpack runtime。 - 在生產(chǎn)環(huán)境中。為了利用瀏覽器緩存并及時更新。我們希望當代碼變化時,打包生成的文件名變化。當代碼沒有變化時,打包生成的文件名不變。
- 修改打包配置文件
webpack.dev.config.js
//...
output: {
filename: '[name].[contenthash].bundle.js',
chunkFilename: '[name].[contenthash].chunk.js',
},
//...
打生產(chǎn)環(huán)境包才需要這樣配置。
在使用ExtractTextWebpackPlugin時,可以用[contenthash]來獲取提取文件的hash既不是[hash]也不是[chunkhash])。
- 在
webpack 4早期版本中,可能出現(xiàn)代碼內(nèi)容沒有變化,但是重新打包contentHash仍然變化的情況。原因是打包輸出的js文件之間的關系代碼manifest發(fā)生變化??梢酝ㄟ^將manifest代碼分割的方式解決。
//...
optimization: {
runtimeChunk: {
name: 'runtime'
}
}
//...
最新版本
webpack不需要此項配置。
