轻松搞定 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 项目中配置多页面开发环境提供一定帮助。

相关推荐
轻口味27 分钟前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王1 小时前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
娃哈哈哈哈呀2 小时前
vue中的css深度选择器v-deep 配合!important
前端·css·vue.js
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
ekskef_sef4 小时前
32岁前端干了8年,是继续做前端开发,还是转其它工作
前端
sunshine6414 小时前
【CSS】实现tag选中对钩样式
前端·css·css3
真滴book理喻4 小时前
Vue(四)
前端·javascript·vue.js
蜜獾云4 小时前
npm淘宝镜像
前端·npm·node.js