为什么 React 如此简单:5分钟理解核心概念,快速上手开发

为什么选择 React?

1. 简单直观的更新机制

Vue 和 React 都是优秀的前端框架,各有特色:

  • Vue 在国内更受欢迎,很大程度上是因为它延续了传统的 HTML 开发方式,对从传统 Web 开发转型的开发者更友好
  • React 则采用了更现代的函数式编程思想,其更新机制更加直观:函数重新执行 = 组件重新渲染
  • 这种函数式的更新机制让 React 的学习曲线更加平缓,只需要理解几个核心概念:
    • 组件就是函数
    • 状态变化触发函数重新执行
    • 函数重新执行 = 重新渲染
    • 只需要掌握几个核心 Hooks 就能开始开发

2. TypeScript 的完美支持

React 与 TypeScript 的配合堪称完美:

tsx 复制代码
// 完整的类型推导
interface Props {
  name: string;
  age: number;
  onUpdate: (newAge: number) => void;
}

function UserProfile({ name, age, onUpdate }: Props) {
  // 编辑器会提供完整的类型提示
  const handleClick = () => {
    onUpdate(age + 1);
  };

  return (
    <div>
      <h1>{name}</h1>
      <p>年龄: {age}</p>
      <button onClick={handleClick}>增加年龄</button>
    </div>
  );
}

优势:

  • 组件 Props 类型检查
  • Hooks 类型推导
  • 事件处理函数类型安全
  • 状态管理类型安全
  • 减少运行时错误
  • 提高开发效率,减少查文档时间

3. 开发效率对比

不同框架的学习路径:

rust 复制代码
Vue 学习路径:
HTML 基础 -> 模板语法 -> 响应式原理 -> 计算属性 -> 组件通信 -> 状态管理

React 学习路径:
JavaScript 基础 -> 函数式编程 -> 核心 Hooks -> 开始开发

React 的核心优势:

  1. 更新机制简单直观

    • 函数重新执行就是重新渲染
    • 不需要理解复杂的响应式原理
    • 状态变化直接触发更新
  2. 核心 Hooks 记忆点:

tsx 复制代码
// 1. 状态管理
const [state, setState] = useState<Type>(initialValue);

// 2. 副作用处理
useEffect(() => {
  // 副作用代码
  return () => {
    // 清理代码
  };
}, [依赖项]);

// 3. 性能优化
const memoizedValue = useMemo(() => computeValue(), [依赖项]);
const memoizedCallback = useCallback(() => handleEvent(), [依赖项]);

// 4. 引用保持
const ref = useRef<Type>(initialValue);

简单介绍

React 是一个用于构建用户界面的 JavaScript 库,它的设计理念深受函数式编程的影响。React 将 UI 拆分为独立的、可复用的组件,每个组件都是一个纯函数,接收 props 并返回要渲染的内容。

函数式编程在 React 中的应用

1. 纯函数

React 组件本质上就是纯函数:

jsx 复制代码
// 纯函数组件
function Welcome(props) {
  return <h1>你好, {props.name}</h1>;
}

纯函数的特点:

  • 相同的输入总是产生相同的输出
  • 没有副作用
  • 不依赖外部状态

2. 不可变性

React 强调数据的不可变性:

jsx 复制代码
// 错误的方式
const obj = { count: 1 };
obj.count = 2; // 直接修改

// 正确的方式
const newObj = { ...obj, count: 2 }; // 创建新对象

React 核心机制

1. 函数式更新机制

React 的核心更新机制可以理解为:

rust 复制代码
函数执行 -> 状态变化 -> 重新渲染

关键点:

  • 组件本质上是一个函数
  • 函数内的任何状态变化都会触发重新执行
  • 每次执行都会创建新的虚拟 DOM
  • 通过对比新旧虚拟 DOM 来决定实际更新

2. 状态管理与副作用

React 的核心理念是:函数内部任何变量的变化都会触发更新机制。这带来了两个主要问题:

  1. 性能问题:频繁更新导致性能下降
  2. 副作用问题:某些操作不应该在每次更新时都执行

解决方案:Hooks 机制

jsx 复制代码
function Component() {
  // 1. 状态管理
  const [count, setCount] = useState(0);

  // 2. 副作用处理
  useEffect(() => {
    // 这里的代码不会在每次渲染时都执行
    // 只有在依赖项变化时才会执行
  }, [count]);

  // 3. 记忆化
  const memoizedValue = useMemo(() => {
    // 复杂计算的结果会被缓存
    return expensiveComputation(count);
  }, [count]);
}

3. 更新流程详解

scss 复制代码
组件函数执行
    ↓
创建/更新状态 (useState)
    ↓
执行副作用 (useEffect)
    ↓
生成虚拟 DOM
    ↓
对比新旧虚拟 DOM
    ↓
更新真实 DOM

4. 为什么需要 Hooks?

  1. 解决副作用问题
jsx 复制代码
// 没有 Hooks 时的问题
function Component() {
  const data = fetchData(); // 每次渲染都会执行
  return <div>{data}</div>;
}

// 使用 Hooks 后
function Component() {
  const [data, setData] = useState(null);
  useEffect(() => {
    fetchData().then(setData);
  }, []); // 只在组件挂载时执行一次
  return <div>{data}</div>;
}
  1. 状态隔离
jsx 复制代码
function Component() {
  // 状态被隔离在 Hooks 中
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');

  // 这些状态的变化不会相互影响
  // 每个 Hook 都有自己的更新机制
}
  1. 性能优化
