轻松搞定 React 多页面开发:保姆级教程助你提升效率

背景

最近部门在进行 降本增笑,我们面临的挑战是将团队内所有业务线的 Node 服务迁移到新搭建的统一 Node 服务上(项目整合)。在这个过程中,我们遇到了一个棘手的问题:静态页面(next.js)的迁移。由于新的 Node 服务层暂时只能处理 ejs 模板,如果我们按照 ejs 模板的方案进行开发,无疑会导致开发效率和体验的下降。

方案

为了解决这个问题,我们提出了一个 折中 的解决方案:在 Node 层提供一个 ejs 文件,该文件中包含一个 div 标签。然后,我们通过挂载 js 文件的方式,加载业务 UI。这样,我们既能充分利用新的 Node 服务层的能力,同时又能保持 React 开发的高效和优秀体验。(暂不考虑 SSR)

确保 ejs 文件中的 div 标签具有唯一的 ID,以便于我们在挂载 js 文件时准确地定位到该标签

ini 复制代码
// ejs 文件

<div id='root'></div>

<script src="{cdn js 地址}">

这个方案对于原有采用 React 编写的页面来说非常可行,且开发成本相对较低。

要将 React 项目配置为打包输出一个 JS 文件,第一个想到的构建工具就是 Webpack。Webpack 可以将所有的资源(如JavaScript、CSS、图片等)打包成一个或多个文件。从功能和生态上来讲,Webpack 都是首选

实施

从 0 开始搭建项目

本篇博客,将记录在 React 项目中,从 0 搭建一个多页面的开发环境。以下是如何使用 Webpack 配置 React 项目的步骤:

  1. 首先,确保你已经安装了 Node.js。你可以在这里下载:nodejs.org/en/download...

  2. 在项目根目录下创建一个 package.json 文件,用于管理项目的依赖。你可以使用 npm init 命令来创建一个新的 package.json 文件。

    sh 复制代码
    # 以 Mac 电脑为例
    
    # 创建项目目录
    mkdir my-product
    # 创建 package.json
    npm init -y
    
    # 创建一个 src 目录(用于后续存放代码文件)
    mkdir src
  3. 安装 React 和 ReactDOM:

    css 复制代码
    npm install react react-dom --save
  4. 安装 Webpack 及其相关依赖:

    css 复制代码
    npm install webpack webpack-cli webpack-dev-server html-webpack-plugin --save-dev
  5. 安装Babel,它是一个 JavaScript 编译器,用于将 ES6+ 代码转换为浏览器兼容的代码:

    sql 复制代码
    npm install @babel/core @babel/preset-env @babel/preset-react babel-loader --save-dev
  6. 在项目根目录下创建一个名为 webpack.config.js 的文件,这是 Webpack 的配置文件。在此文件中,你可以配置 Webpack 如何打包项目:

    javascript 复制代码
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.js',
      output: {
        path: __dirname + '/dist',
        filename: 'bundle.js'
      },
      module: {
        rules: [
          {
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env', '@babel/preset-react']
              }
            }
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        })
      ]
    };

    在这个配置中,我们指定了入口文件(./src/index.js),输出文件(./dist/bundle.js),以及如何处理 JSX 文件(使用 Babel 编译器)。

  7. package.json 文件中的 scripts 字段中,添加以下脚本,以便在开发过程中启动 Webpack 开发服务器,以及在生产环境中构建项目:

    json 复制代码
    "scripts": {
      "start": "webpack serve --mode development --open",
      "build": "webpack --mode production"
    }
  8. src 目录下创建一个名为 index.html 的文件,它将作为项目的 HTML 模板:

    html 复制代码
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>React App</title>
      </head>
      <body>
        <div id="root"></div>
      </body>
    </html>
  9. src 目录下创建一个名为 index.js 的文件,它将作为项目的入口文件:

    javascript 复制代码
    import React from 'react';
    import ReactDOM from 'react-dom';
    
    ReactDOM.render(<div>我是页面 A</div>, document.getElementById('root'));
  10. 现在你可以在开发环境中运行项目:

    arduino 复制代码
    npm run start

    这将启动一个开发服务器,并在浏览器中打开项目。

  11. 要构建项目并生成一个打包后的JS文件,请运行:

    arduino 复制代码
    npm run build

    这将在dist目录下生成一个名为bundle.js的文件,你可以将其部署到 CDN。

