React前端开发哲学:5大核心拆解
如果说原生JS是“亲自动手在田间种麦子”jQuery是“用拖拉机种麦子”那么React就是经营一家高度自动化的中央厨房。在以前命令式编程你要做一个菜得亲自写每一步拿个盘子、把肉放进去、开火、肉变色了加盐、盘子满了换个大盘子。你告诉浏览器**“怎么做”**。而React声明式编程是你只需要告诉厨房**“我要吃什么”**厨房做好后会把整桌菜直接端给你。如果加个菜厨房不是把现有的菜倒掉重做而是聪明地只把新菜端上来。这就是React的核心哲学UI f(state)界面是状态的函数。你只管管好数据状态界面React帮你搞定。下面我们用“中央厨房”的生动模型把React的五大基础底盘拆个明明白白。一、组件乐高积木与厨房工作站在React眼里整个页面不是一个完整的画布而是一堆积木拼起来的。组件就是这一个个乐高积木或者厨房里专司其职的工作站切菜站、炒菜站、摆盘站。1. 为什么要有组件如果一个大项目有10万个HTML标签写在一个文件里绝对是灾难。组件让你可以把页面拆成独立的、可复用的小黑盒。App总厨房包含Header出单机和MainContent炒菜区。MainContent包含无数个FoodCard菜品卡片。2. 组件的本质它就是一个普通的JS函数在现代React函数式组件中组件就是这样一个函数function FoodCard({ name }) { // 接收参数 return div classNamecard{name}/div; // 返回界面 }铁律组件名必须大写开头FoodCard不是foodCard。React看到大写开头才知道这是你自己造的积木而不是HTML原生标签。二、JSX在JS里写HTML的“魔法糖”初学React最大的震撼JS里怎么能直接写HTMLconst element h1你好厨房/h1;这叫JSX。它看起来像HTML但骨子里是纯JS。1. 揭开JSX的画皮浏览器根本不认识JSX。上面的代码经过编译器Babel后会变成这样constelementReact.createElement(h1,null,你好厨房);React.createElement会造出一个JS对象虚拟DOM长这样{ type: h1, props: { children: 你好厨房 } }所以JSX只是用看起来像HTML的语法来写生成JS对象的代码。你写的是UI的蓝图而不是真实的DOM。2. JSX的“土规矩”必须有一个根节点因为一个函数只能返回一个对象。如果你不想多包一层无意义的div可以用幽灵节点/Fragment包裹。花括号{}是任意门在JSX的HTML里只有套上{}才能进入JS的世界写变量、函数或表达式。className代替class因为JS里class是保留字ES6类所以JSX里写CSS类名必须用className。三、Props流水线上的传菜单厨房里的各个工作站怎么协作靠传菜单。Props就是从父组件流向子组件的只读数据。1. 单向数据流水往低处流数据像水一样只能从父组件流向子组件通过Props绝不能逆流。// 父组件出单机 function App() { return FoodCard name宫保鸡丁 price{28} /; } // 子组件摆盘站 function FoodCard({ name, price }) { // 解构赋值拿到props return div{name} - ¥{price}/div; }2. Props是绝对只读的纯函数原则子组件绝不允许修改传进来的Props。就像摆盘站不能偷偷把菜单上的“宫保鸡丁”涂改成“炒白菜”。如果一定要改子组件要呼叫父组件让父组件自己改这涉及后面的状态提升。这种严格的单向流让代码的bug追踪变得极其清晰——数据乱了顺着流水线往上找父组件就行。四、State工作站的私人记忆本如果整个厨房全靠传菜单来运转那太死板了。有些临时状态比如切菜站切了多少片肉不需要汇报给总厨房自己记在小本子上就行。State就是组件内部私有的记忆本。1. 普通变量 vs Statelet count 0; // 普通变量 function click() { count; }如果你在组件里用普通变量记数点击后count确实1了但界面不会更新因为React不知道它变了。State的核心超能力只要它一变React就会自动重新渲染刷新这个组件的界面2. 使用State的咒语useStateimport { useState } from react; function Counter() { // 声明一个叫count的记忆本初始值是0setCount是用来更新它的魔法棒 const [count, setCount] useState(0); return ( div p点了{count}份/p button onClick{() setCount(count 1)}再加一份/button /div ); }3. State的暗坑异步与批量更新在React里当你调用setCount时不是立刻改变count的值而是向React提交一个更新请求“喂这个变量待会儿要变成新值请安排重新渲染”。所以千万别这么写setCount(count 1); setCount(count 1);最后只会1。如果要基于旧值更新必须用回调函数写法setCount(prev prev 1);这保证了每次都在最新的值上累加。五、生命周期与Hooks生老病死与副作用组件从出生挂载到页面到更新State/Props变化到死亡从页面移除叫生命周期。在函数组件里我们用Hooks钩子来接入这些生命周期的节点。最核心的是useEffect。1. 什么是副作用在厨房里切菜炒菜是纯函数输入食材输出菜品不污染环境。但如果你要在炒菜时开排风扇、给顾客发微信通知、去隔壁偷调料这些就是“副作用”。在React里跟外部世界网络请求、DOM操作、定时器交互的都叫副作用必须放在useEffect里。2.useEffect随身管家import { useEffect } from react; function FoodCard({ name }) { useEffect(() { // 1. 这里的代码在组件每次渲染完后执行 // 相当于“搬进新家后立刻开通Wi-Fi和燃气” console.log(菜单更新了${name}); // 返回的函数是“清理函数” // 相当于“搬走前必须关水关电退租” return () { console.log(组件要被销毁了清理定时器或取消订阅); }; }, [name]); // 2. 依赖数组只有name变化时才重新执行上面的代码 }3. 依赖数组的黑魔法不写数组useEffect(() {...})每次渲染都执行容易死循环。空数组useEffect(() {...}, [])只在组件第一次出生时执行一次最适合发初始化网络请求。有变量useEffect(() {...}, [a, b])第一次出生执行且当a或b变化时再次执行。六、虚拟DOM与DiffingReact的终极杀器为什么React只让你改数据不让你手动碰DOM因为直接操作真实DOM极其缓慢就像每次换菜都要拆掉厨房重盖。React在内存里建了一个假厨房——虚拟DOMJS对象。当State变化时React先在内存里生成一棵新的虚拟DOM树。Diffing算法React像找茬游戏一样把新旧两棵树进行对比找出最小的差异比如只有某个li里的文字变了。Reconciliation和解React只把那一点差异精准地打到真实的DOM上。结论你每次重新渲染组件React并不是把整个DOM替换掉而是聪明地“微创手术”。这就是为什么React既让你用声明式的方式爽快地写代码又能保持不错的性能。 总结与心智模型口诀把React装进大脑只需要记住这五句话万物皆组件页面是乐高函数是积木。JSX即蓝图看起来像HTML其实是造对象的JS。Props向下流父传子的只读菜单绝不逆流。State本地存组件私密记忆改它就刷新。Effect管杂活发请求加监听离开必清理。当你不再去想“我怎么用JS获取那个按钮然后改变它的文字”而是开始想**“那个按钮的文字应该绑定什么状态当状态变了它自然就变了”**的时候你就真正跨入React的大门了。