React中拦截console.log替换为自定义函数(结合eval()开发Codepen替代工具)
在React中拦截console.log并实现自定义容器输出(适配eval场景)
要实现类似Codepen的代码运行器,核心是重写console相关方法,并确保eval执行的代码能调用到自定义的输出逻辑,以下是具体实现方案:
核心思路
- 在React组件中维护输出消息的状态,用于渲染到自定义div容器
- 重写全局
console的方法(或在代码执行上下文注入自定义console),将原本的控制台输出转为组件内的状态更新 - 处理代码执行时的错误,统一输出到自定义容器
完整实现代码
import { useState, useEffect } from 'react'; function CodePlayground() { const [outputMessages, setOutputMessages] = useState([]); const [codeInput, setCodeInput] = useState('console.log("Hello from playground!");'); // 保存原生console方法,防止全局污染后无法恢复 const originalConsole = { log: console.log, error: console.error, warn: console.warn, info: console.info }; // 自定义日志处理函数 const handleLog = (type, ...args) => { // 将任意参数转为可展示的字符串格式 const formattedMsg = args.map(arg => { if (typeof arg === 'object') return JSON.stringify(arg, null, 2); return String(arg); }).join(' '); setOutputMessages(prev => [...prev, { type, message: formattedMsg }]); // 可选:保留原生控制台输出,方便调试 originalConsole[type](...args); }; useEffect(() => { // 重写全局console方法 console.log = (...args) => handleLog('log', ...args); console.error = (...args) => handleLog('error', ...args); console.warn = (...args) => handleLog('warn', ...args); console.info = (...args) => handleLog('info', ...args); // 组件卸载时恢复原生console return () => { Object.assign(console, originalConsole); }; }, []); // 执行用户输入的代码 const runCode = () => { setOutputMessages([]); try { // eval会直接使用全局已重写的console eval(codeInput); } catch (err) { // 捕获代码执行错误并输出 handleLog('error', `Execution Error: ${err.message}`); } }; return ( <div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}> <textarea value={codeInput} onChange={(e) => setCodeInput(e.target.value)} rows={12} style={{ width: '100%', padding: '12px', border: '1px solid #ddd', borderRadius: '4px', fontSize: '14px' }} placeholder="输入代码,比如console.log('测试输出');" /> <button onClick={runCode} style={{ marginTop: '10px', padding: '10px 20px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }} > 运行代码 </button> <div style={{ marginTop: '20px', border: '1px solid #ddd', borderRadius: '4px', padding: '15px', backgroundColor: '#f8f9fa' }}> <h4 style={{ margin: '0 0 10px 0', color: '#333' }}>输出结果:</h4> {outputMessages.length === 0 ? ( <p style={{ color: '#666' }}>点击运行代码查看输出</p> ) : ( outputMessages.map((msg, idx) => { const textColor = { error: '#dc3545', warn: '#ffc107', info: '#17a2b8', log: '#28a745' }[msg.type]; return ( <div key={idx} style={{ marginBottom: '8px', color: textColor }}> <span style={{ fontWeight: 'bold' }}>{msg.type.toUpperCase()}:</span> {msg.message} </div> ); }) )} </div> </div> ); } export default CodePlayground;
关键细节说明
1. 避免全局console污染
组件挂载时重写全局console,卸载时通过保存的originalConsole恢复,防止影响其他组件或应用的正常日志输出。
2. 参数格式化处理
针对对象、数组等复杂类型,使用JSON.stringify转为格式化后的字符串,确保在div容器中能正常展示。
3. 错误捕获
用try-catch包裹eval执行过程,将代码语法错误、运行时错误统一输出到自定义容器,提升用户体验。
4. 更安全的替代eval方案
如果不想污染全局console,可以使用new Function手动注入自定义console上下文,实现隔离执行:
const runCode = () => { setOutputMessages([]); try { // 创建自定义console,仅用于当前代码执行 const customConsole = { log: (...args) => handleLog('log', ...args), error: (...args) => handleLog('error', ...args), warn: (...args) => handleLog('warn', ...args), info: (...args) => handleLog('info', ...args) }; // 用new Function替代eval,传入自定义console作为参数 const executeCode = new Function('console', codeInput); executeCode(customConsole); } catch (err) { handleLog('error', `Execution Error: ${err.message}`); } };
这种方式更适合多实例场景,不会影响全局环境的console使用。
内容的提问来源于stack exchange,提问作者Rajat Sandeep
相关产品推荐
相关产品推荐

