如果你是從開始一直在沿用指南的示例,現(xiàn)在會有一個小項目,顯示 ?"Hello webpack"
??,F(xiàn)在我們嘗試混合一些其他資源,比如 images,看看 webpack 如何處理。
在 webpack 出現(xiàn)之前,前端開發(fā)人員會使用 grunt 和 gulp 等工具來處理資源,并將它們從 /src 文件夾移動到 /dist 或 /build 目錄中。JavaScript 模塊也遵循同樣方式,但是,像 webpack 這樣的工具,將動態(tài)打包所有依賴(創(chuàng)建所謂的 依賴圖(dependency graph))。這是極好的創(chuàng)舉,因為現(xiàn)在每個模塊都可以明確表述它自身的依賴,可以避免打包未使用的模塊。
webpack 最出色的功能之一就是,除了引入 JavaScript,還可以通過 loader 或內(nèi)置的 Asset Modules 引入任何其他類型的文件。也就是說,以上列出的那些 JavaScript 的優(yōu)點(例如顯式依賴),同樣可以用來構(gòu)建 web 站點或 web 應(yīng)用程序中的所有非 JavaScript 內(nèi)容。讓我們從 CSS 開始起步,或許你可能已經(jīng)熟悉了下面這些設(shè)置。
在開始之前,讓我們對項目做一個小的修改:
dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
- <title>起步</title>
+ <title>管理資源</title>
</head>
<body>
- <script src="main.js"></script>
+ <script src="bundle.js"></script>
</body>
</html>
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
- filename: 'main.js',
+ filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
為了在 JavaScript 模塊中 import 一個 CSS 文件,你需要安裝 style-loader 和 css-loader,并在 module 配置 中添加這些 loader:
npm install --save-dev style-loader css-loader
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
+ module: {
+ rules: [
+ {
+ test: /\.css$/i,
+ use: ['style-loader', 'css-loader'],
+ },
+ ],
+ },
};
模塊 loader 可以鏈式調(diào)用。鏈中的每個 loader 都將對資源進行轉(zhuǎn)換。鏈會逆序執(zhí)行。第一個 loader 將其結(jié)果(被轉(zhuǎn)換后的資源)傳遞給下一個 loader,依此類推。最后,webpack 期望鏈中的最后的 loader 返回 JavaScript。
應(yīng)保證 loader 的先后順序:'style-loader' 在前,而 'css-loader' 在后。如果不遵守此約定,webpack 可能會拋出錯誤。
這使你可以在依賴于此樣式的 js 文件中 import './style.css'。現(xiàn)在,在此模塊執(zhí)行過程中,含有 CSS 字符串的 <style> 標簽,將被插入到 html 文件的 <head> 中。
我們嘗試一下,通過在項目中添加一個新的 style.css 文件,并將其 import 到我們的 index.js 中:
project
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- style.css
|- index.js
|- /node_modules
src/style.css
.hello {
color: red;
}
src/index.js
import _ from 'lodash';
+import './style.css';
function component() {
const element = document.createElement('div');
// Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ element.classList.add('hello');
return element;
}
document.body.appendChild(component());
現(xiàn)在運行 build 命令:
$ npm run build
...
[webpack-cli] Compilation finished
asset bundle.js 72.6 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1000 bytes 5 modules
orphan modules 326 bytes [orphan] 1 module
cacheable modules 539 KiB
modules by path ./node_modules/ 538 KiB
./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
modules by path ./src/ 965 bytes
./src/index.js + 1 modules 639 bytes [built] [code generated]
./node_modules/css-loader/dist/cjs.js!./src/style.css 326 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 2231 ms
再次在瀏覽器中打開 dist/index.html,你應(yīng)該看到 Hello webpack 現(xiàn)在的樣式是紅色。要查看 webpack 做了什么,請檢查頁面(不要查看頁面源代碼,它不會顯示結(jié)果,因為 <style> 標簽是由 JavaScript 動態(tài)創(chuàng)建的),并查看頁面的 head 標簽。它應(yīng)該包含 style 塊元素,也就是我們在 index.js 中 import 的 css 文件中的樣式。
注意,在多數(shù)情況下,你也可以進行 壓縮 CSS,以便在生產(chǎn)環(huán)境中節(jié)省加載時間。最重要的是,現(xiàn)有的 loader 可以支持任何你可以想到的 CSS 風(fēng)格 - postcss, sass 和 less 等。
假如,現(xiàn)在我們正在下載 CSS,但是像 background 和 icon 這樣的圖像,要如何處理呢?在 webpack 5 中,可以使用內(nèi)置的 Asset Modules,我們可以輕松地將這些內(nèi)容混入我們的系統(tǒng)中:
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
+ {
+ test: /\.(png|svg|jpg|jpeg|gif)$/i,
+ type: 'asset/resource',
+ },
],
},
};
現(xiàn)在,在 import MyImage from './my-image.png' 時,此圖像將被處理并添加到 output 目錄,并且 MyImage 變量將包含該圖像在處理后的最終 url。在使用 css-loader 時,如前所示,會使用類似過程處理你的 CSS 中的 ?url('./my-image.png')
?。loader 會識別這是一個本地文件,并將 './my-image.png' 路徑,替換為 output 目錄中圖像的最終路徑。而 html-loader 以相同的方式處理 ?<img src="./my-image.png" />
?。
我們向項目添加一個圖像,然后看它是如何工作的,你可以使用任何你喜歡的圖像:
project
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- icon.png
|- style.css
|- index.js
|- /node_modules
src/index.js
import _ from 'lodash';
import './style.css';
+import Icon from './icon.png';
function component() {
const element = document.createElement('div');
// Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.classList.add('hello');
+ // 將圖像添加到我們已經(jīng)存在的 div 中。
+ const myIcon = new Image();
+ myIcon.src = Icon;
+
+ element.appendChild(myIcon);
+
return element;
}
document.body.appendChild(component());
src/style.css
.hello {
color: red;
+ background: url('./icon.png');
}
重新構(gòu)建并再次打開 index.html 文件:
$ npm run build
...
[webpack-cli] Compilation finished
assets by status 9.88 KiB [cached] 1 asset
asset bundle.js 73.4 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1.82 KiB 6 modules
orphan modules 326 bytes [orphan] 1 module
cacheable modules 540 KiB (javascript) 9.88 KiB (asset)
modules by path ./node_modules/ 539 KiB
modules by path ./node_modules/css-loader/dist/runtime/*.js 2.38 KiB
./node_modules/css-loader/dist/runtime/api.js 1.57 KiB [built] [code generated]
./node_modules/css-loader/dist/runtime/getUrl.js 830 bytes [built] [code generated]
./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
modules by path ./src/ 1.45 KiB (javascript) 9.88 KiB (asset)
./src/index.js + 1 modules 794 bytes [built] [code generated]
./src/icon.png 42 bytes (javascript) 9.88 KiB (asset) [built] [code generated]
./node_modules/css-loader/dist/cjs.js!./src/style.css 648 bytes [built] [code generated]
webpack 5.4.0 compiled successfully in 1972 ms
如果一切順利,你現(xiàn)在應(yīng)該看到你的 icon 圖標成為了重復(fù)的背景圖,以及 Hello webpack 文本旁邊的 img 元素。如果檢查此元素,你將看到實際的文件名已更改為 29822eaa871e8eadeaa4.png。這意味著 webpack 在 src 文件夾中找到我們的文件,并對其進行了處理!
那么,像字體這樣的其他資源如何處理呢?使用 Asset Modules 可以接收并加載任何文件,然后將其輸出到構(gòu)建目錄。這就是說,我們可以將它們用于任何類型的文件,也包括字體。讓我們更新 webpack.config.js 來處理字體文件:
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
+ {
+ test: /\.(woff|woff2|eot|ttf|otf)$/i,
+ type: 'asset/resource',
+ },
],
},
};
在項目中添加一些字體文件:
project
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- my-font.woff
+ |- my-font.woff2
|- icon.png
|- style.css
|- index.js
|- /node_modules
配置好 loader 并將字體文件放在合適的位置后,你可以通過一個 @font-face 聲明將其混合。本地的 url(...) 指令會被 webpack 獲取處理,就像它處理圖片一樣:
src/style.css
+@font-face {
+ font-family: 'MyFont';
+ src: url('./my-font.woff2') format('woff2'),
+ url('./my-font.woff') format('woff');
+ font-weight: 600;
+ font-style: normal;
+}
+
.hello {
color: red;
+ font-family: 'MyFont';
background: url('./icon.png');
}
現(xiàn)在,讓我們重新構(gòu)建,然后看下 webpack 是否處理了我們的字體:
$ npm run build
...
[webpack-cli] Compilation finished
assets by status 9.88 KiB [cached] 1 asset
assets by info 33.2 KiB [immutable]
asset 55055dbfc7c6a83f60ba.woff 18.8 KiB [emitted] [immutable] [from: src/my-font.woff] (auxiliary name: main)
asset 8f717b802eaab4d7fb94.woff2 14.5 KiB [emitted] [immutable] [from: src/my-font.woff2] (auxiliary name: main)
asset bundle.js 73.7 KiB [emitted] [minimized] (name: main) 1 related asset
runtime modules 1.82 KiB 6 modules
orphan modules 326 bytes [orphan] 1 module
cacheable modules 541 KiB (javascript) 43.1 KiB (asset)
javascript modules 541 KiB
modules by path ./node_modules/ 539 KiB
modules by path ./node_modules/css-loader/dist/runtime/*.js 2.38 KiB 2 modules
./node_modules/lodash/lodash.js 530 KiB [built] [code generated]
./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js 6.67 KiB [built] [code generated]
modules by path ./src/ 1.98 KiB
./src/index.js + 1 modules 794 bytes [built] [code generated]
./node_modules/css-loader/dist/cjs.js!./src/style.css 1.21 KiB [built] [code generated]
asset modules 126 bytes (javascript) 43.1 KiB (asset)
./src/icon.png 42 bytes (javascript) 9.88 KiB (asset) [built] [code generated]
./src/my-font.woff2 42 bytes (javascript) 14.5 KiB (asset) [built] [code generated]
./src/my-font.woff 42 bytes (javascript) 18.8 KiB (asset) [built] [code generated]
webpack 5.4.0 compiled successfully in 2142 ms
重新打開 ?dist/index.html
? 看看我們的 Hello webpack 文本顯示是否換上了新的字體。如果一切順利,你應(yīng)該能看到變化。
此外,可以加載的有用資源還有數(shù)據(jù),如 JSON 文件,CSV、TSV 和 XML。類似于 NodeJS,JSON 支持實際上是內(nèi)置的,也就是說 import Data from './data.json' 默認將正常運行。要導(dǎo)入 CSV、TSV 和 XML,你可以使用 csv-loader 和 xml-loader。讓我們處理加載這三類文件:
npm install --save-dev csv-loader xml-loader
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
+ {
+ test: /\.(csv|tsv)$/i,
+ use: ['csv-loader'],
+ },
+ {
+ test: /\.xml$/i,
+ use: ['xml-loader'],
+ },
],
},
};
在項目中添加一些數(shù)據(jù)文件:
project
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- data.xml
+ |- data.csv
|- my-font.woff
|- my-font.woff2
|- icon.png
|- style.css
|- index.js
|- /node_modules
src/data.xml
<?xml version="1.0" encoding="UTF-8"?>
<note>
<to>Mary</to>
<from>John</from>
<heading>Reminder</heading>
<body>Call Cindy on Tuesday</body>
</note>
src/data.csv
to,from,heading,body
Mary,John,Reminder,Call Cindy on Tuesday
Zoe,Bill,Reminder,Buy orange juice
Autumn,Lindsey,Letter,I miss you
現(xiàn)在,你可以 import 這四種類型的數(shù)據(jù)(JSON, CSV, TSV, XML)中的任何一種,所導(dǎo)入的 Data 變量,將包含可直接使用的已解析 JSON:
src/index.js
import _ from 'lodash';
import './style.css';
import Icon from './icon.png';
+import Data from './data.xml';
+import Notes from './data.csv';
function component() {
const element = document.createElement('div');
// Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.classList.add('hello');
// Add the image to our existing div.
const myIcon = new Image();
myIcon.src = Icon;
element.appendChild(myIcon);
+ console.log(Data);
+ console.log(Notes);
+
return element;
}
document.body.appendChild(component());
重新執(zhí)行 npm run build 命令,然后打開 dist/index.html。查看開發(fā)者工具中的控制臺,你應(yīng)該能夠看到導(dǎo)入的數(shù)據(jù)會被打印出來!
// 沒有警告
import data from './data.json';
// 顯示警告,規(guī)范不允許這樣做。
import { foo } from './data.json';
通過使用 自定義 parser 替代特定的 webpack loader,可以將任何 toml、yaml 或 json5 文件作為 JSON 模塊導(dǎo)入。
假設(shè)你在 src 文件夾下有一個 data.toml、一個 data.yaml 以及一個 data.json5 文件:
src/data.toml
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
organization = "GitHub"
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
dob = 1979-05-27T07:32:00Z
src/data.yaml
title: YAML Example
owner:
name: Tom Preston-Werner
organization: GitHub
bio: |-
GitHub Cofounder & CEO
Likes tater tots and beer.
dob: 1979-05-27T07:32:00.000Z
src/data.json5
{
// comment
title: 'JSON5 Example',
owner: {
name: 'Tom Preston-Werner',
organization: 'GitHub',
bio: 'GitHub Cofounder & CEO\n\
Likes tater tots and beer.',
dob: '1979-05-27T07:32:00.000Z',
},
}
首先安裝 toml,yamljs 和 json5 的 packages:
npm install toml yamljs json5 --save-dev
并在你的 webpack 中配置它們:
webpack.config.js
const path = require('path');
+const toml = require('toml');
+const yaml = require('yamljs');
+const json5 = require('json5');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
{
test: /\.(csv|tsv)$/i,
use: ['csv-loader'],
},
{
test: /\.xml$/i,
use: ['xml-loader'],
},
+ {
+ test: /\.toml$/i,
+ type: 'json',
+ parser: {
+ parse: toml.parse,
+ },
+ },
+ {
+ test: /\.yaml$/i,
+ type: 'json',
+ parser: {
+ parse: yaml.parse,
+ },
+ },
+ {
+ test: /\.json5$/i,
+ type: 'json',
+ parser: {
+ parse: json5.parse,
+ },
+ },
],
},
};
src/index.js
import _ from 'lodash';
import './style.css';
import Icon from './icon.png';
import Data from './data.xml';
import Notes from './data.csv';
+import toml from './data.toml';
+import yaml from './data.yaml';
+import json from './data.json5';
+
+console.log(toml.title); // output `TOML Example`
+console.log(toml.owner.name); // output `Tom Preston-Werner`
+
+console.log(yaml.title); // output `YAML Example`
+console.log(yaml.owner.name); // output `Tom Preston-Werner`
+
+console.log(json.title); // output `JSON5 Example`
+console.log(json.owner.name); // output `Tom Preston-Werner`
function component() {
const element = document.createElement('div');
// Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
element.classList.add('hello');
// Add the image to our existing div.
const myIcon = new Image();
myIcon.src = Icon;
element.appendChild(myIcon);
console.log(Data);
console.log(Notes);
return element;
}
document.body.appendChild(component());
重新執(zhí)行 npm run build 命令,然后打開 dist/index.html。你應(yīng)該能夠看到導(dǎo)入的數(shù)據(jù)會被打印到控制臺上!
上述所有內(nèi)容中最出色之處在于,以這種方式加載資源,你可以以更直觀的方式將模塊和資源組合在一起。無需依賴于含有全部資源的 /assets 目錄,而是將資源與代碼組合在一起使用。例如,類似這樣的結(jié)構(gòu)會非常有用:
- |- /assets
+ |– /components
+ | |– /my-component
+ | | |– index.jsx
+ | | |– index.css
+ | | |– icon.svg
+ | | |– img.png
這種配置方式會使你的代碼更具備可移植性,因為現(xiàn)有的集中放置的方式會讓所有資源緊密耦合起來。假如你想在另一個項目中使用 /my-component,只需將其復(fù)制或移動到 /components 目錄下。只要你已經(jīng)安裝過全部 外部依賴 ,并且 已經(jīng)在配置中定義過相同的 loader ,那么項目應(yīng)該能夠良好運行。
但是,假如你只能被局限在舊有開發(fā)方式,或者你有一些在多個組件(視圖、模板、模塊等)之間共享的資源。你仍然可以將這些資源存儲在一個基本目錄(base directory)中,甚至配合使用 alias 來使它們更方便 import 導(dǎo)入。
對于下篇指南,我們無需使用本指南中所有用到的資源,因此我們會進行一些清理工作,以便為下篇指南 管理輸出 做好準備:
project
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
- |- data.csv
- |- data.json5
- |- data.toml
- |- data.xml
- |- data.yaml
- |- icon.png
- |- my-font.woff
- |- my-font.woff2
- |- style.css
|- index.js
|- /node_modules
webpack.config.js
const path = require('path');
-const toml = require('toml');
-const yaml = require('yamljs');
-const json5 = require('json5');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
- module: {
- rules: [
- {
- test: /\.css$/i,
- use: ['style-loader', 'css-loader'],
- },
- {
- test: /\.(png|svg|jpg|jpeg|gif)$/i,
- type: 'asset/resource',
- },
- {
- test: /\.(woff|woff2|eot|ttf|otf)$/i,
- type: 'asset/resource',
- },
- {
- test: /\.(csv|tsv)$/i,
- use: ['csv-loader'],
- },
- {
- test: /\.xml$/i,
- use: ['xml-loader'],
- },
- {
- test: /\.toml$/i,
- type: 'json',
- parser: {
- parse: toml.parse,
- },
- },
- {
- test: /\.yaml$/i,
- type: 'json',
- parser: {
- parse: yaml.parse,
- },
- },
- {
- test: /\.json5$/i,
- type: 'json',
- parser: {
- parse: json5.parse,
- },
- },
- ],
- },
};
src/index.js
import _ from 'lodash';
-import './style.css';
-import Icon from './icon.png';
-import Data from './data.xml';
-import Notes from './data.csv';
-import toml from './data.toml';
-import yaml from './data.yaml';
-import json from './data.json5';
-
-console.log(toml.title); // output `TOML Example`
-console.log(toml.owner.name); // output `Tom Preston-Werner`
-
-console.log(yaml.title); // output `YAML Example`
-console.log(yaml.owner.name); // output `Tom Preston-Werner`
-
-console.log(json.title); // `JSON5 Example`
-console.log(json.owner.name); // output `Tom Preston-Werner`
function component() {
const element = document.createElement('div');
- // lodash,現(xiàn)在通過 script 標簽導(dǎo)入
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
- element.classList.add('hello');
-
- // Add the image to our existing div.
- const myIcon = new Image();
- myIcon.src = Icon;
-
- element.appendChild(myIcon);
-
- console.log(Data);
- console.log(Notes);
return element;
}
document.body.appendChild(component());
然后移除之前添加的那些依賴項:
npm uninstall css-loader csv-loader json5 style-loader toml xml-loader yamljs
我們繼續(xù)移步到 管理輸出
更多建議: