<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React 靈魂 23 問,你能答對幾個?

          共 8625字,需瀏覽 18分鐘

           ·

          2020-12-17 10:37


          授權(quán)轉(zhuǎn)載自:王玉略?

          https://zhuanlan.zhihu.com/p/304213203

          文內(nèi)所有鏈接請點擊底部【閱讀原文】查看

          1、setState 是異步還是同步?

          1. 合成事件中是異步
          2. 鉤子函數(shù)中的是異步
          3. 原生事件中是同步
          4. setTimeout中是同步

          相關(guān)鏈接:你真的理解setState嗎?:

          2、聊聊 [email protected] + 的生命周期

          相關(guān)連接:React 生命周期 我對 React v16.4 生命周期的理解

          3、useEffect(fn, []) 和 componentDidMount 有什么差異?

          useEffect?會捕獲?props??state。所以即便在回調(diào)函數(shù)里,你拿到的還是初始的?props?state。如果想得到“最新”的值,可以使用?ref

          4、hooks 為什么不能放在條件判斷里?

          ?setState?為例,在 react 內(nèi)部,每個組件(Fiber)的 hooks 都是以鏈表的形式存在?memoizeState?屬性中:

          update 階段,每次調(diào)用?setState,鏈表就會執(zhí)行 next 向后移動一步。如果將?setState?寫在條件判斷中,假設(shè)條件判斷不成立,沒有執(zhí)行里面的?setState?方法,會導(dǎo)致接下來所有的setState?的取值出現(xiàn)偏移,從而導(dǎo)致異常發(fā)生。

          參考鏈接:烤透 React Hook

          5、fiber 是什么?

          React Fiber 是一種基于瀏覽器的單線程調(diào)度算法。

          React Fiber 用類似?requestIdleCallback?的機制來做異步 diff。但是之前數(shù)據(jù)結(jié)構(gòu)不支持這樣的實現(xiàn)異步 diff,于是 React 實現(xiàn)了一個類似鏈表的數(shù)據(jù)結(jié)構(gòu),將原來的 遞歸diff 變成了現(xiàn)在的 遍歷diff,這樣就能做到異步可更新了。

          相關(guān)鏈接:React Fiber 是什么?

          6、聊一聊 diff 算法

          傳統(tǒng) diff 算法的時間復(fù)雜度是 O(n^3),這在前端 render 中是不可接受的。為了降低時間復(fù)雜度,react 的 diff 算法做了一些妥協(xié),放棄了最優(yōu)解,最終將時間復(fù)雜度降低到了 O(n)。

          那么 react diff 算法做了哪些妥協(xié)呢?,參考如下:

          1、tree diff:只對比同一層的 dom 節(jié)點,忽略 dom 節(jié)點的跨層級移動

          如下圖,react 只會對相同顏色方框內(nèi)的 DOM 節(jié)點進(jìn)行比較,即同一個父節(jié)點下的所有子節(jié)點。當(dāng)發(fā)現(xiàn)節(jié)點不存在時,則該節(jié)點及其子節(jié)點會被完全刪除掉,不會用于進(jìn)一步的比較。

          這樣只需要對樹進(jìn)行一次遍歷,便能完成整個 DOM 樹的比較。

          這就意味著,如果 dom 節(jié)點發(fā)生了跨層級移動,react 會刪除舊的節(jié)點,生成新的節(jié)點,而不會復(fù)用。

          2、component diff:如果不是同一類型的組件,會刪除舊的組件,創(chuàng)建新的組件

          3、element diff:對于同一層級的一組子節(jié)點,需要通過唯一 id 進(jìn)行來區(qū)分

          如果沒有 id 來進(jìn)行區(qū)分,一旦有插入動作,會導(dǎo)致插入位置之后的列表全部重新渲染。

          這也是為什么渲染列表時為什么要使用唯一的 key。

          7、調(diào)用 setState 之后發(fā)生了什么?

          1. ?setState?的時候,React 會為當(dāng)前節(jié)點創(chuàng)建一個?updateQueue?的更新列隊。
          2. 然后會觸發(fā)?reconciliation?過程,在這個過程中,會使用名為 Fiber 的調(diào)度算法,開始生成新的 Fiber 樹, Fiber 算法的最大特點是可以做到異步可中斷的執(zhí)行。
          3. 然后?React Scheduler?會根據(jù)優(yōu)先級高低,先執(zhí)行優(yōu)先級高的節(jié)點,具體是執(zhí)行?doWork方法。
          4. ?doWork?方法中,React 會執(zhí)行一遍?updateQueue?中的方法,以獲得新的節(jié)點。然后對比新舊節(jié)點,為老節(jié)點打上 更新、插入、替換 等 Tag。
          5. 當(dāng)前節(jié)點?doWork?完成后,會執(zhí)行?performUnitOfWork?方法獲得新節(jié)點,然后再重復(fù)上面的過程。
          6. 當(dāng)所有節(jié)點都?doWork?完成后,會觸發(fā)?commitRoot?方法,React 進(jìn)入 commit 階段。
          7. 在 commit 階段中,React 會根據(jù)前面為各個節(jié)點打的 Tag,一次性更新整個 dom 元素。

          8、為什么虛擬dom 會提高性能?

          虛擬dom 相當(dāng)于在 JS 和真實 dom 中間加了一個緩存,利用 diff 算法避免了沒有必要的 dom 操作,從而提高性能。

          9、錯誤邊界是什么?它有什么用?

          在 React 中,如果任何一個組件發(fā)生錯誤,它將破壞整個組件樹,導(dǎo)致整頁白屏。這時候我們可以用錯誤邊界優(yōu)雅地降級處理這些錯誤。

          例如下面封裝的組件:

          class ErrorBoundary extends React.Component {
          constructor(props: IProps) {
          super(props);
          this.state = { hasError: false };
          }

          static getDerivedStateFromError() {
          // 更新 state 使下一次渲染能夠顯示降級后的 UI
          return { hasError: true };
          }

          componentDidCatch(error, errorInfo) {
          // 可以將錯誤日志上報給服務(wù)器
          console.log('組件奔潰 Error', error);
          console.log('組件奔潰 Info', errorInfo);
          }

          render() {
          if (this.state.hasError) {
          // 你可以自定義降級后的 UI 并渲染
          return this.props.content;
          }
          return this.props.children;
          }
          }

          10、什么是 Portals?

          Portal 提供了一種將子節(jié)點渲染到存在于父組件以外的 DOM 節(jié)點的優(yōu)秀的方案。

          ReactDOM.createPortal(child, container)

          11、React 組件間有那些通信方式?

          父組件向子組件通信

          1、 通過 props 傳遞

          子組件向父組件通信

          1、 主動調(diào)用通過 props 傳過來的方法,并將想要傳遞的信息,作為參數(shù),傳遞到父組件的作用域中

          跨層級通信

          1、 使用 react 自帶的?Context?進(jìn)行通信,createContext?創(chuàng)建上下文,?useContext?使用上下文。

          參考下面代碼:

          import React, { createContext, useContext } from 'react';

          const themes = {
          light: {
          foreground: "#000000",
          background: "#eeeeee"
          },
          dark: {
          foreground: "#ffffff",
          background: "#222222"
          }
          };

          const ThemeContext = createContext(themes.light);

          function App() {
          return (



          );
          }

          function Toolbar() {
          return (



          );
          }

          function ThemedButton() {
          const theme = useContext(ThemeContext);
          return (

          );
          }

          export default App;

          2、使用 Redux 或者 Mobx 等狀態(tài)管理庫

          3、使用訂閱發(fā)布模式

          相關(guān)鏈接:React Docs

          12、React 父組件如何調(diào)用子組件中的方法?

          1、如果是在方法組件中調(diào)用子組件(>= [email protected]),可以使用?useRef??useImperativeHandle:

          const { forwardRef, useRef, useImperativeHandle } = React;

          const Child = forwardRef((props, ref) => {
          useImperativeHandle(ref, () => ({
          getAlert() {
          alert("getAlert from Child");
          }
          }));
          return

          Hi

          ;
          });

          const Parent = () => {
          const childRef = useRef();
          return (




          );
          };

          2、如果是在類組件中調(diào)用子組件(>= [email protected]),可以使用?createRef:

          const { Component } = React;

          class Parent extends Component {
          constructor(props) {
          super(props);
          this.child = React.createRef();
          }

          onClick = () => {
          this.child.current.getAlert();
          };

          render() {
          return (




          );
          }
          }

          class Child extends Component {
          getAlert() {
          alert('getAlert from Child');
          }

          render() {
          return

          Hello

          ;
          }
          }

          參考閱讀:Call child method from parent

          13、React有哪些優(yōu)化性能的手段?

          類組件中的優(yōu)化手段

          1、使用純組件?PureComponent?作為基類。

          2、使用?React.memo?高階函數(shù)包裝組件。

          3、使用?shouldComponentUpdate?生命周期函數(shù)來自定義渲染邏輯。

          方法組件中的優(yōu)化手段

          1、使用?useMemo

          2、使用?useCallBack

          其他方式

          1、在列表需要頻繁變動時,使用唯一 id 作為 key,而不是數(shù)組下標(biāo)。

          2、必要時通過改變 CSS 樣式隱藏顯示組件,而不是通過條件判斷顯示隱藏組件。

          3、使用?Suspense??lazy?進(jìn)行懶加載,例如:

          import React, { lazy, Suspense } from "react";

          export default class CallingLazyComponents extends React.Component {
          render() {
          var ComponentToLazyLoad = null;

          if (this.props.name == "Mayank") {
          ComponentToLazyLoad = lazy(() => import("./mayankComponent"));
          } else if (this.props.name == "Anshul") {
          ComponentToLazyLoad = lazy(() => import("./anshulComponent"));
          }

          return (

          This is the Base User: {this.state.name}


          Loading...
          }>



          )
          }
          }

          Suspense?用法可以參考官方文檔

          相關(guān)閱讀:21個React性能優(yōu)化技巧

          14、為什么 React 元素有一個 $$typeof 屬性?

          目的是為了防止 XSS 攻擊。因為 Synbol 無法被序列化,所以 React 可以通過有沒有 $$typeof 屬性來斷出當(dāng)前的 element 對象是從數(shù)據(jù)庫來的還是自己生成的。

          如果沒有 $$typeof 這個屬性,react 會拒絕處理該元素。

          在 React 的古老版本中,下面的寫法會出現(xiàn) XSS 攻擊:

          // 服務(wù)端允許用戶存儲 JSON
          let expectedTextButGotJSON = {
          type: 'div',
          props: {
          dangerouslySetInnerHTML: {
          __html: '/* 把你想的擱著 */'
          },
          },
          // ...
          };
          let message = { text: expectedTextButGotJSON };

          // React 0.13 中有風(fēng)險


          {message.text}


          相關(guān)閱讀:Dan Abramov Blog

          15、React 如何區(qū)分 Class組件 和 Function組件?

          一般的方式是借助 typeof 和 Function.prototype.toString 來判斷當(dāng)前是不是 class,如下:

          function?isClass(func)?{
          ??return?typeof?func?===?'function'
          ????&&?/^class\s/.test(Function.prototype.toString.call(func));
          }

          但是這個方式有它的局限性,因為如果用了 babel 等轉(zhuǎn)換工具,將 class 寫法全部轉(zhuǎn)為 function 寫法,上面的判斷就會失效。

          React 區(qū)分 Class組件 和 Function組件的方式很巧妙,由于所有的類組件都要繼承 React.Component,所以只要判斷原型鏈上是否有 React.Component 就可以了:

          AComponent.prototype?instanceof?React.Component

          相關(guān)閱讀:Dan Abramov Blog

          16、HTML 和 React 事件處理有什么區(qū)別?

          在 HTML 中事件名必須小寫:

          <button?onclick='activateLasers()'>

          而在 React 中需要遵循駝峰寫法:

          在 HTML 中可以返回 false 以阻止默認(rèn)的行為:

          <a?href='#'?onclick='console.log("The?link?was?clicked.");?return?false;'?/>

          而在 React 中必須地明確地調(diào)用?preventDefault()

          function?handleClick(event)?{
          ??event.preventDefault()
          ??console.log('The?link?was?clicked.')
          }

          17、什么是 suspense 組件?

          Suspense 讓組件“等待”某個異步操作,直到該異步操作結(jié)束即可渲染。在下面例子中,兩個組件都會等待異步 API 的返回值:

          const resource = fetchProfileData();

          function ProfilePage() {
          return (
          Loading profile...}>

          Loading posts...}>



          );
          }

          function ProfileDetails() {
          // 嘗試讀取用戶信息,盡管該數(shù)據(jù)可能尚未加載
          const user = resource.user.read();
          return

          {user.name}

          ;
          }

          function ProfileTimeline() {
          // 嘗試讀取博文信息,盡管該部分?jǐn)?shù)據(jù)可能尚未加載
          const posts = resource.posts.read();
          return (

            {posts.map(post => (
          • {post.text}

          • ))}

          );
          }

          Suspense 也可以用于懶加載,參考下面的代碼:

          const OtherComponent = React.lazy(() => import('./OtherComponent'));

          function MyComponent() {
          return (

          Loading...
          }>



          );
          }

          18、為什么 JSX 中的組件名要以大寫字母開頭?

          因為 React 要知道當(dāng)前渲染的是組件還是 HTML 元素。

          19、redux 是什么?

          Redux 是一個為 JavaScript 應(yīng)用設(shè)計的,可預(yù)測的狀態(tài)容器。

          它解決了如下問題:

          • 跨層級組件之間的數(shù)據(jù)傳遞變得很容易
          • 所有對狀態(tài)的改變都需要 dispatch,使得整個數(shù)據(jù)的改變可追蹤,方便排查問題。

          但是它也有缺點:

          • 概念偏多,理解起來不容易
          • 樣板代碼太多

          20、react-redux 的實現(xiàn)原理?

          通過 redux 和 react context 配合使用,并借助高階函數(shù),實現(xiàn)了?react-redux

          參考鏈接:React.js 小書

          21、reudx 和 mobx 的區(qū)別?

          得益于 Mobx 的 observable,使用 mobx 可以做到精準(zhǔn)更新;對應(yīng)的 Redux 是用 dispath 進(jìn)行廣播,通過Provider 和 connect 來比對前后差別控制更新粒度;

          相關(guān)閱讀:Redux or MobX: An attempt to dissolve the Confusion

          22、redux 異步中間件有什么什么作用?

          假如有這樣一個需求:請求數(shù)據(jù)前要向 Store dispatch 一個 loading 狀態(tài),并帶上一些信息;請求結(jié)束后再向Store dispatch 一個 loaded 狀態(tài)

          一些同學(xué)可能會這樣做:

          function App() {
          const onClick = () => {
          dispatch({ type: 'LOADING', message: 'data is loading' })
          fetch('dataurl').then(() => {
          dispatch({ type: 'LOADED' })
          });
          }

          return (


          );
          }

          但是如果有非常多的地方用到這塊邏輯,那應(yīng)該怎么辦?

          聰明的同學(xué)會想到可以將 onClick 里的邏輯抽象出來復(fù)用,如下:

          function fetchData(message: string) {
          return (dispatch) => {
          dispatch({ type: 'LOADING', message })
          setTimeout(() => {
          dispatch({ type: 'LOADED' })
          }, 1000)
          }
          }

          function App() {
          const onClick = () => {
          fetchData('data is loading')(dispatch)
          }

          return (


          );
          }

          很好,但是?fetchData('data is loading')(dispatch)?這種寫法有點奇怪,會增加開發(fā)者的心智負(fù)擔(dān)。

          于是可以借助 rudux 相關(guān)的異步中間件,以?rudux-chunk?為例,將寫法改為如下:

          function?fetchData(message:?string)?{
          ??return?(dispatch)?=>?{
          ????dispatch({?type:?'LOADING',?message?})
          ????setTimeout(()?=>?{
          ??????dispatch({?type:?'LOADED'?})
          ????},?1000)
          ??}
          }

          function?App()?{
          ??const?onClick?=?()?=>?{
          -???fetchData('data?is?loading')(dispatch)
          +???dispatch(fetchData('data?is?loading'))
          ??}

          ??return?(

          ????click
          ??
          );
          }

          這樣就更符合認(rèn)知一些了,redux 異步中間件沒有什么奧秘,主要做的就是這樣的事情。

          相關(guān)閱讀:Why do we need middleware for async flow in Redux?

          23、redux 有哪些異步中間件?

          1、redux-thunk

          源代碼簡短優(yōu)雅,上手簡單

          2、redux-saga

          借助 JS 的 generator 來處理異步,避免了回調(diào)的問題

          3、redux-observable

          借助了 RxJS 流的思想以及其各種強大的操作符,來處理異步問題

          覺得不錯可以點擊這個 repo 關(guān)注更多內(nèi)容。


          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操操操操黄片免费看 | 欧美骚逼网站 | 沤美日逼片 | 黄色网页伊人 | 有码中文字幕第一页 |