如何在SULU项目中集成React并适配Twig与Webpack配置?
问题描述
我正在开发一个前端采用Twig模板、使用自定义Webpack构建的SULU项目。需要为部分具备高交互性、需状态管理的组件引入React,请问具体该如何实现?以及如何调整Webpack配置,使其同时兼容Twig组件与新增的React部分?
当前项目的Webpack配置如下:
const webpack = require("webpack"); const date = new Date(); const ESLintPlugin = require("eslint-webpack-plugin"); module.exports = () => { return { plugins: [ new ESLintPlugin({ emitWarning: true, failOnError: true, }), new webpack.DefinePlugin({ BUILD: JSON.stringify( `BUILD: ${date.getDate()}.${ date.getMonth() + 1 }.${date.getHours()}.${date.getMinutes()}` ), }), ], }; };
我尝试了以下配置,但似乎无法正常运行:
module.exports = () => { return { mode: process.env.NODE_ENV === "production" ? "production" : "development", entry: "./js/main.js", output: { path: path.resolve(__dirname, "../../public/build/website/js"), filename: "main.js", }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: [ "@babel/preset-env", "@babel/preset-react", ], }, }, }, { test: /\.module\.scss$/, use: [ "style-loader", { loader: "css-loader", options: { modules: { localIdentName: "[name]__[local]___[hash:base64:5]", }, }, }, "sass-loader", ], }, { test: /\.scss$/, exclude: /\.module\.scss$/, use: ["style-loader", "css-loader", "sass-loader"], }, ], }, resolve: { extensions: [".js", ".jsx"], }, plugins: [ new ESLintPlugin({ emitWarning: true, failOnError: true, }), new webpack.DefinePlugin({ BUILD: JSON.stringify( `${new Date().getDate()}.${ new Date().getMonth() + 1 }.${new Date().getHours()}.${new Date().getMinutes()}` ), }), ], }; };
解决方案
一、安装必要依赖
先安装React核心库及编译、样式处理相关依赖:
npm install react react-dom npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader sass-loader style-loader sass path mini-css-extract-plugin
二、修正Webpack配置
你尝试的配置存在path未引入、生产环境样式处理不规范等问题,以下是调整后的完整配置:
const webpack = require("webpack"); const path = require("path"); const date = new Date(); const ESLintPlugin = require("eslint-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = () => { const isProduction = process.env.NODE_ENV === "production"; return { mode: isProduction ? "production" : "development", entry: "./js/main.js", output: { path: path.resolve(__dirname, "../../public/build/website/js"), filename: isProduction ? "main.[contenthash].js" : "main.js", publicPath: "/build/website/js/", }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: [ ["@babel/preset-env", { targets: "defaults" }], "@babel/preset-react" ], plugins: ["@babel/plugin-transform-runtime"] }, }, }, { test: /\.module\.scss$/, use: [ isProduction ? MiniCssExtractPlugin.loader : "style-loader", { loader: "css-loader", options: { modules: { localIdentName: isProduction ? "[hash:base64:8]" : "[name]__[local]___[hash:base64:5]", }, sourceMap: !isProduction, }, }, { loader: "sass-loader", options: { sourceMap: !isProduction } }, ], }, { test: /\.scss$/, exclude: /\.module\.scss$/, use: [ isProduction ? MiniCssExtractPlugin.loader : "style-loader", { loader: "css-loader", options: { sourceMap: !isProduction } }, { loader: "sass-loader", options: { sourceMap: !isProduction } } ], }, ], }, resolve: { extensions: [".js", ".jsx"], }, plugins: [ new ESLintPlugin({ emitWarning: true, failOnError: true, extensions: ["js", "jsx"], }), new webpack.DefinePlugin({ BUILD: JSON.stringify( `BUILD: ${date.getDate()}.${date.getMonth() + 1}.${date.getHours()}.${date.getMinutes()}` ), "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV), }), ...(isProduction ? [new MiniCssExtractPlugin({ filename: "../css/[name].[contenthash].css" })] : []), ], devtool: isProduction ? "source-map" : "eval-cheap-module-source-map", }; };
三、React组件实现与Twig挂载
- 编写React组件
创建js/components/InteractiveComponent.jsx:
import React, { useState } from 'react'; import styles from './InteractiveComponent.module.scss'; const InteractiveComponent = ({ initialCount }) => { const [count, setCount] = useState(initialCount || 0); return ( <div className={styles.container}> <p>当前计数:{count}</p> <button onClick={() => setCount(count + 1)} className={styles.button}> 点击增加 </button> </div> ); }; export default InteractiveComponent;
- 入口文件挂载逻辑
修改js/main.js,添加组件挂载逻辑:
import React from 'react'; import { createRoot } from 'react-dom/client'; import InteractiveComponent from './components/InteractiveComponent'; // 批量挂载页面中的React组件 document.querySelectorAll('[data-react-component]').forEach(el => { const componentName = el.dataset.reactComponent; const props = JSON.parse(el.dataset.props || '{}'); if (componentName === 'InteractiveComponent') { const root = createRoot(el); root.render(<InteractiveComponent {...props} />); } });
- Twig模板预留容器
在需要插入React组件的Twig模板中添加挂载容器:
<div data-react-component="InteractiveComponent" data-props='{"initialCount": 5}' ></div>
四、状态管理方案
针对高交互组件的状态需求,可选择:
- Redux Toolkit:适合复杂状态场景,安装
@reduxjs/toolkit react-redux后,按常规方式配置Store和Provider即可 - React Context API:轻量级状态共享方案,无需额外依赖,直接使用
createContext和useContext实现跨组件状态传递
五、常见问题排查
- Webpack报错"path is not defined":确保配置文件顶部引入
const path = require('path') - React组件不渲染:检查Twig容器的
data-react-component属性与main.js中的匹配关系,查看浏览器控制台报错信息 - 样式不生效:确认SCSS文件导入路径正确,模块样式需通过
styles.xxx方式引用类名 - 生产环境构建异常:检查所有依赖是否安装完整,尤其是
mini-css-extract-plugin这类生产环境专用插件
内容的提问来源于stack exchange,提问作者coral fish
相关产品推荐
相关产品推荐

