王华山 發表於 2023-3-15 09:35:00

2023 年最新最全的 React 面试题

<p>React 作为前端使用最多的框架,必然是面试的重点。我们接下来主要从 React 的使用方式、源码层面和周边生态(如 redux, react-router 等)等几个方便来进行总结。</p>
<h2 id="1-使用方式上">1. 使用方式上</h2>
<p>这里主要考察的是,在开发使用过程中,对 React 框架的了解,如 hook 的不同调用方式得到的结果、函数组件中的 useState 和类组件的 state 的区别等等。</p>
<h3 id="props-的变动是否会引起-state-hook-中数据的变动">props 的变动,是否会引起 state hook 中数据的变动?</h3>
<p>React 组件的 props 变动,会让组件重新执行,但并不会引起 state 的值的变动。state 值的变动,只能由 setState() 来触发。因此若想在 props 变动时,重置 state 的数据,需要监听 props 的变动,如:</p>
<pre><code class="language-javascript">const App = props =&gt; {
const = useState(0);

// 监听 props 的变化,重置 count 的值
useEffect(() =&gt; {
    setCount(0);
}, );

return &lt;div onClick={() =&gt; setCount(count + 1)}&gt;{count}&lt;/div&gt;;
};
</code></pre>
<h3 id="react18-有哪些新变化">React18 有哪些新变化?</h3>
<p>React 的更新都是渐进式的更新,在 React18 中启用的新特性,其实在 v17 中(甚至更早)就埋下了。</p>
<ol>
<li>并发渲染机制:根据用户的设备性能和网速对渲染过程进行适当的调整, 保证 React 应用在长时间的渲染过程中依旧保持可交互性,避免页面出现卡顿或无响应的情况,从而提升用户体验。</li>
<li>新的创建方式:现在是要先通过<code>createRoot()</code>创建一个 root 节点,然后该 root 节点来调用<code>render()</code>方法;</li>
<li>自动批处理优化:批处理: React 将多个状态更新分组到一个重新渲染中以获得更好的性能。(将多次 setstate 事件合并);在 v18 之前只在事件处理函数中实现了批处理,在 v18 中所有更新都将自动批处理,包括 promise 链、setTimeout 等异步代码以及原生事件处理函数;</li>
<li>startTransition:主动降低优先级。比如「搜索引擎的关键词联想」,用户在输入框中的输入希望是实时的,而联想词汇可以稍稍延迟一会儿。我们可以用 startTransition 来降低联想词汇更新的优先级;</li>
<li>useId:主要用于 SSR 服务端渲染的场景,方便在服务端渲染和客户端渲染时,产生唯一的 id;</li>
</ol>
<h3 id="并发模式是如何执行的">并发模式是如何执行的?</h3>
<p>React 中的<code>并发</code>,并不是指同一时刻同时在做多件事情。因为 js 本身就是单线程的(同一时间只能执行一件事情),而且还要跟 UI 渲染竞争主线程。若一个很耗时的任务占据了线程,那么后续的执行内容都会被阻塞。为了避免这种情况,React 就利用 fiber 结构和时间切片的机制,将一个大任务分解成多个小任务,然后按照任务的优先级和线程的占用情况,对任务进行调度。</p>
<ul>
<li>对于每个更新,为其分配一个优先级 lane,用于区分其紧急程度。</li>
<li>通过 Fiber 结构将不紧急的更新拆分成多段更新,并通过宏任务的方式将其合理分配到浏览器的帧当中。这样就能使得紧急任务能够插入进来。</li>
<li>高优先级的更新会打断低优先级的更新,等高优先级更新完成后,再开始低优先级更新。</li>
</ul>
<h3 id="什么是受控组件和非受控组件">什么是受控组件和非受控组件?</h3>
<p>我们稍微了解下什么是受控组件和非受控组件:</p>
<ul>
<li>受控组件:只能通过 React 修改数据或状态的组件,就是受控组件;</li>
<li>非受控组件:与受控组件相反,如 input, textarea, select, checkbox 等组件,本身控件自己就能控制数据和状态的变更,而且 React 是不知道这些变更的;</li>
</ul>
<p>那么如何将非受控组件改为受控组件呢?那就是把上面的这些纯 html 组件数据或状态的变更,交给 React 来操作:</p>
<pre><code class="language-javascript">const App = () =&gt; {
const = useState('');
const = useState(false);

return (
    &lt;&gt;
      &lt;input value={value} onInput={event =&gt; setValue(event.target.value)} /&gt;
      &lt;input type="checkbox" checked={checked} onChange={event =&gt; setChecked(event.target.checked)} /&gt;
    &lt;/&gt;
);
};
</code></pre>
<p>上面代码中,输入框和 checkbox 的变化,均是经过了 React 来操作的,在数据变更时,React 是能够知道的。</p>
<h3 id="高阶组件hoc">高阶组件(HOC)?</h3>
<h3 id="高阶组件">高阶组件?</h3>
<p>高阶组件通过包裹(wrapped)被传入的 React 组件,经过一系列处理,最终返回一个相对增强(enhanced)的 React 组件,供其他组件调用。</p>
<p>作用:</p>
<ol>
<li>复用逻辑:高阶组件更像是一个加工 react 组件的工厂,批量对原有组件进行加工,包装处理。我们可以根据业务需求定制化专属的 HOC,这样可以解决复用逻辑。</li>
<li>强化 props:这个是 HOC 最常用的用法之一,高阶组件返回的组件,可以劫持上一层传过来的 props,然后混入新的 props,来增强组件的功能。代表作 react-router 中的 withRouter。</li>
<li>赋能组件:HOC 有一项独特的特性,就是可以给被 HOC 包裹的业务组件,提供一些拓展功能,比如说额外的生命周期,额外的事件,但是这种 HOC,可能需要和业务组件紧密结合。典型案例 react-keepalive-router 中的 keepaliveLifeCycle 就是通过 HOC 方式,给业务组件增加了额外的生命周期。</li>
<li>控制渲染:劫持渲染是 hoc 一个特性,在 wrapComponent 包装组件中,可以对原来的组件,进行条件渲染,节流渲染,懒加载等功能,后面会详细讲解,典型代表做 react-redux 中 connect 和 dva 中 dynamic 组件懒加载。</li>
</ol>
<p>参考:react 进阶」一文吃透 React 高阶组件(HOC)</p>
<h3 id="react-中为什么要使用-hook">React 中为什么要使用 Hook?</h3>
<p>官方网站有介绍该原因:使用 Hook 的动机。</p>
<p>这里我们简要的提炼下:</p>
<ol>
<li>在组件之间复用状态逻辑很难:在类组件中,可能需要 render props 和 高阶组件等方式,但会形成“嵌套地域”;而使用 Hook,则可以从组件中提取状态逻辑,是的这些逻辑可以单独测试并复用;</li>
<li>复杂组件变得难以理解:在类组件中,每个生命周期常常包含一些不相关的逻辑。如不同的执行逻辑,都要放在<code>componentDidMount</code>中执行和获取数据,而之后需在 <code>componentWillUnmount</code> 中清除;但在函数组件中,不同的逻辑可以放在不同的 Hook 中执行,互不干扰;</li>
<li>难以理解的 class:类组件中,充斥着各种对 <code>this</code> 的使用,如 <code>this.onClick.bind(this)</code>,<code>this.state</code>,<code>this.setState()</code> 等,同时,class 不能很好的压缩,并且会使热重载出现不稳定的情况;Hook 使你在非 class 的情况下可以使用更多的 React 特性;</li>
</ol>
<h3 id="usecallback-和-usememo-的使用场景">useCallback 和 useMemo 的使用场景</h3>
<p>useCallback 和 useMemo 可以用来缓存函数和变量,提高性能,减少资源浪费。但并不是所有的函数和变量都需要用这两者来实现,他也有对应的使用场景。</p>
<p>我们知道 useCallback 可以缓存函数体,在依赖项没有变化时,前后两次渲染时,使用的函数体是一样的。它的使用场景是:</p>
<ul>
<li>函数作为其他 hook 的依赖项时(如在 useEffect()中);</li>
<li>函数作为 React.memo()(或 shouldComponentUpdate )中的组件的 props;</li>
</ul>
<p>主要是为了避免重新生成的函数,会导致其他 hook 或组件的不必要刷新。</p>
<p>useMemo 用来缓存函数执行的结果。如每次渲染时都要执行一段很复杂的运算,或者一个变量需要依赖另一个变量的运算结果,就都可以使用 useMemo()。</p>
<p>参考文章:React18 源码解析之 useCallback 和 useMemo。</p>
<h3 id="usestate-的传参方式有什么区别">useState 的传参方式,有什么区别?</h3>
<p>useState()的传参有两种方式:纯数据和回调函数。这两者在初始化时,除了传入方式不同,没啥区别。但在调用时,不同的调用方式和所在环境,输出的结果也是不一样的。</p>
<p>如:</p>
<pre><code class="language-javascript">const App = () =&gt; {
const = useState(0);

const handleParamClick = () =&gt; {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
};

const handleCbClick = () =&gt; {
    setCount(count =&gt; count + 1);
    setCount(count =&gt; count + 1);
    setCount(count =&gt; count + 1);
};
};
</code></pre>
<p>上面的两种传入方式,最后得到的 count 结果是不一样的。为什么呢?因为在以数据的格式传参时,这 3 个使用的是同一个 count 变量,数值是一样的。相当于<code>setCount(0 + 1)</code>,调用了 3 次;但以回调函数的传参方式,React 则一般地会直接该回调函数,然后得到最新结果并存储到 React 内部,下次使用时就是最新的了。注意:这个最新值是保存在 React 内部的,外部的 count 并不会马上更新,只有在下次渲染后才会更新。</p>
<p>还有,在定时器中,两者得到的结果也是不一样的:</p>
<pre><code class="language-javascript">const App = () =&gt; {
const = useState(0);

useEffect(() =&gt; {
    const timer = setInterval(() =&gt; {
      setCount(count + 1);
    }, 500);
    return () =&gt; clearInterval(timer);
}, []);

useEffect(() =&gt; {
    const timer = setInterval(() =&gt; {
      setCount(count =&gt; count + 1);
    }, 500);
    return () =&gt; clearInterval(timer);
}, []);
};
</code></pre>
<h3 id="为什么在本地开发时组件会渲染两次">为什么在本地开发时,组件会渲染两次?</h3>
<p>issues#2</p>
<p>在 React.StrictMode 模式下,如果用了 useState,usesMemo,useReducer 之类的 Hook,React 会故意渲染两次,为的就是将一些不容易发现的错误容易暴露出来,同时 React.StrictMode 在正式环境中不会重复渲染。</p>
<p>也就是在测试环境的严格模式下,才会渲染两次。</p>
<h3 id="如何实现组件的懒加载">如何实现组件的懒加载</h3>
<p>从 16.6.0 开始,React 提供了 lazy 和 Suspense 来实现懒加载。</p>
<pre><code class="language-javascript">import React, { lazy, Suspense } from 'react';
const OtherComponent = lazy(() =&gt; import('./OtherComponent'));

