作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
迈克尔·本都的头像

Michael Pontus

Michael是一名高级全栈开发人员,专门从事React和TypeScript的前端开发.

工作经验

7

Share

当开始一个新的 React 项目中,您有许多模板可供选择: 创建React应用, react-boilerplate, and React入门工具包,仅举几例.

These templates, 被成千上万的开发人员采用, 能够支持大规模的应用程序开发吗. 但是它们给开发人员的体验和包的输出带来了各种默认值, 哪一个可能不理想.

如果您希望对构建过程保持更大程度的控制, 那么您可能会选择投资于自定义Webpack配置. 你将从这个Webpack React教程中学到, 这个任务不是很复杂, 在对其他人的配置进行故障排除时,这些知识甚至可能很有用.

Webpack:入门

我们今天编写JavaScript的方式与浏览器可以执行的代码不同. 我们经常依赖其他类型的资源, transpiled语言, 以及现代浏览器尚未支持的实验性功能. Webpack是一个用于JavaScript的模块打包器,它可以弥合这一差距,并在不影响开发人员体验的情况下生成跨浏览器兼容的代码.

在我们开始之前, 你应该记住,本Webpack教程中提供的所有代码也以完整的Webpack/React示例的形式提供 GitHub上的配置文件. 如果您有任何问题,请随时参考它,并回到这篇文章.

React Webpack配置

从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文件的方式.

JS Loader

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的生产优化:

下面的命令将安装所有必需的组件 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应用程序中可能还需要其他类型的资源, 我们将在以下几节中介绍哪些内容.

CSS Loader

当涉及到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, 我们会讨论的 下一篇文章.

Image Loader

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

对于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

当我们需要引用任何其他类型的文件时,泛型 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".

HTML Plugin

在没有…的情况下 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并引导我们的应用.

Optimization

我们可以在构建过程中使用几种优化技术. 我们将从代码缩小开始, 通过这个过程,我们可以在不牺牲功能的情况下减小包的大小. 我们将使用两个插件来最小化代码: 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 ()
+      ]
+    }
   };
 };

上述设置将确保代码与所有现代浏览器兼容.

Code Splitting

代码分割是我们可以用来提高应用程序性能的另一种技术. 代码分割可以参考两种不同的方法:

  1. Using a dynamic import() statement, 我们可以提取应用程序中占bundle大小很大一部分的部分, 并根据需要加载它们.
  2. 我们可以提取更改频率较低的代码, 为了利用浏览器缓存和提高性能的重复访问者.

我们将填充 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:“单身”.

Dev Server

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请求转发到后端服务器.

Webpack和React:性能优化和就绪!

React/Webpack教程的第一部分介绍了用Webpack加载各种资源类型, 在开发环境中使用Webpack和React, 以及优化生产构建的几种技术. 如果你需要,你可以随时复习 完整的配置文件 为你自己的React/Webpack设置提供灵感. 提高这些技能是任何提供服务的人的标准票价 React开发服务.

In 本系列的下一部分, 我们将用更具体用例的说明来展开这个配置, 包括TypeScript的使用, CSS预处理器, 以及涉及服务器端渲染和ServiceWorkers的高级优化技术. 请继续关注,学习你需要了解的关于Webpack的一切知识,以便将你的React应用程序投入生产.

了解基本知识

  • React使用Webpack吗?

    不,但它们可以一起使用. Webpack和React在开发过程中完成不同的任务. React不需要Webpack也可以使用.

  • 你能在没有Webpack的情况下使用React吗?

    Yes, 你可以使用不同的模块绑定器来编写React应用程序, 或者干脆跳过捆绑的步骤.

  • Webpack的目的是什么?

    Webpack以一种浏览器可以理解的方式将源文件捆绑在一起.

  • Webpack是什么?它是如何工作的?

    Webpack是JavaScript的模块打包器. Webpack解析外部模块依赖,并以一种浏览器可以理解的方式发布它们.

  • Webpack打包器是什么?

    Webpack是一个用于捆绑JavaScript的npm模块. 它负责收集应用程序的依赖项,并将它们合并以供web浏览器使用.

  • Webpack是一个框架吗?

    No, it is not. 框架可以让您以不同的方式编写代码, 更方便的方式, 或者引入新的功能. Webpack不做这些,而是事后优化和调整你的代码以适应不同的环境.

就这一主题咨询作者或专家.
Schedule a call
迈克尔·本都的头像
Michael Pontus

Located in 圣彼得堡,俄罗斯

Member since 2018年12月5日

作者简介

Michael是一名高级全栈开发人员,专门从事React和TypeScript的前端开发.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

工作经验

7

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal开发者

Join the Toptal® community.