Webpack 管理资源_w3cschool (2023)

如果你是从开始一直在沿用指南的示例,现在会有一个小项目,显示​"Hello webpack"​。现在我们尝试混合一些其他资源,比如 images,看看 webpack 如何处理。

在 webpack 出现之前,前端开发人员会使用grunt和gulp等工具来处理资源,并将它们从/src文件夹移动到/dist或/build目录中。JavaScript 模块也遵循同样方式,但是,像 webpack 这样的工具,将动态打包所有依赖(创建所谓的依赖图(dependency graph))。这是极好的创举,因为现在每个模块都可以明确表述它自身的依赖,可以避免打包未使用的模块。

webpack 最出色的功能之一就是,除了引入 JavaScript,还可以通过 loader 或内置的Asset Modules引入任何其他类型的文件。也就是说,以上列出的那些 JavaScript 的优点(例如显式依赖),同样可以用来构建 web 站点或 web 应用程序中的所有非 JavaScript 内容。让我们从 CSS 开始起步,或许你可能已经熟悉了下面这些设置。

设置

在开始之前,让我们对项目做一个小的修改:

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'), }, };

加载 CSS

为了在 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 可以链式调用。链中的每个 loader 都将对资源进行转换。链会逆序执行。第一个 loader 将其结果(被转换后的资源)传递给下一个 loader,依此类推。最后,webpack 期望链中的最后的 loader 返回 JavaScript。

应保证 loader 的先后顺序:'style-loader'在前,而'css-loader'在后。如果不遵守此约定,webpack 可能会抛出错误。

这使你可以在依赖于此样式的 js 文件中import './style.css'。现在,在此模块执行过程中,含有 CSS 字符串的<style>标签,将被插入到 html 文件的<head>中。

我们尝试一下,通过在项目中添加一个新的style.css文件,并将其 import 到我们的index.js中:

(Video) Research Talk: Secrets, Lies, and Account Recovery: Lessons from the Use of Personal Knowledge...

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());

现在运行 build 命令:

$ npm run build...[webpack-cli] Compilation finishedasset bundle.js 72.6 KiB [emitted] [minimized] (name: main) 1 related assetruntime modules 1000 bytes 5 modulesorphan modules 326 bytes [orphan] 1 modulecacheable 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,你应该看到Hello webpack现在的样式是红色。要查看 webpack 做了什么,请检查页面(不要查看页面源代码,它不会显示结果,因为<style>标签是由 JavaScript 动态创建的),并查看页面的 head 标签。它应该包含 style 块元素,也就是我们在index.js中 import 的 css 文件中的样式。

注意,在多数情况下,你也可以进行压缩 CSS,以便在生产环境中节省加载时间。最重要的是,现有的 loader 可以支持任何你可以想到的 CSS 风格 -postcss,sass和less等。

加载 images 图像

假如,现在我们正在下载 CSS,但是像 background 和 icon 这样的图像,要如何处理呢?在 webpack 5 中,可以使用内置的Asset Modules,我们可以轻松地将这些内容混入我们的系统中:

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',+ }, ], }, };

现在,在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');+ // 将图像添加到我们已经存在的 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'); }

重新构建并再次打开index.html文件:

$ npm run build...[webpack-cli] Compilation finishedassets by status 9.88 KiB [cached] 1 assetasset bundle.js 73.4 KiB [emitted] [minimized] (name: main) 1 related assetruntime modules 1.82 KiB 6 modulesorphan modules 326 bytes [orphan] 1 modulecacheable 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

如果一切顺利,你现在应该看到你的 icon 图标成为了重复的背景图,以及Hello webpack文本旁边的img元素。如果检查此元素,你将看到实际的文件名已更改为29822eaa871e8eadeaa4.png。这意味着 webpack 在src文件夹中找到我们的文件,并对其进行了处理!

加载 fonts 字体

那么,像字体这样的其他资源如何处理呢?使用 Asset Modules 可以接收并加载任何文件,然后将其输出到构建目录。这就是说,我们可以将它们用于任何类型的文件,也包括字体。让我们更新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'); }

现在,让我们重新构建,然后看下 webpack 是否处理了我们的字体:

$ npm run build...[webpack-cli] Compilation finishedassets by status 9.88 KiB [cached] 1 assetassets 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 assetruntime modules 1.82 KiB 6 modulesorphan modules 326 bytes [orphan] 1 modulecacheable 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文本显示是否换上了新的字体。如果一切顺利,你应该能看到变化。

加载数据

此外,可以加载的有用资源还有数据,如 JSON 文件,CSV、TSV 和 XML。类似于 NodeJS,JSON 支持实际上是内置的,也就是说import Data from './data.json'默认将正常运行。要导入 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'],+ }, ], }, };

在项目中添加一些数据文件:

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,bodyMary,John,Reminder,Call Cindy on TuesdayZoe,Bill,Reminder,Buy orange juiceAutumn,Lindsey,Letter,I miss you

现在,你可以import这四种类型的数据(JSON, CSV, TSV, XML)中的任何一种,所导入的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());

重新执行npm run build命令,然后打开dist/index.html。查看开发者工具中的控制台,你应该能够看到导入的数据会被打印出来!

// 没有警告import data from './data.json';// 显示警告,规范不允许这样做。import { foo } from './data.json';

自定义 JSON 模块 parser

通过使用自定义 parser替代特定的 webpack loader,可以将任何toml、yaml或json5文件作为 JSON 模块导入。

假设你在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 Exampleowner: 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());

重新执行npm run build命令,然后打开dist/index.html。你应该能够看到导入的数据会被打印到控制台上!

全局资源

上述所有内容中最出色之处在于,以这种方式加载资源,你可以以更直观的方式将模块和资源组合在一起。无需依赖于含有全部资源的/assets目录,而是将资源与代码组合在一起使用。例如,类似这样的结构会非常有用:

- |- /assets+ |– /components+ | |– /my-component+ | | |– index.jsx+ | | |– index.css+ | | |– icon.svg+ | | |– img.png

这种配置方式会使你的代码更具备可移植性,因为现有的集中放置的方式会让所有资源紧密耦合起来。假如你想在另一个项目中使用/my-component,只需将其复制或移动到/components目录下。只要你已经安装过全部外部依赖,并且已经在配置中定义过相同的 loader,那么项目应该能够良好运行。

但是,假如你只能被局限在旧有开发方式,或者你有一些在多个组件(视图、模板、模块等)之间共享的资源。你仍然可以将这些资源存储在一个基本目录(base directory)中,甚至配合使用alias来使它们更方便import 导入。

回退处理

对于下篇指南,我们无需使用本指南中所有用到的资源,因此我们会进行一些清理工作,以便为下篇指南管理输出做好准备:

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,现在通过 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());

然后移除之前添加的那些依赖项:

npm uninstall css-loader csv-loader json5 style-loader toml xml-loader yamljs

下篇指南

我们继续移步到管理输出

延伸阅读

References

Top Articles
Latest Posts
Article information

Author: Rueben Jacobs

Last Updated: 09/10/2023

Views: 6545

Rating: 4.7 / 5 (57 voted)

Reviews: 80% of readers found this page helpful

Author information

Name: Rueben Jacobs

Birthday: 1999-03-14

Address: 951 Caterina Walk, Schambergerside, CA 67667-0896

Phone: +6881806848632

Job: Internal Education Planner

Hobby: Candle making, Cabaret, Poi, Gambling, Rock climbing, Wood carving, Computer programming

Introduction: My name is Rueben Jacobs, I am a cooperative, beautiful, kind, comfortable, glamorous, open, magnificent person who loves writing and wants to share my knowledge and understanding with you.