<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                &emsp;&emsp;[React](https://github.com/facebook/react)可大致分為三部分:Core、Reconciler和Renderer,在閱讀源碼之前,首先需要搭建測試環境,為了方便起見,本文直接采用了[網友搭建好的環境](https://github.com/pws019/react-sourcecode-debug-env),React版本是16.8.6,與最新版本很接近。 ## 一、目錄結構 &emsp;&emsp;React采用了由[Lerna](https://lerna.js.org/)維護monorepo方式進行代碼管理,即用一個倉庫管理多個模塊(module)或包(package)。在React倉庫的根目錄中,包含三個目錄: &emsp;&emsp;(1)fixtures,給源碼貢獻者準備的測試用例。 &emsp;&emsp;(2)packages,React庫提供的包的源碼,包括核心代碼、矢量圖形庫等,如下所列。 ~~~ ├── packages ------------------------------------ 源碼目錄 │ ├── react-art ------------------------------- 矢量圖形渲染器 │ ├── react-dom ------------------------------- DOM渲染器 │ ├── react-native-renderer ------------------- Native渲染器(原生iOS和Android視圖) │ ├── react-test-renderer --------------------- JSON樹渲染器 │ ├── react-reconciler ------------------------ React調和器 ~~~ &emsp;&emsp;(3)scripts,相關的工具配置腳本,包括語法規則、Git鉤子等。 &emsp;&emsp;React使用的前端模塊化打包工具是[Rollup](https://www.rollupjs.com/),在源碼中還引入了[Flow](https://zhenyong.github.io/flowtype/),用于靜態類型檢查,在運行代碼之前發現一些潛在的問題,其語法類似于TypeScript。 ## 二、React核心對象 &emsp;&emsp;在項目中引入React通常是像下面這樣。 ~~~ import React from 'react'; ~~~ &emsp;&emsp;其實引入的是核心入口文件“[packages/react/index.js](https://github.com/facebook/react/blob/master/packages/react/index.js)”中導出的對象,如下所示,其中React.default用于Jest測試,React用于Rollup。 ~~~ const React = require('./src/React'); // TODO: decide on the top-level export form. // This is hacky but makes it work with both Rollup and Jest. module.exports = React.default || React; ~~~ &emsp;&emsp;順著require()語句可以找到[React.js](https://github.com/facebook/react/blob/master/packages/react/src/React.js)中的React對象,代碼省略了一大堆導入語句,其中\_\_DEV\_\_是個全局變量,用于管理開發環境中運行的代碼塊。 ~~~ const React = { Children: { map, forEach, count, toArray, only, }, createRef, Component, PureComponent, createContext, forwardRef, lazy, memo, useCallback, useContext, useEffect, useImperativeHandle, useDebugValue, useLayoutEffect, useMemo, useReducer, useRef, useState, Fragment: REACT_FRAGMENT_TYPE, Profiler: REACT_PROFILER_TYPE, StrictMode: REACT_STRICT_MODE_TYPE, Suspense: REACT_SUSPENSE_TYPE, unstable_SuspenseList: REACT_SUSPENSE_LIST_TYPE, createElement: __DEV__ ? createElementWithValidation : createElement, cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement, createFactory: __DEV__ ? createFactoryWithValidation : createFactory, isValidElement: isValidElement, version: ReactVersion, unstable_withSuspenseConfig: withSuspenseConfig, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals, }; if (enableFlareAPI) { React.unstable_useResponder = useResponder; React.unstable_createResponder = createResponder; } if (enableFundamentalAPI) { React.unstable_createFundamental = createFundamental; } if (enableJSXTransformAPI) { if (__DEV__) { React.jsxDEV = jsxWithValidation; React.jsx = jsxWithValidationDynamic; React.jsxs = jsxWithValidationStatic; } else { React.jsx = jsx; React.jsxs = jsx; } } export default React; ~~~ &emsp;&emsp;在React對象中包含了開放的[核心API](https://zh-hans.reactjs.org/docs/react-api.html),例如React.Component、React.createRef()等,以及新引入的Hooks(內部的具體邏輯可轉移到相關的包中),但渲染的邏輯已經剝離出來。 **1)React.createElement()** &emsp;&emsp;JSX中的元素稱為[React元素](http://www.hmoore.net/pwstrick/fe-questions/1167731),分為兩種類型:DOM元素和組件元素。用JSX描述的組件都會通過Babel編譯器將它們轉換成React.createElement()方法,它包含三個參數(如下所示),其中type是元素類型,也就是它的名稱;props是一個由元素屬性組成的對象;children是它的子元素(即內容),可以是文本也可以是其它元素。 ~~~ React.createElement(type, [props], [...children]) ~~~ &emsp;&emsp;方法的返回值是一個ReactElement,省略了開發環境中的代碼。 ~~~ const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { $$typeof: REACT_ELEMENT_TYPE, type: type, key: key, ref: ref, props: props, _owner: owner //記錄創建該元素的組件 }; return element; }; ~~~ &emsp;&emsp;(1)$$typeof標識該對象是一個ReactElement。 &emsp;&emsp;(2)當ReactElement是DOM元素時,type是元素名稱;當ReactElement是組件元素時,type是其構造函數。 &emsp;&emsp;(3)[key](https://zh-hans.reactjs.org/docs/lists-and-keys.html)和[ref](https://zh-hans.reactjs.org/docs/forwarding-refs.html)是React組件中的兩個特殊屬性,前者用于標識身份,后者用于訪問render()方法內生成的組件實例和DOM元素。 &emsp;&emsp;(4)props是ReactElement中的屬性,包括特殊的[children屬性](https://zh-hans.reactjs.org/docs/glossary.html#propschildren)。 ## 三、Reconciler &emsp;&emsp;雖然React的DOM和Native兩種渲染器內部實現的區別很大,但為了能共享自定義組件、State、生命周期等特性,做到跨平臺,就需要共享一些邏輯,而這些邏輯由Reconciler統一處理,其中協調算法(Diffing算法)也要盡可能相似。 **1)Diffing算法** &emsp;&emsp;當調用React的render()方法時,會創建一棵由React元素組成的樹。在下一次State或Props更新時,相同的render()方法會返回一棵不同的樹。React會應用Diffing算法來高效的比較兩棵樹,算法過程如下。 &emsp;&emsp;(1)當根節點為不同類型的元素時,React會拆卸原有的樹,銷毀對應的DOM節點和關聯的State、卸載子組件,最后再創建新的樹。 &emsp;&emsp;(2)當比對兩個相同類型的DOM元素時,會保留DOM節點,僅比對變更的屬性。 &emsp;&emsp;(3)當比對兩個相同類型的組件元素時,組件實例保持不變,更新該組件實例的Props。 &emsp;&emsp;(4)當遞歸DOM節點的子元素時,React會同時遍歷兩個子元素的列表,比對相同位置的元素,性能比較低效。 &emsp;&emsp;(5)在給子元素添加唯一標識的key屬性后,就能只比對變更了key屬性的元素。 **2)Fiber Reconciler** &emsp;&emsp;JavaScript與樣式計算、界面布局等各種繪制,一起運行在瀏覽器的主線程中,當JavaScript運行時間過長時,將占用整個線程,阻塞其它任務。為了能在React渲染期間回到主線程執行其它任務,在React v16中提出了Fiber Reconciler,并將其設為默認的Reconciler,解決了過去Stack Reconciler中的固有問題和遺留的痛點,提高了動畫、布局和手勢等領域的性能。Fiber Reconciler的主要目標是: &emsp;&emsp;(1)暫停和切分渲染任務,并將分割的任務分布到各個幀中。 &emsp;&emsp;(2)調整優先級,并重置或復用已完成的任務。 &emsp;&emsp;(3)在父子元素之間交錯處理,以支持React中的布局。 &emsp;&emsp;(4)在render()方法中返回多個元素。 &emsp;&emsp;(5)更好地支持錯誤邊界。 **3)調度任務** &emsp;&emsp;Fiber可以分解任務,根據優先級將任務調度到瀏覽器提供的兩個全局函數中,如下所列。 &emsp;&emsp;(1)[requestAnimationFrame](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame):在下一個動畫幀上執行高優先級的任務。 &emsp;&emsp;(2)[requestIdleCallback](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestIdleCallback):在線程空閑時執行低優先級的任務。 &emsp;&emsp;當網頁保持在每秒60幀(1幀約為16ms)時,整體會變得很流暢。在每個幀中調用requestAnimationFrame()執行高優先級的任務;而在兩個幀之間會有一小段空閑時間,此時可執行requestIdleCallback()中的任務,該函數包含一個deadline參數(截止時間),用于切分長任務。 **4)Fiber數據結構** &emsp;&emsp;在調和期間,從render()方法得到的每個React元素都需要升級為Fiber節點,并添加到Fiber節點樹中。而與React元素不同,Fiber節點可復用,不會在每次渲染時重新創建。Fiber的數據結構大致如下,省略了部分屬性,源碼來自于[packages/react-reconciler/src/ReactFiber.js](https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiber.js)。 ~~~ export type Fiber = { tag: WorkTag, key: null | string, elementType: any, type: any, stateNode: any, return: Fiber | null, child: Fiber | null, sibling: Fiber | null, ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject, effectTag: SideEffectTag, nextEffect: Fiber | null, firstEffect: Fiber | null, lastEffect: Fiber | null, expirationTime: ExpirationTime, alternate: Fiber | null, ... }; ~~~ &emsp;&emsp;return、child和sibling三個屬性分別表示父節點、第一個子節點和兄弟節點,通過它們使得Fiber節點能夠基于鏈表連接在一起。假設有個ClickCounter組件,包含和兩個元素,它們三者之間的關系如圖12所示。 ~~~ class ClickCounter extends React.Component { render() { return [ <button>Update counter</button>, <span>10</span> ]; } } ~~~ :-: ![](https://img.kancloud.cn/09/48/09482bf938a075b4af69b4954d03d132_422x177.jpg) 圖 12 節點關系 &emsp;&emsp;使用alternate屬性雙向連接當前Fiber和正在處理的Fiber(workInProgress),如下代碼所示,當需要恢復時,可通過alternate屬性直接回退。 ~~~ let workInProgress = current.alternate; if (workInProgress === null) { workInProgress.alternate = current; current.alternate = workInProgress; } ~~~ &emsp;&emsp;到期時間(ExpirationTime)是指完成此任務的時間,該時間越短,則優先級越高,需要盡早執行,具體邏輯在同目錄的[ReactFiberExpirationTime.js](https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberExpirationTime.js)中。 ## 四、生命周期鉤子方法 &emsp;&emsp;React在內部執行時會分為兩個階段:render和commit。 &emsp;&emsp;在第一個render階段(phase)中,React持有標記了副作用(side effect)的Fiber樹并將其應用于實例,該階段不會發生用戶可見的更改,并且可異步執行,下面列出的是在render階段執行的生命周期鉤子方法 &emsp;&emsp;(1)\[UNSAFE\_\]componentWillMount(棄用) &emsp;&emsp;(2)\[UNSAFE\_\]componentWillReceiveProps(棄用) &emsp;&emsp;(3)getDerivedStateFromProps &emsp;&emsp;(4)shouldComponentUpdate &emsp;&emsp;(5)\[UNSAFE\_\]componentWillUpdate(棄用) &emsp;&emsp;(6)render &emsp;&emsp;標有UNSAFE的生命周期有可能被執行多次,并且經常被誤解和濫用,例如在這些方法中執行副作用代碼,可能出現渲染問題,或者任意操作DOM,可能引起回流(reflow)。于是官方推出了靜態的getDerivedStateFromProps()方法,可限制狀態更新以及DOM操作。 &emsp;&emsp;在第二個commit階段,任務都是同步執行的,下面列出的是commit階段執行的生命周期鉤子方法,這些方法都只執行一次,其中getSnapshotBeforeUpdate()是新增的,用于替換componentWillUpdate()。 &emsp;&emsp;(1)getSnapshotBeforeUpdate &emsp;&emsp;(2)componentDidMount &emsp;&emsp;(3)componentDidUpdate &emsp;&emsp;(4)componentWillUnmount &emsp;&emsp;新的流程將變成圖13這樣。 :-: ![](https://img.kancloud.cn/92/a3/92a369f6f9934e2683251045ff781d24_2048x1116.jpg) 圖 13 新的流程 ***** > 原文出處: [博客園-React躬行記](https://www.cnblogs.com/strick/category/1455720.html) [知乎專欄-React躬行記](https://zhuanlan.zhihu.com/pwreact) 已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。 ![](https://box.kancloud.cn/2e1f8ecf9512ecdd2fcaae8250e7d48a_430x430.jpg =200x200) 推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看