You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅在指定文件中将React别名替换为Preact?

问题描述

我的项目基于Ruby on Rails,部分页面使用React前端,同时采用Webpack构建。我希望在应用的某一模块中使用react-final-form,并通过将React别名替换为Preact来减小页面体积。我已实现全项目范围的React到Preact的别名替换,但实际项目中无法修改package.json,请问能否仅对单个文件/页面/入口点应用该别名?

以下是全项目别名替换的示例代码:

index.js

import React from 'react'
import { render } from 'react-dom'
import { Form, Field } from 'react-final-form'
const onSubmit = async values => {
  window.alert(JSON.stringify(values, 0, 2))
}
const App = () => (
  <div>
    <h1>React Final Form - Simple Example</h1>
    <Form
      onSubmit={onSubmit}
      initialValues={{ stooge: 'larry', employed: false }}
      render={({ handleSubmit, form, submitting, pristine, values }) => (
        <form onSubmit={handleSubmit}>
          <div>
            <label>First Name</label>
            <Field
              name="firstName"
              component="input"
              type="text"
              placeholder="First Name"
            />
          </div>
          <div className="buttons">
            <button type="submit" disabled={submitting || pristine}>
              Submit
            </button>
          </div>
          <pre>{JSON.stringify(values, 0, 2)}</pre>
        </form>
      )}
    />
  </div>
)
render(<App />, document.getElementById('root'))

package.json

{
  "name": "my-react-webpack-app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack serve",
    "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "final-form": "^4.20.9",
    "preact": "^10.15.1",
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat",
    "react-final-form": "^6.5.9"
  },
  "devDependencies": {
    "@babel/core": "^7.22.1",
    "@babel/preset-env": "^7.22.4",
    "@babel/preset-react": "^7.22.3",
    "babel-loader": "^9.1.2",
    "html-webpack-plugin": "^5.5.1",
    "webpack": "^5.85.0",
    "webpack-cli": "^5.1.2",
    "webpack-dev-server": "^4.15.0"
  }
}

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: path.join(__dirname, "src", "index.js"),
  output: {
    path:path.resolve(__dirname, "dist"),
  },
  resolve: {
    alias: {
      "react": "preact/compat",
      "react-dom/test-utils": "preact/test-utils",
      "react-dom": "preact/compat",     // Must be below test-utils
      "react/jsx-runtime": "preact/jsx-runtime"
    },
  },
  module: {
    rules: [
      {
        test: /\.js$|jsx/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src", "index.html"),
    }),
  ],
}

解决方案

完全可以仅针对单个入口点或文件实现React到Preact的别名替换,不需要修改package.json。下面提供两种精准控制的实现方式:

方法一:多入口单独配置别名

如果你的Webpack是多入口架构,可以给需要替换的入口单独设置resolve.alias,其他入口保持原有React依赖:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    // 普通React入口,不做替换
    main: path.join(__dirname, "src", "main.js"),
    // 需要替换为Preact的目标入口
    preactForm: path.join(__dirname, "src", "index.js")
  },
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: '[name].bundle.js'
  },
  // 全局默认不设置React别名
  resolve: {},
  module: {
    rules: [
      {
        test: /\.js$|jsx/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src", "main.html"),
      chunks: ['main']
    }),
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src", "index.html"),
      chunks: ['preactForm']
    }),
  ],
  // 针对目标入口单独注入别名配置
  resolve: {
    alias: {
      // 只在preactForm入口生效的别名
      "react": "preact/compat",
      "react-dom/test-utils": "preact/test-utils",
      "react-dom": "preact/compat",
      "react/jsx-runtime": "preact/jsx-runtime"
    }
  },
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
};

方法二:针对单个文件精准替换

如果只想给特定文件(比如示例中的index.js)应用别名,可以通过Webpack模块规则或Babel插件实现:

方案1:Webpack规则级别名配置

module.exports = {
  // ...其他全局配置
  module: {
    rules: [
      {
        // 匹配需要替换的目标文件
        test: /index\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        },
        // 给该文件单独设置别名
        resolve: {
          alias: {
            "react": "preact/compat",
            "react-dom/test-utils": "preact/test-utils",
            "react-dom": "preact/compat",
            "react/jsx-runtime": "preact/jsx-runtime"
          }
        }
      },
      // 其他文件的默认React处理规则
      {
        test: /\.js$|jsx/,
        exclude: /(node_modules|index\.js)/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react']
          }
        }
      },
      // ...其他样式、资源规则
    ]
  }
};

方案2:Babel插件层面单个文件替换

安装babel-plugin-module-resolver后,在.babelrc.js中针对目标文件设置别名:

module.exports = function(api) {
  api.cache(true);

  const presets = ['@babel/preset-env', '@babel/preset-react'];
  const plugins = [];

  // 检测当前处理的文件是否为目标文件
  const filename = api.caller(caller => caller?.filename);
  if (filename && filename.includes('src/index.js')) {
    plugins.push([
      'module-resolver',
      {
        alias: {
          'react': 'preact/compat',
          'react-dom': 'preact/compat',
          'react/jsx-runtime': 'preact/jsx-runtime'
        }
      }
    ]);
  }

  return {
    presets,
    plugins
  };
};

Rails项目适配提示

如果你的Rails项目使用jsbundling-rails,可以直接在webpack.config.js中用上述多入口或规则匹配的方式实现;如果是旧版webpacker,可以在config/webpack/environment.js中通过webpack-chain给特定入口添加别名:

const { environment } = require('@rails/webpacker');

// 注册目标入口
environment.config.set('entry.preactForm', './app/javascript/src/index.js');
// 给该入口单独设置别名
environment.config.set('resolve.alias', {
  "react": "preact/compat",
  "react-dom": "preact/compat",
  "react/jsx-runtime": "preact/jsx-runtime"
});

module.exports = environment;

内容的提问来源于stack exchange,提问作者su_aseer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 13:52:20