最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 「React Build」之二集成 Webpack5/React17

    正文概述 掘金(大古同学)   2021-04-06   408

    「React Build」之二集成 Webpack5/React17

    在上一章节中我们已经使用 TypeScript/ESlint/Prettier/EditorConfig/stylelint,搭建好了前端规范的基础设施。这篇文章将会使用 webpack5 构建一个 React 应用。

    在我们的 Webpack 构建的过程中将会包含

    • 使用 Typescript 进行类型检查
    • 使用 Eslint 进行代码规范检查

    因为这些能帮助我们提高代码质量。我们还会

    • 配置 webpack 热更新
    • 配置 webpack 以区分开发/生产环境

    以便提高我们的开发体验

    创建一个基础项目

    使用 npm init -y 初始化一个前端项目,这会自动生成package.json 文件。当我们安装项目依赖的时候,这个文件会自动更新

    接下来我们创建以下文件目录

    └── config/                                            // webpack配置文件
        ├── webpack.dev.js
        ├── webpack.pro.js
        ├── webpack.common.js
    └── public/
        ├── index.html/                                   // html模板文件
    └── src/
        ├── index.tsx                                    // 项目入口文件
    ├── package.json
    

    让我们在 index.html 中添加如下代码

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>react-app</title>
      </head>
      <body>
        <div id="root"></div>
      </body>
    </html>
    

    这个 HTML 文件是 Webpack 构建过程中的模板文件。目的是告诉 Webpack 将 React 代码注入到 id="root"的 div 元素中,并在 HTML 中自动引入打包好的 JavaScript 和 CSS。

    添加 React

    在 CLI 中安装 React 及其对应的类型库

    yarn add react react-dom
    
    
    yarn add   @types/react @types/react-dom --dev
    

    添加 React 根组件

    创建一个 src/index.tsx 来编写 React 组件,此代码将会被展示到index.html 文件id="root"的 div 元素下

    import React from "react";
    import ReactDOM from "react-dom";
    
    const App = () => <h1>My React and TypeScript App!</h1>;
    
    ReactDOM.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>,
      document.getElementById("root")
    );
    

    在上面的代码中,我们使用React.StrictMode 创建组件并插入到id="root"的 div 元素下

    添加 Babel

    在项目中,我们需要使用 Babel 将 React 和 TypeScript 代码转换为 JavaScript。接下来我们安装一些 Babel 工具

     yarn add @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript @babel/plugin-transform-runtime @babel/runtime   --dev
    

    以下是一些 Babel 依赖的解释

    • @babel/core:Babel 核心库
    • @babel/preset-env:让我们可以在不支持 JavaScript 最新特性的浏览器中使用 ES6+语法
    • @babel/preset-react:将 React 代码转换为 JavaScript
    • @babel/preset-typescript:将 TypeScript 代码转换为 JavaScript
    • @babel/plugin-transform-runtime@babel/runtime:支持在低版本浏览器使用 ES6+语法,如 async/await

    Babel 配置

    我们通过.babelrc文件来进行 Babel 配置,在根目录创建此文件并加入以下内容

    {
      "presets": [
        "@babel/preset-env",
        "@babel/preset-react",
        "@babel/preset-typescript"
      ],
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "regenerator": true
          }
        ]
      ]
    }
    
    

    上面的配置是告诉 Babel 使用哪些插件(为了减少多文件配置麻烦,后面我们把此配置转移到 Webpack 中去)

    添加 Webpack

    Webpack 是目前最流行的前端模块打包工具

    接下来我们开始安装 Webpack 依赖

    yarn add webpack webpack-cli @types/webpack --dev
    

    在开发环境中,我们还要使用 Webpack 为我们提供的 web server功能

    yarn add webpack-dev-server @types/webpack-dev-server  --dev
    

    安装babel-loader-通知 Babel 将 React 和 TypeScript 代码转换为 JavaScript

    yarn add babel-loader  --dev
    

    安装 html-webpack-plugin-用来生成 HTML 模板

    yarn add html-webpack-plugin  --dev
    

    开发环境配置

    我们需要为 Webpack 添加几个配置文件

    • 公共配置
    • 开发环境配置
    • 生产环境配置

    注: Webpack 配置文件的代码需要符合 CommonJs规范。

    让我们首先配置开发环境文件,在根目录创建 config/webpack.dev.js并加入以下内容

    const path = require("path");
    const webpack = require("webpack");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    const config = {
      mode: "development",
      entry: {
        main: path.resolve(__dirname, "../src/index.tsx"),
      },
      output: {
        filename: "[name].js",
        path: path.resolve(__dirname, "../build"),
      },
      module: {
        rules: [
          {
            test: /\.(ts|js)x?$/i,
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: [
                  "@babel/preset-env",
                  "@babel/preset-react",
                  "@babel/preset-typescript",
                ],
                plugins: [
                  [
                    "@babel/plugin-transform-runtime",
                    {
                      regenerator: true,
                    },
                  ],
                ],
              },
            },
          },
        ],
      },
      resolve: {
        alias: {
          "@": path.resolve(__dirname, "../src"),
        },
        extensions: [".tsx", ".ts", ".jsx", ".js"],
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: "public/index.html",
        }),
        new webpack.HotModuleReplacementPlugin(),
      ],
      devtool: "inline-source-map",
      devServer: {
        contentBase: path.join(__dirname, "../build"),
        historyApiFallback: true,
        port: 4000,
        hot: true,
      },
    };
    
    module.exports = config;
    

    下面是一些相关配置的解释

    • mode : 构建开发环境代码还是生产环境代码。在上面的配置中我们使用development. Webpack 会自动将 process.env.NODE_ENV设置为development
    • output.public:构建的根路径是什么。
    • entry :模块构建的入口文件.在我们的项目中,入口是 src/index.tsx
    • module: 用于处理不同的资源模块.在我们的项目中,用babel-loader来处理.js,.jsx,.js,.tsx 后缀的文件
    • resolve.alias: 可以让我们在引入模块路径时使用别名
    • resolve.extensions告诉 Webpack 在模块解析期间要按顺序查找哪些文件的后缀,以方便我们在在引入模块文件时不带后缀名。
    • HtmlWebpackPlugin:用来创建 HTML 文件.在上面的配置中,我们告诉此插件使用public/index.html 作为文件模板
    • HotModuleReplacementPlugin/devServer.hot:修改业务代码后界面可以自动局部刷新,而不是整体刷新
    • devtool: 使用inline-source-map,可以在让我们在谷歌开发工具中调试源代码
    • devServer: 启动 Webpack 开发服务器,我们告诉 Webpack web 服务的根路径是 build目录,并且在4000端口上启动服务. historyApiFallback 对于多页面应用是比较有用的。最后,使用open在服务启动后自动打开浏览器

    为开发环境添加 NPM 脚本

    为了方便以开发模式启动应用,可以利用 npm 脚本-将以下内容添加到package.json

      ...
      "scripts": {
        "start": "webpack serve --config config/webpack.dev.js",
      }
      ...
    

    以上脚本会启动一个 Webpack 下的开发环境服务器.并且使用 config 选项来引用开发环境配置文件

    启动应用

    yarn start
    

    N 秒后,Webpack development server 将会启动,然后我们在浏览器中访问http://localhost:4000

    「React Build」之二集成 Webpack5/React17

    注意:Webpack 并没有在 build 目录生成任何文件,这是因为 Webpack 服务启动后文件都在内存中

    现在,我们修改 React 代码内容并观察变化.当我们保存代码后,浏览器会自动刷新

    「React Build」之二集成 Webpack5/React17

    在 Webpack 中手动配置热更新插件

    可能因为各种各样的原因导致 webpack 的 HMR 不生效。我们还可以手动配置热更新插件!

    安装 React 热更新插件react-refresh-webpack-plugin

    yarn add -D @pmmmwh/react-refresh-webpack-plugin react-refresh
    

    修改config/webpack.dev.js并加入以下内容

    const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin");
    
    const config = {
      // 指定target为 web
      target: "web",
      module: {
        rules: [
          {
            test: /\.(t|j)sx?$/i,
            include: path.resolve(__dirname, "../src"),
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: [
                  "@babel/preset-env",
                  "@babel/preset-react",
                  "@babel/preset-typescript",
                ],
                plugins: [
                  [
                    "@babel/plugin-transform-runtime",
                    {
                      regenerator: true,
                    },
                  ],
                  // 热更新加载器
                  "react-refresh/babel",
                ],
              },
            },
          },
        ],
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin(),
        // 热更新插件
        new ReactRefreshWebpackPlugin({
          exclude: [/node_modules/],
        }),
      ],
    };
    

    如果热更新配置遇到问题,可以参考以下 issue

    • Webpack 5 does not re render
    • HMR/Live Reloading broken after Webpack 5

    在 webpack 构建过程中添加类型检查

    目前, Webpack 构建过程没有做任何类型检查,我们可以使用fork-ts-checker-webpack-plugin 让 Webpack 构建过程支持类型检查。这意味着 Webpack 会通知我们任何类型相关的错误。 接下来我们安装相关依赖

    yarn add fork-ts-checker-webpack-plugin @types/fork-ts-checker-webpack-plugin --dev
    

    webpack.dev.js添加如下配置

    ...
    const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
    
    const config = {
      ...,
      plugins: [
        ...,
        new ForkTsCheckerWebpackPlugin({
          async: false
        }),
      ],
    };
    

    我们使用 async 标志来告诉 Webpack 等待代码的类型检查结束,然后才提交代码进行编译

    修改后,我们需要重新启动应用

    让我们在 src/index.tsx 中做如下修改

    ...
    const App = () => <h1>My React and TypeScript App!! {today}</h1>;
    ...
    

    当然,控制台报错了,因为使用了一个未定义的变量today。Webpack 将在终端中显示此类型错误

    「React Build」之二集成 Webpack5/React17

    现在,可以修改为类似的如下代码来解决此问题

    const App = () => (
      <h1>My React and TypeScript App!! {new Date().toLocaleDateString()}</h1>
    );
    

    控制台的类型错误消失了,刷新浏览器界面后显示为正确的内容

    「React Build」之二集成 Webpack5/React17

    在 webpack 构建过程中添加代码规范校验

    目前,Webpack 构建流程不会执行代码规范校验。 我们可以使用ESLintPlugin来使 Webpack 构建过程能够使用 ESLint 进行代码规范校验。 这意味着 Webpack 会通知我们任何代码规范校验的错误。 让我们安装这个依赖

    yarn add eslint-webpack-plugin --dev
    

    webpack.dev.js 修改如下内容

    ...
    const ESLintPlugin = require('eslint-webpack-plugin')
    
    const config = {
      ...,
      plugins: [
        ...,
        new ESLintPlugin({
          extensions: ["js", "jsx", "ts", "tsx"],
        }),
      ],
    };
    

    src/index.tsx 中,添加一个未使用的变量

    const unused = "something";
    

    Webpack 将会在控制台出现如下的代码校验警告

    「React Build」之二集成 Webpack5/React17

    生产环境配置

    Webpack 的生产环境配置与开发环境有些不同-我们需要项目代码被打包到文件目录中,并且做一定的优化

    • 不需要热更新/代码规范校验等功能
    • 为打包的文件名生成 hash 串
    • 清空打build目录
    • 压缩代码
    • ......

    让我们创建webpack.pro.js并加入以下内容

    const path = require("path");
    const webpack = require("webpack");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    const config = {
      mode: "production",
    
      entry: {
        main: path.resolve(__dirname, "../src/index.tsx"),
      },
      output: {
        filename: "[name].[contenthash].js",
        publicPath: "",
        path: path.resolve(__dirname, "../build"),
        // 打包前清空输出目录
        clean: true,
      },
      module: {
        rules: [
          {
            test: /\.(ts|js)x?$/i,
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: [
                  "@babel/preset-env",
                  "@babel/preset-react",
                  "@babel/preset-typescript",
                ],
              },
            },
          },
        ],
      },
      resolve: {
        extensions: [".tsx", ".ts", ".js"],
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: "public/index.html",
        }),
      ],
    };
    
    module.exports = config;
    

    配置与开发环境很像,但是又有以下不同

    • 我们将mode设置为 production. Webpack 会自动将process.env.NODE_ENV设置为production.这意味着打包后的代码中不会包含 React 开发者工具

    • output告诉 Webpack 将打包后的资源放到哪里.在我们的项目中,是放在build 目录中.

      • 如果项目中做了代码分离(code split).我们使用[name]标志告诉 Webpack 分离后的文件名称
      • 同时将[contenthash]标志加入到文件名称中.以便在代码内容更改后,打包以生成新的文件名称。这就可以避免浏览器缓存旧的文件
      • clean: true用来在每次打包构建前清空build目录,而不需要额外的插件,比如CleanWebpackPlugin

    为生产环境添加 NPM 脚本

    让我们为生产环境添加 NPM 脚本

      ...,
      "scripts": {
        "build": "webpack --config config/webpack.pro.js",
      },
      ...
    

    该脚本可以启动 Webpack 打包流程。 我们使用config选项来引用我们刚刚创建的生产配置文件。

    在终端运行以下命令:

    npm run build
    

    N 秒后,Webpack 将会在 build 目录生成打包后的文件

    如果我们查看 JavaScript 文件,可以发现它是被压缩过的。因为 Webpack 在生产模式会使用TerserWebpackPlugin来压缩代码。

    打包后的 JavaScript 文件也包含了我们应用程序中的所有代码以及 reactreact-dom 依赖包中的代码。

    如果我们查看 html 文件,会发现所有空格/换行都已被删除。 如果仔细观察,我们会看到一个 script 元素,该元素是通过HtmlWebpackPlugin自动插入的,以便引用打包后的 JavaScript 文件。

    「React Build」之二集成 Webpack5/React17

    抽离 Webpack 的公共配置

    虽然,我们将 生产环境 和 开发环境 做了区分,但是我们还是应该遵循不重复原则(Don't repeat yourself - DRY),保留一个 "common(通用)" 配置。为了将这些配置合并在一起,我们使用一个名为 webpack-merge 的工具。此工具会引用 "common" 配置,因此我们不必再在环境特定(environment-specific)的配置中编写重复代码。 参考文档

    我们先从安装 webpack-merge 开始

    yarn add  webpack-merge --dev
    

    添加 config/webpack.common.js 文件并加入以下配置

    const path = require("path");
    const HtmlWebpackPlugin = require("html-webpack-plugin");
    
    module.exports = {
      entry: {
        main: path.resolve(__dirname, "../src/index.tsx"),
      },
    
      module: {
        rules: [
          {
            test: /\.(ts|js)x?$/i,
            exclude: /node_modules/,
            use: {
              loader: "babel-loader",
              options: {
                presets: [
                  "@babel/preset-env",
                  "@babel/preset-react",
                  "@babel/preset-typescript",
                ],
                plugins: [
                  [
                    "@babel/plugin-transform-runtime",
                    {
                      regenerator: true,
                    },
                  ],
                ],
              },
            },
          },
        ],
      },
      resolve: {
        alias: {
          "@": path.resolve(__dirname, "../src"),
        },
        extensions: [".tsx", ".ts", ".js"],
      },
      plugins: [
        new HtmlWebpackPlugin({
          title: "React Build",
          template: "public/index.html",
        }),
      ],
    };
    

    修改 config/webpack.dev.js 的配置

    const path = require("path");
    const webpack = require("webpack");
    const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin");
    const ESLintPlugin = require("eslint-webpack-plugin");
    
    const { merge } = require("webpack-merge");
    const common = require("./webpack.common.js");
    
    module.exports = merge(common, {
      mode: "development",
      output: {
        filename: "[name].js",
        path: path.resolve(__dirname, "../build"),
      },
      plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new ForkTsCheckerWebpackPlugin({
          async: false,
        }),
        new ESLintPlugin({
          extensions: ["js", "jsx", "ts", "tsx"],
        }),
      ],
      devtool: "inline-source-map",
      devServer: {
        contentBase: path.join(__dirname, "../build"),
        historyApiFallback: true,
        port: 4000,
        hot: true,
      },
    });
    

    修改config/webpack.prod.js的配置

    const path = require("path");
    const { merge } = require("webpack-merge");
    const common = require("./webpack.common.js");
    
    module.exports = merge(common, {
      mode: "production",
      output: {
        filename: "[name].[contenthash].js",
        publicPath: "",
        path: path.resolve(__dirname, "../build"),
        // 打包前清空输出目录
        clean: true,
      },
    });
    

    webpack.common.js 中,我们设置了 entryoutput 配置,并且在其中引入了开发/生产环境公用的全部插件。

    webpack.dev.js 中,我们将 mode 设置为 development,并且为此环境添加了推荐的 devtool(强大的 source map)和简单的 devServer 配置。

    webpack.prod.js 中,我们将 mode 设置为 production

    随便运行下 NPM 脚本,然后查看输出结果的变化都能按预期所展示


    完美! ? 现在我们的项目已经准备就绪,并可以有效地开发 React 和 TypeScript 应用程序了。通过 build 命令也可以轻松地将项目集成到 CI / CD 流程中

    最后

    下一篇文章是 「React Build」之三集成 css/less/sass/antd design.敬请期待

    如果本文对你有帮助的话,给本文点个赞吧。

    参考文档

    1. Webpack5 新特性业务落地实战-字节前端

    起源地下载网 » 「React Build」之二集成 Webpack5/React17

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元