如此一来,就可以在任何一个 ejs 模板页面上添加一个带有唯一 ID 的 div 标签。接下来,只需加载对应的 bundle.js 文件,即可轻松渲染出所需的 UI。

配置 TS

如果 React 项目使用了 TypeScript,需要额外配置 TypeScript。以下是如何使用 Webpack 配置 TypeScript 的步骤:

  1. 首先,安装 TypeScript 和 ts-loader。ts-loader 是一个 Webpack 插件,用于处理 TypeScript 文件:

    sql 复制代码
    npm install typescript ts-loader --save-dev
    
    npm install @types/react @types/react-dom --save-dev
  2. 在项目根目录下创建一个 tsconfig.json 文件,这是TypeScript的配置文件。在此文件中,你可以配置TypeScript的编译选项:

    json 复制代码
    {
     "compilerOptions": {
       "target": "ESNEXT",
       "outDir": "./dist/",
       "sourceMap": true,
       "noImplicitAny": true,
       "module": "commonjs",
       "jsx": "react",
       "esModuleInterop": true,
       "resolveJsonModule": true,
       "typeRoots": ["./node_modules/@types"],
       "baseUrl": "./src",
       "paths": {
         "@/*": ["*"]
       }
     },
     "include": [
       "./src/**/*"
     ],
     "exclude": [
       "node_modules"
     ]
    }

    在这个配置中,我们指定了输出目录(./dist/),启用了source map(用于调试),禁止了隐式的any类型,设置了模块系统为 commonjs,设置了目标JavaScript版本为ES5,以及启用了JSX。

  3. 修改你的webpack.config.js文件,添加一个新的规则来处理TypeScript文件:

    javascript 复制代码
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: './src/index.tsx',
      output: {
        path: __dirname + '/dist',
        filename: 'bundle.js'
      },
      resolve: {
        extensions: ['.ts', '.tsx', '.js']
      },
      module: {
        rules: [
          {
            test: /\.(ts|tsx)$/,
            exclude: /node_modules/,
            use: 'ts-loader'
          },
          {
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env', '@babel/preset-react']
              }
            }
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        })
      ]
    };

    在这个配置中,我们添加了一个新的规则来处理 .ts.tsx 文件(使用 ts-loader),并修改了入口文件为 ./src/index.tsx。我们还添加了一个 resolve.extensions 选项,用于指定 Webpack 可以自动解析的文件扩展名。

  4. 现在你可以在开发环境中运行项目:

    arduino 复制代码
    npm run start

配置 多语言