function MyComponent() {
return (
    &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;OtherComponent /&gt;
    &lt;/Suspense&gt;
);
}
</code></pre>
<p>属性<code>fallback</code>表示在加载组件前,渲染的内容。</p>
<h3 id="如何实现一个定时器的-hook">如何实现一个定时器的 hook</h3>
<p>若在定时器内直接使用 React 的代码,可能会收到意想不到的结果。如我们想实现一个每 1 秒加 1 的定时器:</p>
<pre><code class="language-javascript">const App = () =&gt; {
const = useState(0);

useEffect(() =&gt; {
    const timer = setInterval(() =&gt; {
      setCount(count + 1);
    }, 1000);
    return () =&gt; clearInterval(timer);
}, []);

return &lt;div className="App"&gt;{count}&lt;/div&gt;;
};
</code></pre>
<p>可以看到,coun 从 0 变成 1 以后,就再也不变了。为什么会这样?</p>
<p><img src="https://img2023.cnblogs.com/blog/443443/202303/443443-20230315093418867-1569879374.webp" alt="count的作用域" loading="lazy"></p>
<p>尽管由于定时器的存在,组件始终会一直重新渲染,但定时器的回调函数是挂载期间定义的,所以它的闭包永远是对挂载时 Counter 作用域的引用,故 count 永远不会超过 1。</p>
<p>针对这个单一的 hook 调用,还比较好解决,例如可以监听 count 的变化,或者通过 useState 的 callback 传参方式。</p>
<pre><code class="language-javascript">const App = () =&gt; {
const = useState(0);

// 监听 count 的变化,不过这里将定时器改成了 setTimeout
// 即使不修改,setInterval()的timer也会在每次渲染时被清除掉,
// 然后重新启动一个新的定时器
useEffect(() =&gt; {
    const timer = setTimeout(() =&gt; {
      setCount(count + 1);
    }, 1000);
    return () =&gt; clearInterval(timer);
}, );

// 以回调的方式
// 回调的方式,会计算回调的结果,然后作为下次更新的初始值
// 详情可见: https://www.xiabingbao.com/post/react/react-usestate-rn5bc0.html#5.+updateReducer
useEffect(() =&gt; {
    const timer = setInterval(() =&gt; {
      setCount(count =&gt; count + 1);
    }, 1000);
    return () =&gt; clearInterval(timer);
}, []);

return &lt;div className="App"&gt;{count}&lt;/div&gt;;
};
</code></pre>
<p>当然还有别的方式也可以实现 count 的更新。那要是调用更多的 hook,或者更复杂的代码,该怎么办呢?这里我们可以封装一个新的 hook 来使用:</p>
<pre><code class="language-javascript">// https://overreacted.io/zh-hans/making-setinterval-declarative-with-react-hooks/
const useInterval = (callback: () =&gt; void, delay: number | null): void =&gt; {
const savedCallback = useRef(callback);

useEffect(() =&gt; {
    savedCallback.current = callback;
});

useEffect(() =&gt; {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () =&gt; clearInterval(id);
    }
}, );
};
</code></pre>
<h3 id="useeffect的清除机制是什么在什么时候执行">useEffect()的清除机制是什么?在什么时候执行?</h3>
<p>useEffect(callback)的回调函数里,若有返回的函数,这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。</p>
<p>React 何时清除 effect? React 会在组件卸载的时候执行清除操作。同时,若组件产生了更新,会先执行上一个的清除函数,然后再运行下一个 effect。如</p>
<pre><code class="language-javascript">// 运行第一个 effect

