作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
当开始一个新的 React 项目中,您有许多模板可供选择: 创建React应用, react-boilerplate
, and React入门工具包,仅举几例.
These templates, 被成千上万的开发人员采用, 能够支持大规模的应用程序开发吗. 但是它们给开发人员的体验和包的输出带来了各种默认值, 哪一个可能不理想.
如果您希望对构建过程保持更大程度的控制, 那么您可能会选择投资于自定义Webpack配置. 你将从这个Webpack React教程中学到, 这个任务不是很复杂, 在对其他人的配置进行故障排除时,这些知识甚至可能很有用.
我们今天编写JavaScript的方式与浏览器可以执行的代码不同. 我们经常依赖其他类型的资源, transpiled语言, 以及现代浏览器尚未支持的实验性功能. Webpack是一个用于JavaScript的模块打包器,它可以弥合这一差距,并在不影响开发人员体验的情况下生成跨浏览器兼容的代码.
在我们开始之前, 你应该记住,本Webpack教程中提供的所有代码也以完整的Webpack/React示例的形式提供 GitHub上的配置文件. 如果您有任何问题,请随时参考它,并回到这篇文章.
从Legato(版本4)开始,Webpack不需要任何配置就可以运行. 选择构建模式将应用一组更适合目标环境的默认值. 本着这篇文章的精神, 我们将忽略这些默认值,并为每个目标环境实现合理的配置.
首先,我们需要安装 webpack
and webpack-cli
:
npm install -D webpack
然后我们需要增加人口 webpack.config.js
具有以下选项的配置:
devtool
:在开发模式下启用源映射生成功能.entry
: React应用程序的主文件.output.path
:存放输出文件的根目录.output.filename
:用于生成文件的文件名模式.output.publicPath
: web服务器上部署文件的根目录的路径.Const path = require("path");
module.Exports = function(_env, argv) {
const isProduction = argv.模式===“生产”;
const isDevelopment = !isProduction;
return {
devtool: isDevelopment && “cheap-module-source-map”,
entry: "./src/index.js",
output: {
path: path.解决(__dirname " dist”),
文件名:“资产/ js /[名称].(contenthash: 8).js",
publicPath: "/"
}
};
};
上面的配置对于普通的JavaScript文件工作得很好. 但是当在React项目中使用Webpack时, 在将代码交付给用户之前,我们需要执行额外的转换. 在下一节中,我们将使用Babel来改变Webpack加载JavaScript文件的方式.
Babel 是一个JavaScript编译器与许多插件的代码转换. In this section, 我们将把它作为加载器引入到Webpack配置中,并将其配置为将现代JavaScript代码转换为普通浏览器可以理解的代码.
首先,我们需要安装 babel-loader
and @babel/core
:
@babel/core babel-loader
然后加上a module
节到我们的Webpack配置中 babel-loader
负责加载JavaScript文件;
@@ -11,6 +11,25 @@模块.Exports = function(_env, argv) {
path: path.解决(__dirname " dist”),
文件名:“资产/ js /[名称].(contenthash: 8).js",
publicPath: "/"
+ },
+ module: {
+ rules: [
+ {
+ test: /\.jsx?$/,
+ exclude: /node_modules/,
+ use: {
+ loader: "babel-loader",
+ options: {
+ cacheDirectory: true,
+ cacheCompression: false,
+ envName: isProduction ? “生产”:“发展”
+ }
+ }
+ }
+ ]
+ },
+ resolve: {
+ extensions: [".js", ".jsx"]
}
};
};
我们将使用一个单独的配置文件配置Babel, babel.config.js
. 它将使用以下功能:
@babel / preset-env
:将现代JavaScript功能转换为向后兼容的代码.@babel / preset-react
:将JSX语法转换为普通的JavaScript函数调用.@babel / plugin-transform-runtime
:通过将Babel helper提取到共享模块中来减少代码重复.@babel / plugin-syntax-dynamic-import
: Enables dynamic import()
syntax 在浏览器中缺少native Promise
support.@babel / plugin-proposal-class-properties
:启用对 公共实例字段 语法建议,用于编写基于类的React组件.我们还将启用一些 特定于react的生产优化:
babel-plugin-transform-react-remove-prop-types
从生产代码中删除不必要的属性类型.@babel / plugin-transform-react-inline-elements
evaluates React.createElement
在编译期间内联结果.@babel / plugin-transform-react-constant-elements
提取静态React元素作为常量.下面的命令将安装所有必需的组件 dependencies:
@babel / plugin-transform-runtime @babel / plugin-syntax-dynamic-import @babel / plugin-proposal-class-properties babel-plugin-transform-react-remove- pro- types @babel / plugin-transform-react-inline-elements @babel / plugin-transform-react-constant-elements
然后我们就会增加人口 babel.config.js
通过这些设置:
module.exports = {
presets: [
[
“@babel / preset-env”,
{
modules: false
}
],
“@babel / preset-react”
],
plugins: [
“@babel / plugin-transform-runtime”,
“@babel / plugin-syntax-dynamic-import”,
“@babel / plugin-proposal-class-properties”
],
env: {
production: {
only: ["src"],
plugins: [
[
“transform-react-remove-prop-types”,
{
removeImport:真
}
],
“@babel / plugin-transform-react-inline-elements”,
“@babel / plugin-transform-react-constant-elements”
]
}
}
};
这种配置允许我们以一种与所有相关浏览器兼容的方式编写现代JavaScript. React应用程序中可能还需要其他类型的资源, 我们将在以下几节中介绍哪些内容.
当涉及到React应用程序的样式时, 至少, 我们需要能够包括纯CSS文件. 我们将在Webpack中使用以下加载器:
css-loader
: CSS文件解析, 解决外部资源, such as images, fonts, 以及其他样式导入.style-loader
:在开发期间,在运行时将加载的样式注入文档.mini-css-extract-plugin
:将加载的样式提取到单独的文件中以供生产使用,以利用浏览器缓存.让我们安装上面的CSS加载器:
npm install -D css-loader style-loader mini-css-extract-plugin
然后我们将添加一个新规则到 module.rules
我们的Webpack配置:
@@ -1,4 +1,5 @@
Const path = require("path");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.Exports = function(_env, argv) {
const isProduction = argv.模式===“生产”;
@@ -25,6 +26,13 @@模块.Exports = function(_env, argv) {
envName: isProduction ? “生产”:“发展”
}
}
+ },
+ {
+ test: /\.css$/,
+使用:[
+ isProduction ? MiniCssExtractPlugin.Loader:“style-loader”;
+ " css-loader”
+ ]
}
]
},
We’ll also add MiniCssExtractPlugin
to the plugins
节,我们只会在生产模式下启用它:
@@ -38,6 +38,13 @@模块.Exports = function(_env, argv) {
},
resolve: {
extensions: [".js", ".jsx"]
- }
+ },
+插件:[
+ isProduction &&
+新MiniCssExtractPlugin({
+文件名:"assets/css/[name].(contenthash: 8).css",
+ chunkFilename:“资产/ css /[名称].(contenthash: 8).chunk.css"
+ })
+ ].过滤器(布尔)
};
};
这种配置适用于普通CSS文件,并且可以扩展到使用各种CSS处理器, 比如Sass和PostCSS, 我们会讨论的 下一篇文章.
Webpack还可以用于加载静态资源,如图像、视频和其他二进制文件. 处理这类文件的最通用方法是使用 file-loader
or url-loader
,它将为其使用者提供所需资源的URL引用.
在本节中,我们将添加 url-loader
处理常见的图像格式. What sets url-loader
apart from file-loader
是原始文件的大小小于给定的阈值吗, 它将把整个文件作为base64编码的内容嵌入到URL中, 这样就不需要额外的请求了.
首先我们安装 url-loader
:
npm install -D url-loader
然后,我们添加一个新规则 module.rules
我们的Webpack配置:
@@ -34,6 +34,16 @@模块.Exports = function(_env, argv) {
isProduction ? MiniCssExtractPlugin.Loader:“style-loader”;
"css-loader"
]
+ },
+ {
+ test: /\.(png | jpg | gif) $ /我,
+ use: {
+ loader: "url-loader",
+ options: {
+上限:8192;
+ 名称:“静态/媒体/[名称].[hash:8].[ext]"
+ }
+ }
}
]
},
对于SVG图像,我们将使用 @svgr/webpack
它将导入的文件转换为React组件.
We install @svgr/webpack
:
npm install -D @svgr/webpack
然后,我们添加一个新规则 module.rules
我们的Webpack配置:
@@ -44,6 +44,10 @@模块.Exports = function(_env, argv) {
名称:“静态/媒体/[名称].[hash:8].[ext]"
}
}
+ },
+ {
+ test: /\.svg$/,
+ 使用(“@svgr / webpack”):
}
]
},
SVG图像作为React组件可以很方便,而且 @svgr/webpack
执行优化使用 SVGO.
注意:对于某些动画甚至鼠标悬停效果, 您可能需要使用JavaScript操作SVG. Fortunately, @svgr/webpack
默认情况下将SVG内容嵌入到JavaScript包中, 允许您绕过为此需要的安全限制.
当我们需要引用任何其他类型的文件时,泛型 file-loader
will do the job. 它的工作原理与 url-loader
,为需要它的代码提供资产URL,但不尝试对其进行优化.
和往常一样,首先我们安装Node.js module. In this case, file-loader
:
npm install -D file-loader
然后,我们添加一个新规则 module.rules
节. For example:
@@ -48,6 +48,13 @@模块.Exports = function(_env, argv) {
{
test: /\.svg$/,
使用(“@svgr / webpack”):
+ },
+ {
+ test: /\.(测试结束|传递| ttf woff | | woff2) /美元,
+ loader: require.解决(“file-loader”),
+ options: {
+ 名称:“静态/媒体/[名称].[hash:8].[ext]"
+ }
}
]
},
Here we added file-loader
用于加载字体,您可以从CSS文件中引用这些字体. 您可以扩展这个示例来加载所需的任何其他类型的文件.
我们可以使用Webpack的 DefinePlugin()
将构建环境中的环境变量公开给应用程序代码. For example:
@@ -1,5 +1,6 @@
Const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+Const webpack = require("webpack");
module.Exports = function(_env, argv) {
const isProduction = argv.模式===“生产”;
@@ -65,7 +66,12 @@模块.Exports = function(_env, argv) {
新MiniCssExtractPlugin ({
文件名:“资产/ css /[名称].(contenthash: 8).css",
chunkFilename:“资产/ css /[名称].(contenthash: 8).chunk.css"
- })
+ }),
+ new webpack.DefinePlugin({
+ " 过程.env.NODE_ENV": JSON.stringify(
+ isProduction ? “生产”:“发展”
+ )
+ })
].过滤器(布尔)
};
};
这里我们代入 process.env.NODE_ENV
使用表示生成模式的字符串: "development"
or "production"
.
在没有…的情况下 index.html
文件,我们的JavaScript包是无用的,只是坐在那里,没有人能找到它. 在本节中,我们将介绍 html-webpack-plugin
为我们生成一个HTML文件.
We install html-webpack-plugin
:
npm install -D html-webpack-plugin
Then we add html-webpack-plugin
to the plugins
我们的Webpack配置:
@@ -1,6 +1,7 @@
Const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
Const webpack = require("webpack");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
module.Exports = function(_env, argv) {
const isProduction = argv.模式===“生产”;
@@ -71,6 +72,10 @@模块.Exports = function(_env, argv) {
"process.env.NODE_ENV": JSON.stringify(
isProduction ? “生产”:“发展”
)
+ }),
+ new HtmlWebpackPlugin({
+ template: path.解决(__dirname,“公共/索引.html"),
+ inject: true
})
].过滤器(布尔)
};
The generated public/index.html
文件将加载我们的bundle并引导我们的应用.
我们可以在构建过程中使用几种优化技术. 我们将从代码缩小开始, 通过这个过程,我们可以在不牺牲功能的情况下减小包的大小. 我们将使用两个插件来最小化代码: terser-webpack-plugin
的JavaScript代码,和 optimize-css-assets-webpack-plugin
for CSS.
让我们来安装它们:
优化css-assets-webpack-plugin
然后加一个 optimization
节到我们的配置:
@@ -2,6 +2,8 @@ Const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
Const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
+const TerserWebpackPlugin = require(" terserwebpack -plugin");
+const optimizecssassetplugin = require("optimize-css-assets-webpack-plugin");
module.Exports = function(_env, argv) {
const isProduction = argv.模式===“生产”;
@@ -75,6 +77,27 @@模块.Exports = function(_env, argv) {
isProduction ? “生产”:“发展”
)
})
- ].过滤器(布尔)
+ ].过滤器(布尔),
+优化:{
+最小化:是生产,
+ minimizer: [
+ new TerserWebpackPlugin({
+ terserOptions: {
+ compress: {
+比较:false
+ },
+ mangle: {
+ safari10:正确
+ },
+输出:{
+ comments: false;
+ ascii_only: true
+ },
+ warnings: false
+ }
+ }),
+ new optimizecssassetplugin ()
+ ]
+ }
};
};
上述设置将确保代码与所有现代浏览器兼容.
代码分割是我们可以用来提高应用程序性能的另一种技术. 代码分割可以参考两种不同的方法:
import()
statement, 我们可以提取应用程序中占bundle大小很大一部分的部分, 并根据需要加载它们.我们将填充 optimization.splitChunks
Webpack配置的一部分,其中设置了将第三方依赖项和公共块提取到单独的文件中:
@@ -99,7 +99,29 @@模块.Exports = function(_env, argv) {
sourceMap: true
}),
新的OptimizeCssAssetsPlugin ()
- ]
+ ],
+ splitChunks: {
+ chunks: "all",
+ minSize: 0,
+ maxInitialRequests: 20;
+ maxAsyncRequests: 20;
+ cacheGroups: {
+ vendor: {
+ 测试:/ [\ \ /]node_modules \ \ / /,
+ name(module, chunks, cacheGroupKey) {
+ const packageName = module.context.match(
+ /[\\/] node_modules [/ \ \] (.*?)([\\/]|$)/
+ )[1];
+ return ' ${cacheGroupKey}.${packageName.取代 ("@", "")}`;
+ }
+ },
+ common: {
+ minChunks: 2,
+ priority: -10
+ }
+ }
+ },
+ runtimeChunk:“单身”
}
};
};
让我们更深入地看看我们在这里使用的选项:
chunks: "all"
默认情况下,公共块提取只影响加载了动态块的模块 import()
. 此设置还支持对入口点加载进行优化.minSize: 0
默认情况下,只有超过一定大小阈值的块才有资格提取. 此设置允许对所有公共代码进行优化,而不管其大小.maxInitialRequests: 20
and maxAsyncChunks: 20
:这些设置增加了可以为入口点导入和分离点导入并行加载的源文件的最大数量, respectively.此外,我们指定以下内容 cacheGroups
configuration:
vendors
:配置第三方模块的提取.
测试:/ [\ \ /]node_modules \ \ / /
:用于匹配第三方依赖的文件名模式.name(module, chunks, cacheGroupKey)
:通过给相同的模块一个共同的名称,将它们分组在一起.common
:配置从应用程序代码中提取通用块.
minChunks: 2
:如果从至少两个模块中引用,则认为一个块是公共的.priority: -10
:指定负优先级 common
缓存组,以便块为 vendors
首先考虑缓存组.我们还从单个块中提取Webpack运行时代码,这些代码可以在多个入口点之间共享, by specifying runtimeChunk:“单身”
.
So far, 我们专注于创建和优化应用程序的生产构建, 但Webpack也有自己的web服务器,可以实时重新加载和错误报告, 哪些对我们的开发有帮助. It’s called webpack-dev-server
,我们需要单独安装:
npm install -D webpack-dev-server
在这个代码片段中,我们引入 devServer
section放入我们的Webpack配置:
@@ -120,6 +120,12 @@模块.Exports = function(_env, argv) {
}
},
runtimeChunk:“单身”
+ },
+ devServer: {
+ compress: true,
+ historyApiFallback:真,
+ open: true,
+ overlay: true
}
};
};
这里我们使用了以下选项:
compress: true
:启用资源压缩,以便更快地重新加载.historyApiFallback:真
:允许回退到 index.html
对于基于历史的路由.open: true
:启动开发服务器后打开浏览器.overlay: true
:在浏览器窗口中显示Webpack错误.您可能还需要进行配置 proxy settings 将API请求转发到后端服务器.
React/Webpack教程的第一部分介绍了用Webpack加载各种资源类型, 在开发环境中使用Webpack和React, 以及优化生产构建的几种技术. 如果你需要,你可以随时复习 完整的配置文件 为你自己的React/Webpack设置提供灵感. 提高这些技能是任何提供服务的人的标准票价 React开发服务.
In 本系列的下一部分, 我们将用更具体用例的说明来展开这个配置, 包括TypeScript的使用, CSS预处理器, 以及涉及服务器端渲染和ServiceWorkers的高级优化技术. 请继续关注,学习你需要了解的关于Webpack的一切知识,以便将你的React应用程序投入生产.
不,但它们可以一起使用. Webpack和React在开发过程中完成不同的任务. React不需要Webpack也可以使用.
Yes, 你可以使用不同的模块绑定器来编写React应用程序, 或者干脆跳过捆绑的步骤.
Webpack以一种浏览器可以理解的方式将源文件捆绑在一起.
Webpack是JavaScript的模块打包器. Webpack解析外部模块依赖,并以一种浏览器可以理解的方式发布它们.
Webpack是一个用于捆绑JavaScript的npm模块. 它负责收集应用程序的依赖项,并将它们合并以供web浏览器使用.
No, it is not. 框架可以让您以不同的方式编写代码, 更方便的方式, 或者引入新的功能. Webpack不做这些,而是事后优化和调整你的代码以适应不同的环境.
Michael是一名高级全栈开发人员,专门从事React和TypeScript的前端开发.
7
世界级的文章,每周发一次.
世界级的文章,每周发一次.