要在 React 项目中处理国际化(i18n),你可以使用 react-i18next 库。以下是如何配置 react-i18next 的步骤:

  1. 安装 react-i18nexti18next

    css 复制代码
    npm install react-i18next i18next --save
  2. src 目录下创建一个名为 i18n.js 的文件,用于配置i18n:

    javascript 复制代码
    import i18n from 'i18next';
    import { initReactI18next } from 'react-i18next';
    
    // 导入翻译资源
    import enTranslations from './locales/en/translation.json';
    import zhTranslations from './locales/zh/translation.json';
    
    i18n
      .use(initReactI18next)
      .init({
        resources: {
          en: {
            translation: enTranslations
          },
          zh: {
            translation: zhTranslations
          }
        },
        lng: 'en', // 默认语言
        fallbackLng: 'en', // 如果找不到翻译资源,使用的备用语言
        interpolation: {
          escapeValue: false
        }
      });
    
    export default i18n;

    在这个配置中,我们导入了两个翻译资源(英文和中文),并指定了默认语言和备用语言。你可以根据需要添加更多的翻译资源。

  3. src/locales目录下创建翻译资源文件。例如,你可以创建以下目录结构:

    css 复制代码
    src
    └── locales
        ├── en
        │   └── translation.json
        └── zh
            └── translation.json

    在这些JSON文件中,你可以添加键值对来表示翻译文本。例如:

    json 复制代码
    // src/locales/en/translation.json
    {
      "welcome": "Welcome to React"
    }
    
    // src/locales/zh/translation.json
    {
      "welcome": "欢迎来到React"
    }
  4. 在你的 src/index.js(或src/index.tsx,如果使用TypeScript)文件中,导入 i18n.js

    javascript 复制代码
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import './i18n'; // 导入i18n配置
    
    ReactDOM.render(<App />, document.getElementById('root'));
  5. 在你的 React 组件中,使用 react-i18next 提供的 useTranslation 钩子来获取翻译函数:

    javascript 复制代码
    import React from 'react';
    import { useTranslation } from 'react-i18next';
    
    function App() {
      const { t, i18n } = useTranslation();
    
      // 切换语言的函数
      const changeLanguage = (lng) => {
        i18n.changeLanguage(lng);
      };
    
      return (
        <div>
          <h1>{t('welcome')}</h1>
          <button onClick={() => changeLanguage('en')}>English</button>
          <button onClick={() => changeLanguage('zh')}>中文</button>
        </div>
      );
    }
    
    export default App;

    使用 useTranslation 钩子获取了翻译函数 ti18n 实例。我们可以使用 t 函数来获取翻译文本,例如 {t('welcome')}。我们还定义了一个 changeLanguage 函数,用于切换当前语言。

多个页面,单独打包部署

如果你的 React 项目有两个页面,并且你想要分别打包它们,可以在 Webpack 配置中设置多个入口点。

  1. 首先,修改的 webpack.config.js 文件,设置多个入口点和输出:

    此处需注意 output 的修改,必须加上 [name]

    javascript 复制代码
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    module.exports = {
      entry: {
        page1: './src/Page1.tsx',
        page2: './src/Page2.tsx'
      },
      output: {
        path: __dirname + '/dist',
        filename: '[name].bundle.js'
      },
      resolve: {
        extensions: ['.ts', '.tsx', '.js']
      },
      module: {
        rules: [
          {
            test: /\.(ts|tsx)$/,
            exclude: /node_modules/,
            use: 'ts-loader'
          },
          {
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env', '@babel/preset-react']
              }
            }
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/page1.html',
          chunks: ['page1'],
          filename: 'page1.html'
        }),
        new HtmlWebpackPlugin({
          template: './src/page2.html',
          chunks: ['page2'],
          filename: 'page2.html'
        })
      ]
    };

    在这个配置中,我们设置了两个入口点:page1page2,它们分别对应两个 React 组件(./src/Page1.tsx./src/Page2.tsx)。我们还修改了输出文件名,现在它会根据入口点的名称生成不同的文件(例如,page1.bundle.jspage2.bundle.js)。

    我们还添加了两个HtmlWebpackPlugin实例,用于生成两个HTML文件。每个插件实例都有一个chunks选项,用于指定包含哪些入口点的代码。

  2. 新建 page1 和 page2 的文件

  3. 现在你可以在开发环境中运行项目:

    arduino 复制代码
    npm run start

    你本地访问的 url,需要变成 http://localhost:8080/page1.html

  4. 要构建项目并生成打包后的JS文件,请运行:

    arduino 复制代码
    npm run build

    这将在 dist 目录下生成两个 HTML 文件和两个 JS 文件

输出文件携带 hash 值

