如何仅在指定文件中将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
相关产品推荐
相关产品推荐

