如何配置 Webpack 📦
Webpack 及公司
配置
生产
热力重装上阵 🔥
一份礼物🎁
这篇文章摘自我的博客,记得去看看博客上的最新内容哦😉
近年来,打包资源和代码已成为一种非常普遍的做法。它允许创建可移植的包,这些包不仅易于重用和传输,而且有利于快速交付,从而提升用户体验(性能)。自从ES6 模块规范发布以来,打包技术得到了极大的推动——ES6 模块规范为 JavaScript 代码提供了一种标准化的模块化方式。虽然浏览器并没有迅速采用 ES6 模块,但它在开发者中迅速流行起来,取代了其他一些性能较差的系统,例如AMD和CommonJS。随着模块化程度的提高,对打包工具的需求也随之增长。Webpack凭借其强大的功能和可扩展性迅速占据了主导地位。但是,由于可用的插件、扩展程序、加载器等数量众多,很难为所有不同需求的用户提供一个合适的解决方案或更具体的配置。这就是为什么 Webpack 配置对某些人来说可能有点复杂和繁琐。而这正是本教程存在的意义。在这里,我将尝试向您介绍创建 Webpack 配置的基础知识。我强烈建议您从头到尾阅读本文,因为最后会有惊喜等着您。 😂 废话不多说,我们先来看看 Webpack 本身。
Webpack 及公司
Webpack 被宣传为现代 JavaScript 应用的静态模块打包工具。它是一款流行的 Web 应用打包工具。Webpack 支持 ES6 模块、CommonJS、AMD 和@import,几乎可以处理日常 Web 应用使用的所有资源。它拥有庞大的社区,以及丰富的插件和加载器生态系统,支持各种不同的资源。话虽如此,它并非唯一的选择。市面上还有许多其他高质量的打包工具,例如Rollup.js。它也是一款打包工具,但更侧重于打包库和其他 JS 工具,而非 Web 应用。此外,还有一个名为Parcel.js的新兴工具。对于那些不喜欢配置的用户来说,Parcel.js 可能是一个完美的解决方案。它为许多不同的资源和格式提供了开箱即用的支持。以上三款是我最喜欢的,当然还有更多其他工具,但我不会一一列举。 🙃 现在,既然您已经了解了可能的替代方案,下面我们将一步一步地介绍如何配置您的 Webpack。
配置
更具体地说,让我们来定义一下配置的具体功能。以下配置应该能够满足我们项目的所有需求。在这个例子中,我们将开发一个基于React 、使用TypeScript编写的简单单页应用 (SPA)和渐进式 Web 应用 (PWA)。为了在定义样式时获得更好的用户体验,我们还将使用SCSS(完全不支持 CSS)。让我们开始吧!😁
来看一下Webpack配置文件的框架。
const path = require('path');
module.exports = {
entry: './src/index.tsx',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js'
},
resolve: {
extensions: []
},
module: {
rules: []
},
plugins: []
}
这就是我们配置的基本结构。它位于webpack.config.js文件中,该文件使用 CommonJS 语法导出配置对象。在配置对象内部,我们有一个字段,它entry指向打包器应该从哪个文件开始执行操作。然后是output包含生成包的正确名称的对象path。filename名称使用[name]占位符,表示输出名称应该与我们的模块名称(默认为main)相对应。部分本质上是一个文件扩展Resolve.extensions名数组,Webpack 需要读取和处理这些扩展名。接下来是可以说是整个配置中最重要的部分之一。在这里,我们定义应该处理特定文件的加载器。最后是所有 Webpack插件的存放位置。现在,让我们填充一些内容吧。module.rulesplugins
// ...
resolve: {
extensions: [ '.tsx', '.ts', '.js', '.jsx' ]
},
module: {
rules: [{
test: /\.tsx?$/,
use: ['babel-loader', 'ts-loader'],
exclude: /node_modules/
}]
},
// ...
好了……这就是处理 TypeScript 所需的大部分内容!让我们仔细看看是怎么回事。在 `<filename>` 中extensions,我们添加了所有将来可能用到的扩展名。在 `<filename>` 中rules,我们提供了第一条规则。这是一个包含三个属性的对象。`<filename>`test是一个正则表达式,它匹配所有以`.ts`或`.tsx`扩展名结尾的文件,并使用 `<filename> ` 字段中提供的ts-loader和babel-loader来处理它们use。使用两个处理器使我们能够使用 Babel 处理 TS 编译器输出的代码。请记住,加载器是从数组中最后一个到第一个依次使用的。最后,我们排除了`node_modules`,因为谁会需要处理这些文件并拖慢系统呢?😂 值得一提的是,你不需要以任何方式引入 ts-loader,只需安装它即可。说到安装,我可能忘记提及 Webpack 的安装,所以让我们用一个简单的命令来解决所有这些问题:
npm install --save-dev webpack webpack-cli typescript @babel/core babel-loader ts-loader
现在让我们添加对SCSS的支持!
// ...
{
test: /\.scss$/,
use: [
'style-loader',
{ loader: 'css-loader', options: { importLoaders: 1 } },
'sass-loader',
],
},
// ...
这里我们需要使用多达 3 个加载器,所以让我们先安装它们,别忘了安装node-sass来处理 SCSS!
npm install --save-dev node-sass style-loader css-loader sass-loader
通常情况下,我们在这里使用node-sass 库通过sass-loader处理 SCSS 文件,使用css-loader转换所有@import和URL ,最后使用style-loader插入样式。css -loader 的选项用于指定在 CSS 加载器之前使用的加载器数量。在我们的示例中,只有一个加载器——sass-loader。您可以查看为加载器提供其他选项的语法。importLoaders
最后,让我们来点更高级的功能,添加对打包图片(即静态文件)的支持!
npm install --save-dev file-loader
// ...
{
test: /\.(jpe?g|png|gif|svg)$/i,
loader: 'file-loader'
},
// ...
使用文件加载器,Webpack 会将每个匹配的导入处理成正确的 URL。请注意,在定义单个加载器时,可以使用此加载器字段代替。
另外,别忘了其他配置文件,例如TypeScript的tsconfig.json 文件……
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "commonjs",
"target": "es5",
"jsx": "react",
"lib": ["es5", "es6", "dom"]
},
"include": [
"./src/**/*"
],
}
以及Babel的.babelrc文件:
npm install --save-dev @babel/preset-env @babel/preset-react @babel/preset-typescript
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"env": {
"development": {
"presets": ["@babel/preset-typescript"]
}
}
}
这些内容与本文主题略有偏离,我就不赘述了。如果您想了解更多信息,可以点击链接查看相关页面——本文中列出的所有工具都附有非常棒的文档。📙⚡
现在我们来谈谈插件。
npm install --save-dev clean-webpack-plugin html-webpack-plugin
workbox-webpack-plugin webpack-pwa-manifest
const CleanPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
const WebpackPwaManifest = require('webpack-pwa-manifest');
// ...
plugins: [
new CleanPlugin(["dist"]),
new HtmlWebpackPlugin({
filename: 'index.html',
title: 'Webpack Config',
template: './src/index.html'
}),
new WebpackPwaManifest({
name: 'Webpack Config',
short_name: 'WpConfig',
description: 'Example Webpack Config',
background_color: '#ffffff'
}),
new WorkboxPlugin.GenerateSW({
swDest: 'sw.js',
clientsClaim: true,
skipWaiting: true,
})
],
// ...
上面的代码片段中竟然出现了 4 个插件!每个插件都有其特定的用途。Clean -webpack-plugin负责清理输出目录——一项简单的任务。Html -webpack-plugin则使用提供的数据和模板文件来配置我们的 HTML 文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
顺便一提,这是我们的模板文件,标题直接取自插件的配置对象。最后,workbox-webpack-plugin和webpack-pwa-manifest分别提供了离线 Service Worker和应用清单的 PWA功能。其中一些插件提供了大量的自定义选项,如果您计划使用它们,请访问它们的项目页面了解更多信息。
生产
至此,我们可以说我们的配置已经基本可以正常运行了。但这还不够。使用 Webpack,您可以针对不同的使用场景创建多个配置。最常见的例子是为生产环境和开发环境分别创建两个配置,因为每个环境都有其特定的需求。让我们把 webpack.config.js 文件拆分成三个部分。
Webpack.common.js将包含开发环境和生产环境相同的配置。
const CleanPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
const WebpackPwaManifest = require('webpack-pwa-manifest');
const path = require("path");
module.exports = {
entry: "./src/index.tsx",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js"
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".jsx"]
},
module: {
rules: [
{
test: /\.scss$/,
use: [
"style-loader",
{ loader: "css-loader", options: { importLoaders: 1 } },
"sass-loader"
]
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
loader: "file-loader"
}
]
},
plugins: [
new CleanPlugin(["dist"]),
new HtmlWebpackPlugin({
filename: 'index.html',
title: 'Webpack Config',
template: './src/index.html',
}),
new WebpackPwaManifest({
name: 'Webpack Config',
short_name: 'WpConfig',
description: 'Example Webpack Config',
background_color: '#ffffff'
}),
new WorkboxPlugin.GenerateSW({
swDest: 'sw.js',
clientsClaim: true,
skipWaiting: true,
})
]
};
现在,我们来创建webpack.prod.js配置。我们需要将其与通用配置合并。为此,我们可以使用webpack-merge——一个专门用于此目的的工具。所以,让我们安装它以及稍后会用到的另外两个插件。
npm install --save-dev webpack-merge uglifyjs-webpack-plugin hard-source-webpack-plugin
const merge = require('webpack-merge');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
module: {
rules: [{
test: /\.tsx?$/,
use: ["babel-loader", "ts-loader"],
exclude: /node_modules/
}]
},
optimization: {
minimizer: [new UglifyJsPlugin({
sourceMap: true
})],
},
});
这里我们可以看到两个新的属性——`<environment>`mode和 ` <environment> devtool`。` <environment> Mode` 表示我们当前的开发环境,可以是“production”、“development”或“none”。这使得一些工具能够针对所选环境应用相应的优化。` <environment>` 属性指的是生成源映射Devtool的方式。Webpack 内置了许多针对此属性的选项。此外,还有许多插件提供了额外的功能。但目前来说,从内容文件生成源映射的`<source-map>`选项已经足够了。接下来是传统的.ts文件加载器。之后是配置中一些新的、不言自明的字段。`<plugin>`允许我们指定一个用于最小化文件的插件,这在面向生产环境时非常有用。这里我将使用uglifyjs-webpack-plugin,它经过了充分的实战检验,性能良好,输出稳定。不要忘记为该插件设置 `<plugin>` 选项,否则源映射将无法生成!现在,让我们来看看开发环境的配置文件——`webpack.dev.js`。optimization.minimizersourceMap
const merge = require('webpack-merge');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
module: {
rules: [{
test: /\.tsx?$/,
loader: "babel-loader",
exclude: /node_modules/
}]
},
plugins: [
new HardSourceWebpackPlugin()
]
});
在开发阶段,我们只关注速度。此时无需进行任何优化。我们只希望代码能够快速打包。源代码映射也是如此,这次我们使用了速度更快但优化程度不高的“eval-source-map”选项。然后,在定义 TypeScript 加载器时,我们只使用一个加载器——babel-loader。这样做只会编译 .ts 文件而不进行类型检查,从而极大地提升了打包速度。这就是为什么我之前在.babelrc文件中定义了@babel /preset-typescript,以便在开发阶段使用。最后,我们使用了hard-source-webpack-plugin,它提供了一种简便的文件缓存方式,因此第二次打包速度会更快! 好了……就是这样!我们已经准备好了适用于特定环境的配置!
热力重装上阵 🔥
我们已经有了不错的配置,但是谁需要一个没有热重载的快速开发配置呢!?没错——它正在升温!🔥 所以,我们先把生产配置放在一边,来看看如何实现这个强大的功能吧?使用webpack-dev-server非常简单!你可以用以下命令安装:
npm install --save-dev webpack-dev-server
配置方面,请将devServerconfig 对象添加到webpack.dev.js文件中。
// ...
devServer: {
contentBase: path.join(__dirname, 'dist'),
compress: true,
port: 9000
}
// ...
这里我们提供了一些基本选项,例如端口、服务器目录以及是否进行压缩。就这些!最后,让我们在package.json 文件中添加两个脚本,以便于后续开发。
"scripts": {
"start": "webpack-dev-server --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
}
通过使用--config选项,我们可以提供特定于环境的 Webpack 配置的位置。
好了,这就是你的专属 Webpack 配置!它支持 TS/TSX、SCSS,优化了生产和开发环境的设置,还支持 HMR!顺便提一下,我们的 HMR 目前运行良好,但在 React 相关的功能方面还有改进空间。例如,如果你想在页面重新加载后保留组件的状态,可以使用react-hot-loader,并参考这篇优秀的指南,同时使用你之前创建的配置。
一份礼物🎁
所以,正如你通过本教程所看到的,创建 Webpack 配置并不难。这只是一个比较耗时的过程,而且可能需要时不时地查阅一些资料。但对某些人来说,这本身也是一种乐趣。如果你属于另一类人,我这里有个特别的东西要推荐给你。我创建了一个简单的命令行工具,可以帮你生成Webpack 配置的基本样板。有了它,你就不必一遍又一遍地重复设置相同的内容了。这个工具叫做webpack-suit-up ,你可以从NPM下载。所以,如果你感兴趣的话,不妨试试看。
希望这篇教程能帮助你完成 Webpack 的配置。想了解更多关于 Webpack 的信息,可以访问其官方网站。不过,正如我开头所说,还有很多其他优秀的工具,它们甚至不需要配置。有些工具甚至基于 Webpack,可以自动进行配置。此外,Webpack 本身从v4 版本开始就不需要配置了,但在大多数情况下,配置仍然是必要的。或许你想看看其他优秀的Web 打包工具的完整列表?或者关于Rollup.js配置的指南?请在下方评论区留言。分享这篇文章,让更多人能够看到它。也欢迎在 Twitter或Facebook 主页上关注我,获取更多最新内容。😁
文章来源:https://dev.to/areknawo/how-to-setup-webpack-config--1ooi