jsx 复制代码
function ExpensiveComponent() {
  const [count, setCount] = useState(0);

  // 使用 useMemo 避免重复计算
  const expensiveValue = useMemo(() => {
    return complexCalculation(count);
  }, [count]);

  // 使用 useCallback 避免函数重新创建
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);
}

更新机制详解

1. React 的更新原理

想象你在画一幅画:

  • 每次需要修改时,你不是直接擦掉重画
  • 而是先画一张新的草稿,对比新旧两张画
  • 只修改真正需要改变的部分

React 的更新机制就是这样:

rust 复制代码
数据变化 -> 生成新的虚拟DOM -> 对比新旧差异 -> 只更新需要改变的部分

2. 触发更新的常见情况

就像画画的场景:

  • 你修改了画的内容(状态变化)
  • 别人给了你新的参考图(props 变化)
  • 你决定重新画一遍(强制更新)

3. 避免更新陷阱

就像画画时要注意的问题:

  1. 不要一直重画
jsx 复制代码
function BadComponent() {
  const [count, setCount] = useState(0);
  // 错误示范:每次渲染都改变状态
  setCount(count + 1);
  return <div>{count}</div>;
}
  1. 只在需要时更新
jsx 复制代码
function GoodComponent() {
  const [count, setCount] = useState(0);
  // 正确示范:只在点击时更新
  return (
    <button onClick={() => setCount(count + 1)}>
      点击次数:{count}
    </button>
  );
}

4. 小心无限刷新的陷阱

就像画画时陷入的循环:

  • 你画了一笔,觉得不对
  • 擦掉重画,还是不对
  • 又擦掉重画...陷入死循环

React 中常见的无限刷新问题:

  1. 在渲染时直接修改状态
jsx 复制代码
function BadComponent() {
  const [count, setCount] = useState(0);
  // 错误:每次渲染都会触发新的渲染
  setCount(count + 1);
  return <div>{count}</div>;
}
  1. useEffect 依赖项设置不当
jsx 复制代码
function BadEffect() {
  const [data, setData] = useState({});
  // 错误:data 变化会触发 effect,effect 又会改变 data
  useEffect(() => {
    setData({ ...data, new: 'value' });
  }, [data]); // 错误:把 data 作为依赖项
}
  1. 正确的处理方式
jsx 复制代码
function GoodEffect() {
  const [data, setData] = useState({});
  // 正确:只在组件挂载时执行一次
  useEffect(() => {
    setData({ new: 'value' });
  }, []); // 空依赖数组,只在挂载时执行
}

记住:React 的更新是"单向流动"的,就像水流一样,不能形成循环。

常用 Hooks 详解

1. useState

  • 用于管理组件内部状态
  • 每次更新都会创建新的状态值
  • 异步更新,多个 setState 会合并
jsx 复制代码
const [state, setState] = useState(initialValue);

2. useEffect

  • 处理副作用(数据获取、订阅、手动修改 DOM)
  • 依赖项数组控制执行时机
  • 清理函数在组件卸载或重新执行前运行
jsx 复制代码
useEffect(() => {
  // 副作用代码
  return () => {
    // 清理代码
  };
}, [依赖项]);

3. useCallback

  • 缓存函数引用
  • 避免不必要的子组件重渲染
  • 依赖项变化时才会创建新函数
jsx 复制代码
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

4. useMemo

  • 缓存计算结果
  • 避免重复计算
  • 依赖项变化时重新计算
jsx 复制代码
const memoizedValue = useMemo(
  () => computeExpensiveValue(a, b),
  [a, b]
);

最佳实践

  1. 合理使用依赖项
jsx 复制代码
// 好的实践
useEffect(() => {
  const id = setInterval(() => {
    setCount(c => c + 1);
  }, 1000);
  return () => clearInterval(id);
}, []); // 空依赖数组,只在挂载时执行
  1. 避免不必要的重渲染
jsx 复制代码
// 使用 React.memo 包装纯展示组件
const MemoizedComponent = React.memo(function MyComponent(props) {
  return <div>{props.value}</div>;
});
  1. 状态提升
jsx 复制代码
// 将共享状态提升到最近的共同父组件
function Parent() {
  const [sharedState, setSharedState] = useState();
  return (
    <>
      <ChildA state={sharedState} />
      <ChildB state={sharedState} />
    </>
  );
}

总结

React 的核心在于:

  1. 组件化开发
  2. 虚拟 DOM 和 Diff 算法
  3. 单向数据流
  4. Hooks 机制

理解这些核心概念,就能更好地使用 React 进行开发,避免常见问题,写出更高效的代码。

相关推荐
星河丶1 分钟前
如何将多次提交压缩为一个新的提交
前端
bilibilibiu灬2 分钟前
实现一个web视频动效播放器video-alpha-player
前端·javascript
十盒半价4 分钟前
深入探索 JavaScript:从作用域到闭包的奇妙之旅
前端·javascript·trae
90后的晨仔30 分钟前
RxSwift 中的 DisposeBag解析
前端·ios
蓝胖子的多啦A梦1 小时前
搭建前端项目 Vue+element UI引入 步骤 (超详细)
前端·vue.js·ui
TE-茶叶蛋1 小时前
WebSocket 前端断连原因与检测方法
前端·websocket·网络协议
骆驼Lara1 小时前
前端跨域解决方案(1):什么是跨域?
前端·javascript
离岸听风1 小时前
学生端前端用户操作手册
前端
onebyte8bits1 小时前
CSS Houdini 解锁前端动画的下一个时代!
前端·javascript·css·html·houdini
yxc_inspire1 小时前
基于Qt的app开发第十四天
前端·c++·qt·app·面向对象·qss