Skip to content

React 框架与原生网页的区别

概述

本章将深入对比 React 框架与原生 JavaScript 开发的区别,帮助您理解为什么选择 React,以及各自的优缺点和适用场景。

🔄 开发方式对比

原生 JavaScript 开发

html
<!DOCTYPE html>
<html>
<head>
    <title>原生 JavaScript 待办事项</title>
    <style>
        .container { max-width: 600px; margin: 0 auto; padding: 20px; }
        .todo-item { padding: 10px; border: 1px solid #ddd; margin: 5px 0; }
        .completed { text-decoration: line-through; opacity: 0.6; }
    </style>
</head>
<body>
    <div class="container">
        <h1>待办事项</h1>
        <form id="todoForm">
            <input type="text" id="todoInput" placeholder="添加新任务..." required>
            <button type="submit">添加</button>
        </form>
        <div id="todoList"></div>
        <div id="stats"></div>
    </div>

    <script>
        // 全局状态
        let todos = [];
        let nextId = 1;

        // DOM 元素引用
        const todoForm = document.getElementById('todoForm');
        const todoInput = document.getElementById('todoInput');
        const todoList = document.getElementById('todoList');
        const stats = document.getElementById('stats');

        // 添加事件监听器
        todoForm.addEventListener('submit', handleAddTodo);

        function handleAddTodo(e) {
            e.preventDefault();
            const text = todoInput.value.trim();
            if (text) {
                addTodo(text);
                todoInput.value = '';
            }
        }

        function addTodo(text) {
            const todo = {
                id: nextId++,
                text: text,
                completed: false
            };
            todos.push(todo);
            renderTodos();
            renderStats();
        }

        function toggleTodo(id) {
            todos = todos.map(todo =>
                todo.id === id ? { ...todo, completed: !todo.completed } : todo
            );
            renderTodos();
            renderStats();
        }

        function deleteTodo(id) {
            todos = todos.filter(todo => todo.id !== id);
            renderTodos();
            renderStats();
        }

        function renderTodos() {
            todoList.innerHTML = '';
            todos.forEach(todo => {
                const todoElement = document.createElement('div');
                todoElement.className = `todo-item ${todo.completed ? 'completed' : ''}`;
                todoElement.innerHTML = `
                    <input type="checkbox" ${todo.completed ? 'checked' : ''} 
                           onchange="toggleTodo(${todo.id})">
                    <span>${todo.text}</span>
                    <button onclick="deleteTodo(${todo.id})" style="float: right;">删除</button>
                `;
                todoList.appendChild(todoElement);
            });
        }

        function renderStats() {
            const total = todos.length;
            const completed = todos.filter(t => t.completed).length;
            const active = total - completed;
            stats.innerHTML = `总计: ${total} | 已完成: ${completed} | 进行中: ${active}`;
        }

        // 初始渲染
        renderTodos();
        renderStats();
    </script>
</body>
</html>

React 开发方式

jsx
function ReactTodoApp() {
    const [todos, setTodos] = React.useState([]);
    const [inputValue, setInputValue] = React.useState('');
    const [nextId, setNextId] = React.useState(1);
    
    const addTodo = (text) => {
        if (text.trim()) {
            setTodos(prev => [...prev, {
                id: nextId,
                text: text.trim(),
                completed: false
            }]);
            setNextId(prev => prev + 1);
            setInputValue('');
        }
    };
    
    const toggleTodo = (id) => {
        setTodos(prev => prev.map(todo =>
            todo.id === id ? { ...todo, completed: !todo.completed } : todo
        ));
    };
    
    const deleteTodo = (id) => {
        setTodos(prev => prev.filter(todo => todo.id !== id));
    };
    
    const handleSubmit = (e) => {
        e.preventDefault();
        addTodo(inputValue);
    };
    
    const stats = {
        total: todos.length,
        completed: todos.filter(t => t.completed).length,
        active: todos.filter(t => !t.completed).length
    };
    
    return (
        <div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
            <h1>React 待办事项</h1>
            
            <form onSubmit={handleSubmit}>
                <input
                    type="text"
                    value={inputValue}
                    onChange={(e) => setInputValue(e.target.value)}
                    placeholder="添加新任务..."
                    required
                />
                <button type="submit">添加</button>
            </form>
            
            <div>
                {todos.map(todo => (
                    <div 
                        key={todo.id} 
                        style={{
                            padding: '10px',
                            border: '1px solid #ddd',
                            margin: '5px 0',
                            textDecoration: todo.completed ? 'line-through' : 'none',
                            opacity: todo.completed ? 0.6 : 1
                        }}
                    >
                        <input
                            type="checkbox"
                            checked={todo.completed}
                            onChange={() => toggleTodo(todo.id)}
                        />
                        <span>{todo.text}</span>
                        <button 
                            onClick={() => deleteTodo(todo.id)}
                            style={{ float: 'right' }}
                        >
                            删除
                        </button>
                    </div>
                ))}
            </div>
            
            <div>
                总计: {stats.total} | 已完成: {stats.completed} | 进行中: {stats.active}
            </div>
        </div>
    );
}

📊 详细对比分析

代码复杂度对比

jsx
function ComparisonExample() {
    const [showVanilla, setShowVanilla] = React.useState(false);
    
    const comparisons = [
        {
            aspect: '代码量',
            vanilla: '约 100+ 行 HTML/CSS/JS',
            react: '约 60 行 JSX',
            advantage: 'React'
        },
        {
            aspect: '状态管理',
            vanilla: '手动管理全局变量',
            react: 'useState Hook 自动管理',
            advantage: 'React'
        },
        {
            aspect: 'DOM 操作',
            vanilla: '手动 createElement/innerHTML',
            react: '声明式 JSX,自动更新',
            advantage: 'React'
        },
        {
            aspect: '事件处理',
            vanilla: 'addEventListener/onclick',
            react: '组件内事件处理',
            advantage: 'React'
        },
        {
            aspect: '代码复用',
            vanilla: '函数复用,较困难',
            react: '组件化,高度复用',
            advantage: 'React'
        },
        {
            aspect: '学习成本',
            vanilla: '基础 Web 技术',
            react: '需要学习 React 生态',
            advantage: 'Vanilla'
        },
        {
            aspect: '性能',
            vanilla: '直接操作 DOM',
            react: '虚拟 DOM + 优化',
            advantage: 'Depends'
        },
        {
            aspect: '包大小',
            vanilla: '无额外依赖',
            react: '约 40KB (gzipped)',
            advantage: 'Vanilla'
        }
    ];
    
    return (
        <div style={{ padding: '20px' }}>
            <h2>React vs 原生 JavaScript 对比</h2>
            
            <div style={{ overflowX: 'auto' }}>
                <table style={{ width: '100%', borderCollapse: 'collapse' }}>
                    <thead>
                        <tr style={{ backgroundColor: '#f8f9fa' }}>
                            <th style={{ padding: '12px', border: '1px solid #ddd' }}>对比维度</th>
                            <th style={{ padding: '12px', border: '1px solid #ddd' }}>原生 JavaScript</th>
                            <th style={{ padding: '12px', border: '1px solid #ddd' }}>React</th>
                            <th style={{ padding: '12px', border: '1px solid #ddd' }}>优势</th>
                        </tr>
                    </thead>
                    <tbody>
                        {comparisons.map((item, index) => (
                            <tr key={index}>
                                <td style={{ padding: '12px', border: '1px solid #ddd', fontWeight: 'bold' }}>
                                    {item.aspect}
                                </td>
                                <td style={{ padding: '12px', border: '1px solid #ddd' }}>
                                    {item.vanilla}
                                </td>
                                <td style={{ padding: '12px', border: '1px solid #ddd' }}>
                                    {item.react}
                                </td>
                                <td style={{ 
                                    padding: '12px', 
                                    border: '1px solid #ddd',
                                    backgroundColor: item.advantage === 'React' ? '#d4edda' : 
                                                   item.advantage === 'Vanilla' ? '#fff3cd' : '#f8f9fa',
                                    fontWeight: 'bold'
                                }}>
                                    {item.advantage}
                                </td>
                            </tr>
                        ))}
                    </tbody>
                </table>
            </div>
        </div>
    );
}

🎯 适用场景分析

原生 JavaScript 适用场景

jsx
function VanillaJSScenarios() {
    const scenarios = [
        {
            title: '简单的静态网站',
            description: '企业官网、个人博客等内容展示型网站',
            pros: ['快速开发', '无框架依赖', 'SEO 友好'],
            example: '公司官网的联系表单'
        },
        {
            title: '小型交互功能',
            description: '页面上的简单交互效果和动画',
            pros: ['轻量级', '直接控制', '性能好'],
            example: '图片轮播、下拉菜单、模态框'
        },
        {
            title: '现有项目的功能增强',
            description: '为已有的非 SPA 网站添加交互功能',
            pros: ['渐进增强', '无需重构', '兼容性好'],
            example: '表单验证、AJAX 加载更多'
        },
        {
            title: '性能敏感的应用',
            description: '对包大小和性能要求极高的场景',
            pros: ['零依赖', '完全控制', '最小化'],
            example: '移动端 H5 页面、嵌入式页面'
        }
    ];
    
    return (
        <div style={{ padding: '20px' }}>
            <h2>原生 JavaScript 适用场景</h2>
            <div style={{ display: 'grid', gap: '20px' }}>
                {scenarios.map((scenario, index) => (
                    <div key={index} style={{
                        border: '1px solid #ddd',
                        borderRadius: '8px',
                        padding: '20px',
                        backgroundColor: '#fff8dc'
                    }}>
                        <h3 style={{ margin: '0 0 10px 0', color: '#d63384' }}>
                            {scenario.title}
                        </h3>
                        <p style={{ margin: '0 0 15px 0', color: '#666' }}>
                            {scenario.description}
                        </p>
                        <div style={{ marginBottom: '15px' }}>
                            <strong>优势:</strong>
                            <ul style={{ margin: '5px 0 0 20px' }}>
                                {scenario.pros.map((pro, i) => (
                                    <li key={i}>{pro}</li>
                                ))}
                            </ul>
                        </div>
                        <div style={{ 
                            backgroundColor: '#f8f9fa', 
                            padding: '10px', 
                            borderRadius: '4px',
                            fontStyle: 'italic'
                        }}>
                            <strong>示例:</strong> {scenario.example}
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

React 适用场景

jsx
function ReactScenarios() {
    const scenarios = [
        {
            title: '复杂的单页应用 (SPA)',
            description: '具有复杂状态管理和用户交互的应用',
            pros: ['组件化', '状态管理', '生态丰富'],
            example: '后台管理系统、在线编辑器'
        },
        {
            title: '数据驱动的应用',
            description: '需要频繁更新 UI 的数据密集型应用',
            pros: ['虚拟 DOM', '自动更新', '性能优化'],
            example: '实时图表、股票交易界面'
        },
        {
            title: '团队协作项目',
            description: '多人开发,需要代码复用和维护的项目',
            pros: ['组件复用', '规范开发', '易维护'],
            example: '企业级 Web 应用、电商平台'
        },
        {
            title: '快速原型开发',
            description: '需要快速验证想法和功能的项目',
            pros: ['开发效率高', '组件库丰富', '快速迭代'],
            example: 'MVP 产品、概念验证'
        }
    ];
    
    return (
        <div style={{ padding: '20px' }}>
            <h2>React 适用场景</h2>
            <div style={{ display: 'grid', gap: '20px' }}>
                {scenarios.map((scenario, index) => (
                    <div key={index} style={{
                        border: '1px solid #ddd',
                        borderRadius: '8px',
                        padding: '20px',
                        backgroundColor: '#e7f3ff'
                    }}>
                        <h3 style={{ margin: '0 0 10px 0', color: '#0066cc' }}>
                            {scenario.title}
                        </h3>
                        <p style={{ margin: '0 0 15px 0', color: '#666' }}>
                            {scenario.description}
                        </p>
                        <div style={{ marginBottom: '15px' }}>
                            <strong>优势:</strong>
                            <ul style={{ margin: '5px 0 0 20px' }}>
                                {scenario.pros.map((pro, i) => (
                                    <li key={i}>{pro}</li>
                                ))}
                            </ul>
                        </div>
                        <div style={{ 
                            backgroundColor: '#f8f9fa', 
                            padding: '10px', 
                            borderRadius: '4px',
                            fontStyle: 'italic'
                        }}>
                            <strong>示例:</strong> {scenario.example}
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

🔄 迁移策略

从原生到 React 的迁移

jsx
function MigrationStrategy() {
    const [currentStep, setCurrentStep] = React.useState(0);
    
    const migrationSteps = [
        {
            title: '1. 评估现有代码',
            content: '分析现有功能模块,识别可以组件化的部分',
            code: `
// 识别重复的 DOM 操作
function createUserCard(user) {
    const card = document.createElement('div');
    card.innerHTML = \`
        <h3>\${user.name}</h3>
        <p>\${user.email}</p>
    \`;
    return card;
}
// 👆 这种重复模式适合组件化
            `
        },
        {
            title: '2. 渐进式引入 React',
            content: '在页面的某个区域开始使用 React',
            code: `
// 在现有页面中挂载 React 组件
const userListContainer = document.getElementById('user-list');
const root = ReactDOM.createRoot(userListContainer);
root.render(<UserList />);
            `
        },
        {
            title: '3. 状态管理迁移',
            content: '将全局状态逐步迁移到 React 状态管理',
            code: `
// 原生: 全局变量
let users = [];

// React: useState
function UserManager() {
    const [users, setUsers] = useState([]);
    // ...
}
            `
        },
        {
            title: '4. 事件系统迁移',
            content: '将事件监听器迁移到 React 事件系统',
            code: `
// 原生: addEventListener
button.addEventListener('click', handleClick);

// React: JSX 事件属性
<button onClick={handleClick}>Click</button>
            `
        },
        {
            title: '5. 完整重构',
            content: '将整个应用重构为 React SPA',
            code: `
// 完整的 React 应用结构
function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<Home />} />
                <Route path="/users" element={<UserList />} />
            </Routes>
        </BrowserRouter>
    );
}
            `
        }
    ];
    
    return (
        <div style={{ padding: '20px' }}>
            <h2>迁移策略:从原生 JavaScript 到 React</h2>
            
            <div style={{ display: 'flex', marginBottom: '20px' }}>
                {migrationSteps.map((_, index) => (
                    <button
                        key={index}
                        onClick={() => setCurrentStep(index)}
                        style={{
                            padding: '10px 15px',
                            margin: '0 5px',
                            backgroundColor: currentStep === index ? '#007bff' : '#f8f9fa',
                            color: currentStep === index ? 'white' : '#333',
                            border: '1px solid #ddd',
                            borderRadius: '4px',
                            cursor: 'pointer'
                        }}
                    >
                        步骤 {index + 1}
                    </button>
                ))}
            </div>
            
            <div style={{
                border: '1px solid #ddd',
                borderRadius: '8px',
                padding: '20px',
                backgroundColor: '#f8f9fa'
            }}>
                <h3>{migrationSteps[currentStep].title}</h3>
                <p>{migrationSteps[currentStep].content}</p>
                <pre style={{
                    backgroundColor: '#282c34',
                    color: '#abb2bf',
                    padding: '15px',
                    borderRadius: '4px',
                    overflow: 'auto'
                }}>
                    {migrationSteps[currentStep].code.trim()}
                </pre>
            </div>
        </div>
    );
}

📝 决策指南

技术选择决策树

jsx
function TechDecisionTree() {
    const [answers, setAnswers] = React.useState({});
    
    const questions = [
        {
            id: 'complexity',
            question: '项目复杂度如何?',
            options: [
                { value: 'simple', label: '简单(静态页面或简单交互)', weight: { vanilla: 3, react: 1 } },
                { value: 'medium', label: '中等(多个交互模块)', weight: { vanilla: 2, react: 2 } },
                { value: 'complex', label: '复杂(复杂状态管理)', weight: { vanilla: 1, react: 3 } }
            ]
        },
        {
            id: 'team',
            question: '团队规模和经验?',
            options: [
                { value: 'solo', label: '个人项目', weight: { vanilla: 2, react: 2 } },
                { value: 'small', label: '小团队(2-5人)', weight: { vanilla: 2, react: 3 } },
                { value: 'large', label: '大团队(5+人)', weight: { vanilla: 1, react: 3 } }
            ]
        },
        {
            id: 'timeline',
            question: '开发时间要求?',
            options: [
                { value: 'urgent', label: '非常紧急(1-2周)', weight: { vanilla: 3, react: 1 } },
                { value: 'normal', label: '正常(1-3个月)', weight: { vanilla: 2, react: 3 } },
                { value: 'long', label: '长期项目(3个月+)', weight: { vanilla: 1, react: 3 } }
            ]
        },
        {
            id: 'maintenance',
            question: '维护和扩展需求?',
            options: [
                { value: 'minimal', label: '很少维护', weight: { vanilla: 3, react: 1 } },
                { value: 'moderate', label: '定期更新', weight: { vanilla: 2, react: 2 } },
                { value: 'frequent', label: '频繁迭代', weight: { vanilla: 1, react: 3 } }
            ]
        }
    ];
    
    const calculateRecommendation = () => {
        let vanillaScore = 0;
        let reactScore = 0;
        
        Object.values(answers).forEach(answer => {
            if (answer) {
                vanillaScore += answer.weight.vanilla;
                reactScore += answer.weight.react;
            }
        });
        
        if (vanillaScore > reactScore) {
            return {
                recommendation: 'Vanilla JavaScript',
                reason: '基于您的需求,原生 JavaScript 更适合您的项目',
                color: '#ffc107'
            };
        } else if (reactScore > vanillaScore) {
            return {
                recommendation: 'React',
                reason: '基于您的需求,React 框架更适合您的项目',
                color: '#007bff'
            };
        } else {
            return {
                recommendation: '两者皆可',
                reason: '您的项目适合两种技术,可根据团队熟悉度选择',
                color: '#28a745'
            };
        }
    };
    
    const handleAnswerChange = (questionId, option) => {
        setAnswers(prev => ({
            ...prev,
            [questionId]: option
        }));
    };
    
    const result = Object.keys(answers).length === questions.length ? calculateRecommendation() : null;
    
    return (
        <div style={{ padding: '20px' }}>
            <h2>技术选择决策工具</h2>
            
            {questions.map(question => (
                <div key={question.id} style={{ marginBottom: '25px' }}>
                    <h3>{question.question}</h3>
                    <div>
                        {question.options.map(option => (
                            <label key={option.value} style={{
                                display: 'block',
                                margin: '8px 0',
                                padding: '10px',
                                border: '1px solid #ddd',
                                borderRadius: '4px',
                                cursor: 'pointer',
                                backgroundColor: answers[question.id]?.value === option.value ? '#e7f3ff' : '#f8f9fa'
                            }}>
                                <input
                                    type="radio"
                                    name={question.id}
                                    value={option.value}
                                    checked={answers[question.id]?.value === option.value}
                                    onChange={() => handleAnswerChange(question.id, option)}
                                    style={{ marginRight: '10px' }}
                                />
                                {option.label}
                            </label>
                        ))}
                    </div>
                </div>
            ))}
            
            {result && (
                <div style={{
                    padding: '20px',
                    backgroundColor: result.color + '20',
                    border: `2px solid ${result.color}`,
                    borderRadius: '8px',
                    marginTop: '30px'
                }}>
                    <h2 style={{ color: result.color, margin: '0 0 10px 0' }}>
                        推荐技术:{result.recommendation}
                    </h2>
                    <p style={{ margin: 0, fontSize: '16px' }}>{result.reason}</p>
                </div>
            )}
        </div>
    );
}

📊 主要区别

  • ✅ 开发方式:命令式 vs 声明式
  • ✅ 状态管理:手动 vs 自动
  • ✅ DOM 操作:直接操作 vs 虚拟 DOM
  • ✅ 代码组织:函数 vs 组件化

🎯 选择原则

  1. 项目复杂度:简单项目用原生,复杂项目用 React
  2. 团队规模:大团队更适合 React 的标准化
  3. 维护需求:长期维护项目选择 React
  4. 性能要求:极致性能可能需要原生优化
  5. 学习成本:考虑团队的技术储备

🔄 迁移建议

  • 渐进式引入:不必全盘重写
  • 评估收益:确保迁移带来的好处大于成本
  • 团队培训:确保团队掌握新技术
  • 风险控制:在小模块试点后再大规模应用

React 和原生 JavaScript 各有优势,选择合适的技术栈是项目成功的关键因素之一。

本站内容仅供学习和研究使用。