理解现代 Web 技术栈:Webpack - DevServer、React 和 Typescript
(本教程使用webpack v5编写,但一般概念适用于任何版本)
目录
回顾
本教程假定您已经熟悉本教程中概述的 webpack 基础知识:
我们演示了如何从更大的库中导入单个文件和函数,而无需携带我们不使用的代码。
本教程向您介绍源映射、webpack 的 DevServer,以及如何将 React 和 Typescript 引入 webpack 项目。
因此,在深入研究之前,让我们先设置好我们的工作环境。
初始化项目
创建以下目录结构:
root
| webpack.config.js
└───src
│ │ script.js
| | index.html
src/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Webpack Example</title>
</head>
<body></body>
</html>
src/script.js
const element = document.createElement("h1");
element.innerHTML = "Welcome";
document.body.appendChild(element);
webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
module.exports = {
mode: "none",
entry: "./src/script.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
clean: true,
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/index.html",
}),
],
};
现在安装您的依赖项,并运行 webpack 命令来生成您的dist
文件夹。
npm init -y
npm install webpack webpack-cli html-webpack-plugin --save dev
npx webpack
如果您可以成功提供您的dist
文件夹并看到欢迎消息,那么您就可以开始本教程了。
源映射
源映射是一种文件类型(或文件内的注释),它在转换发生之前向其他工具提供有关源代码来源的信息。
例如,即使我们只对一行文件运行 webpack .js
,打包后的版本也会包含 webpack 注入的少量额外代码。这意味着你之前编写的代码line 1
实际上可能会出现在你的打包line 10
文件line 10000
中。
当您的浏览器根据其运行的包在不同的行上而不是源代码中的原始行上报告错误时,这会使您很难找到错误。
Source Map 帮我们解决了这个问题。我们来演示一下。
首先,我们将向文件中添加一个错误script.js
:
src/script.js
throw Error("Something happened!");
const element = document.createElement("h1");
element.innerHTML = "Welcome";
document.body.appendChild(element);
运行npx webpack
并查看我们的“错误”程序。访问dist
目录并查看浏览器的开发控制台。
请注意,webpack 报告第 3 行的错误main.js
(您的行号可能不同)。
throw Error
从技术上来说这是正确的,因为这就是 webpack在输出文件上放置代码的地方main.js
。
要将其配置为报告正确的行号,请将以下行添加到您的 webpack 配置中:
webpack.config.js
...
module.exports = {
...
devtool: "inline-source-map",
...
};
现在我们运行npx webpack
并查看控制台:
现在错误被正确地报告在我们的代码中实际所在的位置!
Webpack 开发服务器
webpack 的DevServer是一个很棒的工具,它可以通过即时重新加载和即时反馈您的更改来加快您的开发时间。
DevServer 会自动监控文件的更改,并在您保存时自动更新 bundle。使用实时服务器时,bundle 驻留在内存 (RAM) 中,而不是dist
文件夹中,因此更新速度更快。
让我们配置 DevServer 并尝试一下。首先创建一个可以测试的 JS 文件。删除以下throw Error
行并更新script.js
为以下内容:
src/script.js
const element = document.createElement("h1");
element.innerHTML = "Welcome";
document.body.appendChild(element);
接下来我们devServer
向 webpack 配置添加一个属性:
webpack.config.js
...
module.exports = {
...
devServer: {
static: './dist',
},
...
};
然后我们安装DevServer:
npm install --save-dev webpack-dev-server
最后我们运行命令:
npx webpack serve --open
默认情况下,webpack 将在端口 8080 上为您的应用程序提供服务。该--open
标志将自动在您的浏览器中打开该页面。
尝试将Welcome
字符串更改为其他任何内容script.js
,您会看到页面立即更新。这为您创建了一个极其流畅的开发环境,并提供即时反馈。
接下来我们来看看如何引入 React 和 JSX。
React 和 JSX
(本教程使用React v17,但任何版本的概念都是相同的)
本教程的这一部分假设你已经熟悉 React 和 JSX 的基础知识。如果你需要复习一下,React 文档是最好的起点。
本节的目标是向您展示如何配置 webpack,将 JSX 代码转换为常规的 JavaScript 代码。webpack 依赖于Babel 的加载器来实现这一点。如果您不熟悉 Babel 的工作原理,本教程将涵盖您需要了解的所有内容:
我们的第一步是安装 React 并更新script.js
文件以创建 React 组件。首先使用以下命令安装 React:
npm install react react-dom --save-dev
接下来,我们更新script.js
文件以使用 JSX。由于 JSX 是基于 JavaScript 的一种特殊语法,本身并不是有效的 JavaScript,因此它需要不同的文件扩展名。
使用以下代码在同一位置删除script.js
并创建:script.jsx
src/script.jsx
import ReactDOM from 'react-dom';
const Welcome = () => {
return <h1>Welcome</h1>;
};
const mountingNode = document.querySelector("#root");
ReactDOM.render(<Welcome />, mountingNode);
如果你熟悉 React,你会看到这将我们的组件挂载到带有 的根节点上id="root"
。更新我们的组件index.html
以包含它:
src/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Webpack Example</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
我们还需要安装为我们运行转换的软件包:
npm install babel-loader @babel/core @babel/preset-env @babel/preset-react --save-dev
在上面的列表中,您可以看到我们在 Babel 教程中了解的所有 Babel 标准部分。
唯一的新内容babel-loader
是 webpack 的加载器,它将 babel 作为捆绑过程的一部分运行。
现在让我们更新我们的 webpack 配置以使用 babel 加载器:
webpack.config.js
...
module.exports = {
entry: "./src/script.jsx",
...
module: {
rules: [
{
test: /\.m?jsx$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
["@babel/preset-react", { runtime: "automatic" }],
],
},
},
},
],
},
optimization: {
nodeEnv: 'development',
},
}
这里有几件事需要解开,让我们一次解开一件:
-
条目:已更新为使用
JSX
扩展名,而非JS
。不要错过这个小改动,因为它是常见的错误来源。 -
rules:我们有一个正则表达式,用于确保 Babel 加载器能够在项目中的任何 JSX 文件上运行。如果我们使用的是 React v17 或更高版本,则需要 ,
{ runtime: "automatic" }
它告诉 Babel 将 JSX 运行时包含在我们的 bundle 中,这样我们就不需要import React
在 JSX 代码中自己添加了。 -
优化:这是另一个主要的错误来源。React 需要像 Webpack 一样
process.env.NODE_ENV
进行定义(设置为development或production )。我们暂时将其设置为developmentprocess is undefined
。如果您看到错误,很可能意味着您忘记了这个值。
现在运行npx webpack serve --open
命令(或者,npx webpack
如果您想输出到磁盘并自行运行)。如果一切顺利,您将再次看到欢迎页面。
恭喜!现在您已将 React 和 JSX 支持实现到 webpack 配置中。
我们的最后一部分将展示如何实现 Typescript。
打字稿
(本教程使用Typescript v4.4,但任何版本的概念都是相同的)
本教程的这一部分假设您已经熟悉 Typescript 的基础知识。如果您需要复习一下,Typescript 文档是最好的起点。
对于新手来说,理解如何将 Typescript 集成到 webpack 配置中通常很棘手且令人困惑,特别是当您还使用 JSX 和 React 时。
这就是像Create React App这样的工具如此受欢迎的原因,因为它们会帮你处理所有配置。但当你需要配置某些东西时,这可能会带来麻烦,这就是本教程存在的原因。
Typescript、webpack 和 babel 提供的功能有时会重叠。在本教程中,我们将逐一介绍它们,旨在了解它们的作用,以便您更好地了解如何以最佳方式自行管理它们。
我们首先安装 Typescript:
npm install typescript --save-dev
安装 Typescript 后,我们需要在项目中初始化它。这将自动创建一个tsconfig.json
文件,我们可以在其中配置类似于 webpack 配置的 Typescript 选项:
npx tsc --init
(请小心使用此语法,注意我们正在使用npx
包运行器,就像我们使用 webpack 一样。tsc
是 Typescript 的命令行程序的名称)
接下来,让我们在script.jsx
文件中添加一些 Typescript 语法。与过渡到 JSX 类似,Typescript 也需要自己的格式来表明文件正在使用 Typescript。
Typescript 文件的基本扩展名是.ts
,但是如果您使用 JSX ,则扩展名是.tsx
。让我们更新文件扩展名并添加一些代码:
script.tsx
import ReactDOM from 'react-dom';
const x: number = 5; // <-- NEW
const Welcome = () => {
return <h1>Welcome</h1>;
};
const mountingNode = document.querySelector("#root");
ReactDOM.render(<Welcome />, mountingNode);
如果您使用 VS Code 之类的 IDE,您可能会注意到一些错误被高亮显示。第一个错误是 ReactDOM 不包含类型。第二个错误会高亮显示您的 JSX 语法。这是因为 Typescript 默认未配置为处理 JSX,因此我们必须进行相应的配置。
我们将首先提供 ReactDOM 的类型信息。
未自动与其类型捆绑在一起的库和包通常会在Definitely Typed中提供类型包。
因此,要从 ReactDOM 的库中导入这些类型,我们运行以下命令:
npm install @types/react-dom --save-dev
这将修复 ReactDOM 中缺失的类型。接下来,让我们配置 Typescript 来处理 JSX。
理解 tsconfig 中的所有选项tsconfig.json
超出了本教程的范围,但由于他们在生成的文件中实现了自动生成注释的系统,我们现在可以大致了解了。如果这还不够,您可以在这里找到完整的 tsconfig 文档。
我们需要在文件中设置该jsx
属性tsconfig
。默认值为preserve
。这意味着 Typescript 将完全忽略 JSX。它将输出一个.jsx
文件而不是一个.js
文件,并让你使用其他工具来删除 JSX。
让我们测试一下。更新你的tsconfig
文件:
tsconfig.json
{
"compilerOptions": {
...
"jsx": "preserve",
...
}
}
然后运行npx tsc
。您将在src
目录中看到一个script.jsx
文件生成。这就是您的script.tsx
文件,其中包含已检查和已删除的类型。太棒了!我们进展顺利。
您可以随意尝试不同的 JSX 设置jsx
。例如,react
它将自动移除 JSX 代码并输出.js
文件,但会默认您导入的是 React。 设置为 时,react-jsx
将使用 React v17 中新的 JSX 运行时,因此您无需导入。
据我所知,使用哪种工具来转换 JSX 代码差别不大。preserve
由于我们在上一节已经配置了 Babel 来处理 JSX,所以我们就不讨论 Babel 了。
如果有充分的理由选择其中一个,请随时在评论中告诉我们!
此时,您可以删除测试时生成的任何文件。我们只需要script.js
源文件。script.jsx
script.tsx
我们有两种选择可以将 Typescript 阶段添加到我们的 webpack 流程中。
-
我们可以使用
ts-loader
它在打包过程中执行类型检查。如果有任何类型错误,构建将取消并报告错误。 -
我们可以使用 Babel 简单地删除类型信息。这假设我们在打包之前使用了另一个工具进行类型检查。它不会在出现错误时取消构建过程。
我们将研究如何配置这两个选项并选择最适合您的选项。
选项 1:ts-loader
这个选项将执行类型检查和类型删除。我们需要安装加载器:
npm install ts-loader --save-dev
然后我们更新 webpack 配置以包含以下行:
webpack.config.js
...
module.exports = {
entry: "./src/script.tsx", // <-- NEW
...
module: {
rules: [
{
test: /\.m?jsx$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
["@babel/preset-react", { runtime: "automatic" }],
],
},
},
},
{ test: /\.tsx?$/, use: "ts-loader" }, // <-- NEW
],
},
...
};
这里有两行新代码。第一行是对要点的更新entry
。我们现在需要定位到我们的script.tsx
文件,而不是.jsx
……
第二个是ts-loader
。加载器以链式执行,并按相反的顺序执行。因此,我们需要将 Typescript 加载器放在末尾,以便它将转换后的 JSX 文件沿着链传递到babel-loader
。
运行npx webpack serve --open
并查看结果。如果一切顺利,您将看到正在进行类型检查的欢迎页面。
要查看类型检查的实际效果,请尝试引入错误:
script.tsx
...
const x: number = 'this is a string';
...
如果在启用监视的情况下运行 DevServer,您将立即看到浏览器和终端中出现错误:
Type 'string' is not assignable to type 'number'
选项 2:babel/preset-typescript
第二种方案假设我们在 webpack 构建过程之前运行自己的类型检查。如果是这样,那么再次运行它ts-loader
会带来不必要的开销。
Babel 有一个插件,可以直接删除类型而不进行检查。运行以下命令安装它:
npm install @babel/preset-typescript --save-dev
接下来我们更新 webpack 配置。如果你按照上一步操作,请确保删除ts-loader
:
webpack.config.js
...
module.exports = {
entry: "./src/script.tsx",
...
module: {
rules: [
{
test: /\.m?[jt]sx$/, // <-- NEW
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: [
"@babel/preset-env",
["@babel/preset-react", { runtime: "automatic" }],
"@babel/preset-typescript", // <-- NEW
],
},
},
},
// { test: /\.tsx?$/, use: "ts-loader" },
],
},
...
};
有两行新代码需要注意。第一行是对我们的test
正则表达式的更新。我们babel-loader
现在希望在带有 JSX 或 TSX 扩展名的文件上运行。这[jt]
是正则表达式语法的一部分,表示“j 或 t”。
@babel/preset-typescript
第二个是在数组末尾添加presets
。Babel 和 Webpack 一样,以相反的顺序运行这些预设。我们希望在处理 JSX 之前先剥离类型。
运行时,npx webpack serve --open
我们应该再次在页面上看到大大的“欢迎”消息。即使我们引入了类型错误,webpack 仍然会编译,所以它依赖于我们首先将类型检查作为一个单独的过程进行。
总结
希望本教程能让您更好地了解转换 JSX 和 Typescript 代码的复杂生态系统,以及运行支持即时重新加载的本地开发服务器的好处。
虽然这些工具设置起来比较困难,但它们确实为大规模开发复杂的 Web 应用程序提供了一个极其丰富且用户友好的工作环境。一旦您学会了如何自行配置它们,您将能够更好地解决将来在构建工具中遇到的任何问题。
请查看本系列的其他文章!如果您觉得其中任何一篇有帮助,欢迎留言或提问,并与他人分享:
- 理解现代 Web 堆栈:运行本地 Web 服务器
- 理解现代 Web 技术栈:Babel
- 理解现代 Web 技术栈:Webpack - 基础知识
- 理解现代 Web 技术栈:Webpack - Loader、优化和 Bundle 分析
- 理解现代 Web 技术栈:Webpack - DevServer、React 和 Typescript
Twitter 上的@eagleson_alex
感谢您的阅读,敬请期待!
鏂囩珷鏉ユ簮锛�https://dev.to/alexeagleson/understanding-the-modern-web-stack-webpack-devserver-react-typescript-4b9b