<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 問,你能答對幾個?

          共 8799字,需瀏覽 18分鐘

           ·

          2020-12-26 23:10

          關(guān)注 程序員成長指北,回復(fù)“1

          加入我們一起學(xué)習(xí),天天進步

          轉(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 會捕獲 propsstate。所以即便在回調(diào)函數(shù)里,你拿到的還是初始的 propsstate。如果想得到“最新”的值,可以使用 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é)點進行比較,即同一個父節(jié)點下的所有子節(jié)點。當(dāng)發(fā)現(xiàn)節(jié)點不存在時,則該節(jié)點及其子節(jié)點會被完全刪除掉,不會用于進一步的比較。

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


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

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


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

          如果沒有 id 來進行區(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 進入 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 進行通信,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]),可以使用 useRefuseImperativeHandle:

          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、使用 Suspenselazy 進行懶加載,例如:

          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 以阻止默認的行為:

          <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 進行廣播,通過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ā)者的心智負擔(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
          ??
          );
          }

          這樣就更符合認知一些了,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)容。

          ??愛心三連擊

          1.看到這里了就點個在看支持下吧,你的點贊在看是我創(chuàng)作的動力。

          2.關(guān)注公眾號程序員成長指北,回復(fù)「1」加入高級前端交流群!「在這里有好多 前端?開發(fā)者,會討論?前端 Node 知識,互相學(xué)習(xí)」!

          3.也可添加微信【ikoala520】,一起成長。

          “在看轉(zhuǎn)發(fā)”是最大的支持

          瀏覽 18
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  大香蕉伊人在线综合 | 男女大香蕉 | 欧美成人性爱图片 | 麻豆成人久久精品二区三区91 | 色婷婷亚洲 |