要在输出的文件名中添加 hash 值,你可以在 webpack.config.jsoutput.filename 选项中使用[contenthash]占位符。以下是如何修改webpack.config.js文件的示例:

javascript 复制代码
module.exports = {
  entry: {
    page1: './src/Page1.tsx',
    page2: './src/Page2.tsx'
  },
  output: {
    path: __dirname + '/dist',
    filename: '[name].[contenthash].bundle.js'
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js']
  },
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        use: 'ts-loader'
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      }
    ]
  }
};

在这个配置中,将 output.filename 选项更改为 '[name].[contenthash].bundle.js'。这将根据文件内容生成一个 hash 值,并将其添加到文件名中。当文件内容发生变化时,hash 值也会改变,这有助于解决浏览器缓存问题。

现在,当你运行 npm run build 命令时,Webpack将生成带有 hash 值的 JS 文件

编译 CSS

如果你的React项目中使用了CSS,你需要在Webpack配置中添加对CSS文件的处理。以下是如何配置Webpack的步骤:

  1. 首先,安装style-loadercss-loaderstyle-loader用于将CSS注入到DOM中,css-loader用于解析CSS文件:

    css 复制代码
    npm install style-loader css-loader --save-dev
  2. 修改你的webpack.config.js文件,添加一个新的规则来处理CSS文件:

    javascript 复制代码
    module.exports = {
      // ...
      module: {
        rules: [
          {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
          },
          // ...其他规则
        ]
      }
      // ...
    };

    在这个配置中,我们添加了一个新的规则来处理 .css 文件。对于每个 .css 文件,Webpack 会先使用css-loader 来解析CSS,然后使用 style-loader 将CSS注入到DOM中。

现在你的 React 项目已经支持 CSS。你可以在 JS 或 TS 文件中导入 CSS 文件,例如:

javascript 复制代码
import React from 'react';
import './App.css'; // 导入CSS文件

function App() {
  return (
    <div className="App">
      {/* ... */}
    </div>
  );
}

export default App;

当你运行 npm run startnpm run build 命令时,Webpack 将处理你的 CSS 文件,并将其注入到 DOM 中。

优化

构建前删除上一次的打包文件

要在构建前删除 dist 文件夹,如果不进行这一步,你会发现你的 dist 目录中的文件会越来越多。可以使用 clean-webpack-plugin 插件。以下是如何配置 clean-webpack-plugin 的步骤:

  1. 安装clean-webpack-plugin

    css 复制代码
    npm install clean-webpack-plugin --save-dev
  2. 修改你的 webpack.config.js 文件,导入 clean-webpack-plugin 并将其添加到 plugins 数组中:

    javascript 复制代码
    const { CleanWebpackPlugin } = require('clean-webpack-plugin');
    
    module.exports = {
      // ...
      plugins: [
        new CleanWebpackPlugin(),
        // ...其他插件
      ]
      // ...
    };

    在这个配置中,我们导入了 CleanWebpackPlugin 并将其添加到 plugins 数组中。默认情况下, CleanWebpackPlugin 会在每次构建前删除 output.path 选项指定的文件夹(在这个例子中为 dist 文件夹)。

现在,当你运行 npm run build 命令时,Webpack 将在构建前自动删除 dist 文件夹。

配置别名