// 产生更新时
// 清除上一个 effect
// 运行下一个 effect

// 产生更新时
// 清除上一个 effect
// 运行下一个 effect

// 组件卸载时
// 清除最后一个 effect
</code></pre>
<p>参考:为什么每次更新的时候都要运行 Effect</p>
<h2 id="2-源码层面上">2. 源码层面上</h2>
<p>这部分考察的就更有深度一些了,多多少少得了解一些源码,才能明白其中的缘由,比如 React 的 diff 对比,循环中 key 的作用等。</p>
<h3 id="虚拟-dom-有什么优点真实-dom-和虚拟-dom谁快">虚拟 dom 有什么优点?真实 dom 和虚拟 dom,谁快?</h3>
<p>Virtual DOM 是以对象的方式来描述真实 dom 对象的,那么在做一些 update 的时候,可以在内存中进行数据比对,减少对真实 dom 的操作减少浏览器重排重绘的次数,减少浏览器的压力,提高程序的性能,并且因为 diff 算法的差异比较,记录了差异部分,那么在开发中就会帮助程序员减少对差异部分心智负担,提高了开发效率。</p>
<p>虚拟 dom 好多这么多,渲染速度上是不是比直接操作真实 dom 快呢?并不是。虚拟 dom 增加了一层内存运算,然后才操作真实 dom,将数据渲染到页面上。渲染上肯定会慢上一些。虽然虚拟 dom 的缺点在初始化时增加了内存运算,增加了首页的渲染时间,但是运算时间是以毫秒级别或微秒级别算出的,对用户体验影响并不是很大。</p>
<h3 id="什么是合成事件与原生事件有什么区别">什么是合成事件,与原生事件有什么区别?</h3>
<p>React 中所有触发的事件,都是自己在其内部封装了一套事件机制。目的是为了实现全浏览器的一致性,抹平不同浏览器之间的差异性。</p>
<p>在 React17 之前,React 是把事件委托在 document 上的,React17 及以后版本不再把事件委托在 document 上,而是委托在挂载的容器上。React 合成事件采用的是事件冒泡机制,当在某具体元素上触发事件时,等冒泡到顶部被挂载事件的那个元素时,才会真正地执行事件。</p>
<p>而原生事件,当某具体元素触发事件时,会立刻执行该事件。因此若要比较事件触发的先后时机时,原生事件会先执行,React 合成事件会后执行。</p>
<h3 id="key-的作用是什么">key 的作用是什么?</h3>
<p>key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。</p>
<p>当组件刷新时,React 内部会根据 key 和元素的 type,来对比元素是否发生了变化。若选做 key 的数据有问题,可能会在更新的过程中产生异常。</p>
<p>参考:React18 源码解析之 key 的作用。</p>
<h3 id="多次执行-usestate会触发多次更新吗">多次执行 useState(),会触发多次更新吗?</h3>
<p>在 React18 中,无论是多个 useState()的 hook,还是操作(dispatch)多次的数据。只要他们在同一优先级,React 就会将他们合并到一起操作,最后再更新数据。</p>
<p>这是基于 React18 的批处理机制。React 将多个状态更新分组到一个重新渲染中以获得更好的性能。(将多次 setstate 事件合并);在 v18 之前只在事件处理函数中实现了批处理,在 v18 中所有更新都将自动批处理,包括 promise 链、setTimeout 等异步代码以及原生事件处理函数;</p>
<p>参考:多次调用 useState() 中的 dispatch 方法,会产生多次渲染吗?</p>
<h3 id="usestate的-state-是否可以直接修改是否可以引起组件渲染">useState()的 state 是否可以直接修改?是否可以引起组件渲染?</h3>
<p>首先声明,我们不应当直接修改 state 的值,一方面是无法刷新组件(无法将新数据渲染到页面中),再有可能会对下次的更新产生影响。</p>
<p>唯一有影响的,就是后续要使用该变量的地方,会使用到新数据。但若其他 useState() 导致了组件的刷新,刚才变量的值,若是基本类型(比如数字、字符串等),会重置为修改之前的值;若是复杂类型,基于 js 的 对象引用 特性,也会同步修改 React 内部存储的数据,但不会引起视图的变化。</p>
<p>参考:直接修改 state 的值,会怎样?</p>
<h3 id="react-的-diff-过程">React 的 diff 过程</h3>
<ol>
<li>React 只对比当前层级的节点,不跨层级进行比较;</li>
<li>根据不同的节点类型,如函数组件节点、类组件节点、普通 fiber 节点、数组节点等,进入不同的处理函数;</li>
<li>前后两个 fiber 节点进行对比,若 type 不一样,直接舍弃掉旧的 fiber 节点,创建新的 fiber 节点;若 key 不一样,则需要根据情况判断,若是单个元素,则直接舍弃掉,创建新的 fiber 节点;若是数字型的元素,则查找是否移动了位置,若没找到,则创建新的节点;若 key 和 type 都一样,则接着往下递归;</li>
<li>若是单个 fiber 节点,则直接返回;若是并列多个元素的 fiber 节点,这里会形成单向链表,然后返回头指针(该链表最前面的那个 fiber 节点);</li>
</ol>
<p>通过上面的 diff 对比过程,我们也可以看到,当组件产生比较大的变更时,React 需要做更多的动作,来构建出新的 fiber 树,因此我们在开发过程中,若从性能优化的角度考虑,尤其要注意的是:</p>
<ol>
<li>节点不要产生大量的越级操作:因为 React 是只进行同层节点的对比,若同一个位置的子节点产生了比较大的变动,则只会舍弃掉之前的 fiber 节点,从而执行创建新 fiber 节点的操作;React 并不会把之前的 fiber 节点移动到另一个位置;相应的,之前的 jsx 节点移动到另一个位置后,在进行前后对比后,同样会执行更多的创建操作;</li>
<li>不修改节点的 key 和 type 类型,如使用随机数做为列表的 key,或从 div 标签改成 p 标签等操作,在 diff 对比过程中,都会直接舍弃掉之前的 fiber 节点及所有的子节点(即使子节点没有变动),然后重新创建出新的 fiber 节点;</li>
</ol>
<p>参考:React18 源码解析之 reconcileChildren 生成 fiber 的过程</p>
<h3 id="基于-react-框架的特点可以有哪些优化措施">基于 React 框架的特点,可以有哪些优化措施?</h3>
<ol>
<li>使用 React.lazy 和 Suspense 将页面设置为懒加载,避免 js 文件过大;</li>
<li>使用 SSR 同构直出技术,提高首屏的渲染速度;</li>
<li>使用 useCallback 和 useMemo 缓存函数或变量;使用 React.memo 缓存组件;</li>
<li>尽量调整样式或 className 的变动,减少 jsx 元素上的变动,尽量使用与元素相关的字段作为 key,可以减少 diff 的时间(React 会尽量复用之前的节点,若 jsx 元素发生变动,就需要重新创建节点);</li>
<li>对于不需要产生页面变动的数据,可以放到 useRef()中;</li>
</ol>
<h3 id="reactchildrenmap-和-js-的-map-有什么区别">React.Children.map 和 js 的 map 有什么区别?</h3>
<p>JavaScript 中的 map 不会对为 null 或者 undefined 的数据进行处理,而 React.Children.map 中的 map 可以处理 React.Children 为 null 或者 undefined 的情况。</p>
<h2 id="3-周边生态">3. 周边生态</h2>
<p>这部分主要考察 React 周边生态配套的了解,如状态管理库 redux、mobx,路由组件 react-router-dom 等。</p>
<h3 id="react-router-和-react-router-dom-的有什么区别">react-router 和 react-router-dom 的有什么区别?</h3>
<h4 id="api-方面">api 方面</h4>
<p>React-router: 提供了路由的核心 api。如 Router、Route、Switch 等,但没有提供有关 dom 操作进行路由跳转的 api;<br>
React-router-dom: 提供了 BrowserRouter、Route、Link 等 api,可以通过 dom 操作触发事件控制路由。<br>
Link 组件,会渲染一个 a 标签;BrowserRouter 和 HashRouter 组件,前者使用 pushState 和 popState 事件构建路由,后者使用 hash 和 hashchange 事件构建路由。</p>
<h4 id="使用区别">使用区别</h4>
<p>react-router-dom 在 react-router 的基础上扩展了可操作 dom 的 api。 Swtich 和 Route 都是从 react-router 中导入了相应的组件并重新导出,没做什么特殊处理。<br>
react-router-dom 中 package.json 依赖中存在对 react-router 的依赖,故此,不需要额外安装 react-router。</p>
<h3 id="redux-遵循的三个原则是什么">Redux 遵循的三个原则是什么?</h3>
<ol>
<li>单一事实来源:整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序。</li>
<li>状态是只读的:改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。</li>
<li>使用纯函数进行更改:为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。</li>
</ol>
<h3 id="你对单一事实来源有什么理解">你对“单一事实来源”有什么理解?</h3>
<p>Redux 使用 “Store” 将程序的整个状态存储在同一个地方。因此所有组件的状态都存储在 Store 中,并且它们从 Store 本身接收更新。单一状态树可以更容易地跟踪随时间的变化,并调试或检查程序。</p>
<h3 id="redux-有哪些优点">Redux 有哪些优点?</h3>
<p>Redux 的优点如下:</p>
<ul>
<li>结果的可预测性 - 由于总是存在一个真实来源,即 store ,因此不存在如何将当前状态与动作和应用的其他部分同步的问题。</li>
<li>可维护性 - 代码变得更容易维护,具有可预测的结果和严格的结构。</li>
<li>服务器端渲染 - 你只需将服务器上创建的 store 传到客户端即可。这对初始渲染非常有用,并且可以优化应用性能,从而提供更好的用户体验。</li>
<li>开发人员工具 - 从操作到状态更改,开发人员可以实时跟踪应用中发生的所有事情。</li>
<li>社区和生态系统 - Redux 背后有一个巨大的社区,这使得它更加迷人。一个由才华横溢的人组成的大型社区为库的改进做出了贡献,并开发了各种应用。</li>
<li>易于测试 - Redux 的代码主要是小巧、纯粹和独立的功能。这使代码可测试且独立。</li>
<li>组织 - Redux 准确地说明了代码的组织方式,这使得代码在团队使用时更加一致和简单。</li>
</ul>
<h2 id="4-总结">4. 总结</h2>
<p>React 涉及到的相关知识点非常多,我也会经常更新的。</p>
<p>欢迎关注我的公众号:“<strong>前端小茶馆</strong>”。</p>
<p><img src="https://img2020.cnblogs.com/blog/443443/202103/443443-20210329093702763-499977807.png" alt="前端小茶馆公众号" loading="lazy"></p><br><br>
来源:https://www.cnblogs.com/xumengxuan/p/17217337.html
頁: [1]
查看完整版本: 2023 年最新最全的 React 面试题