要在Webpack和TypeScript中配置别名,你需要分别更新webpack.config.jstsconfig.json文件。以下是如何配置别名的步骤:

  1. 修改你的 webpack.config.js 文件,添加 resolve.alias 选项:

    javascript 复制代码
    const path = require('path');
    
    module.exports = {
      // ...
      resolve: {
        extensions: ['.ts', '.tsx', '.js'],
        alias: {
          '@components': path.resolve(__dirname, 'src/components'),
          '@styles': path.resolve(__dirname, 'src/styles')
        }
      },
      // ...
    };

    在这个配置中,我们添加了两个别名:@components@styles。它们分别指向 src/componentssrc/styles 文件夹。你可以根据需要添加更多的别名。

  2. 修改你的 tsconfig.json 文件,添加 compilerOptions.paths 选项:

    json 复制代码
    {
      "compilerOptions": {
        // ...
        "baseUrl": "./src",
        "paths": {
          "@components/*": ["components/*"],
          "@styles/*": ["styles/*"]
        }
      },
      "include": ["./src/**/*"]
    }

    在这个配置中,我们添加了两个别名:@components@styles。它们分别指向 src/componentssrc/styles 文件夹。请注意,我们还设置了 baseUrl 选项,这是TypeScript解析非相对模块名的基本目录。

    现在你可以在项目中使用配置的别名。例如:

    javascript 复制代码
    import React from 'react';
    import styles from '@styles/test.css';
    import HeaderComponent from '@components/HeaderComponent';

    这将使得在项目中导入模块时更加简洁,同时避免了使用相对路径。

添加 SourceMap

如果不配置 SourceMap,你会发现在调试时,代码都是编译之后的,看着很别扭

要在调试过程中使用 SourceMap,你需要在 Webpack 配置中启用 SourceMap

  1. 修改你的 webpack.config.js 文件,添加 devtool 选项:

    javascript 复制代码
    module.exports = (env, argv) => {
      const isProduction = argv.mode === 'production';
    
      return {
        // ...
        devtool: isProduction ? 'source-map' : 'eval-source-map',
        // ...
      };
    };

    在这个配置中,我们使用了一个函数作为导出,以便根据 mode 参数(productiondevelopment)动态选择 sourceMap 类型。对于生产环境,我们选择了 source-map 类型,它会生成独立的 .map 文件。对于开发环境,我们选择了 eval-source-map 类型,它会将 sourceMap 嵌入到每个模块的eval 中,以实现更快的构建速度。

    想要了解更多类型的 sourceMap,请参阅Webpack文档

  2. 运行以下命令启动开发服务器:

    sql 复制代码
    npm run start -- --mode development
  3. 要构建生产环境的代码,请运行:

    arduino 复制代码
    npm run build -- --mode production

    这将在 dist 目录下生成 sourceMap 文件(例如 main.[contenthash].js.map)。请注意,在生产环境中,使用 sourceMap 可能会暴露源代码。如果不希望在生产环境中生成 sourceMap,你可以将 devtool 选项设置为 false 或移除 devtool 选项。

结尾

在整个开发工程搭建完成后,你可以轻松地将原有的 React 页面代码移植过来。整个过程非常顺畅,基本无需进行任何修改,实现了高效的代码迁移。

通过配置 Webpack,我们能够在 React 项目中便捷地创建多页面开发环境,从而提高开发和调试多页面应用程序的效率。若你也遇到类似问题,不妨尝试采用这种方法进行处理。希望本文能为你在 React 项目中配置多页面开发环境提供一定帮助。

相关推荐
GISer_Jing2 小时前
前端面试通关:Cesium+Three+React优化+TypeScript实战+ECharts性能方案
前端·react.js·面试
落霞的思绪3 小时前
CSS复习
前端·css
咖啡の猫5 小时前
Shell脚本-for循环应用案例
前端·chrome
百万蹄蹄向前冲7 小时前
Trae分析Phaser.js游戏《洋葱头捡星星》
前端·游戏开发·trae
朝阳5818 小时前
在浏览器端使用 xml2js 遇到的报错及解决方法
前端
GIS之路8 小时前
GeoTools 读取影像元数据
前端
ssshooter9 小时前
VSCode 自带的 TS 版本可能跟项目TS 版本不一样
前端·面试·typescript
你的人类朋友9 小时前
【Node.js】什么是Node.js
javascript·后端·node.js
Jerry9 小时前
Jetpack Compose 中的状态
前端
dae bal10 小时前
关于RSA和AES加密
前端·vue.js