標籤:

anujs1.4.0發布

anujs1.4.0發布

來自專欄數據體驗技術

經過三個月的埋頭苦幹,終於完成Fiber版的anujs。

主要特性有:

* 測試全部改成jest, 遷移官方測試用例。有許多迷你React吹得怎麼天花亂墜,但是生命周期鉤子的執行順序無法與官方保持一致,那麼就很難共享React龐大的資源。像深度使用React內部 機制的ReactRouter與antd就無法使用。

  • 支持新鉤子,包括getDerivedStateFromProps,getDerivedStateFromCatch, getSnapshotBeforeUpdate,componentDidCatch。新鉤子的誕生與已有的三個componentWillXXX 鉤子的廢棄是同步進行的,你在組件里定義了新鉤子,那舊鉤子就不會被調起。
  • 支持批量更新API,ReactDOM.unstable_batchedUpdates, 這原本在React15的事件回調執行setState就存在的一種優化技術。現在官方將它大眾化。
  • 將createClass移出核心庫,已經2018年了,應該接受es6的洗禮。
  • 重構錯誤邊界,只差一個用例就跑通官方測試了。
  • 重構受控組件,全部用例跑通。
  • 更新劃分目錄,源碼放在packages下,分成core, fiber與render。core對應react.js,包括一些公用方法與內置組件(Fragment組件, Portal組件, PureComponent組件, forwardRef高階組件)。fiber就是React16的非同步架構。render包含了不同的渲染器,dom, noop(測試用), server。職責分明,有利於後繼的調優。
  • 支持antd 99%組件,目前只發現transfer有點問題。

這裡著重說一下fiber版塊下的設計。

unbatch內置了一個Unbatch組件,它用來模擬React內部的unbatchedUpdates。

scheduleWork裡面有一個updateComponet方法,setState的真正實現,用於驅動某一棵樹的更新。ReactDOM.render(vdom, container, cb)也會調用它進行更新,不過在我們container與vdom中,我們放進了一個Unbatch組件。調用ReactDOM.render相當於調用了Unbatch組件的setState, setState有第二個可選參數cb, 也就相當於ReactDOM.render的第三個可選cb。updateComponet最裡面是scheduleWork方法,它按理是使用大名鼎鼎的requestIdleCallback,現在沒有實現,臨時糊弄一下大家。

let deadline = { didTimeout: false, timeRemaining() { return 2; },};function requestIdleCallback(fn) { fn(deadline);}Renderer.scheduleWork = function() { performWork(deadline);};

performWork的實現類似於早期的rAF動畫,發現還有任務沒完工,就繼續遞歸執行自身。

// rAF動畫的示範代碼var start = null;var element = document.getElementById(SomeElementYouWantToAnimate);element.style.position = absolute;function step(timestamp) { if (!start) start = timestamp; var progress = timestamp - start; element.style.left = Math.min(progress / 10, 200) + px; if (progress < 2000) { //遞歸執行自身 requestAnimationFrame(step); }}requestAnimationFrame(step)

performWork的代碼也是如此

function performWork(deadline) { //執行當前的所有任務,更新虛擬DOM與真實DOM workLoop(deadline); //忽略其他往macrotasks中添加任務的代碼。。。 //忽略其他往macrotasks中添加任務的代碼。。。 //忽略其他往macrotasks中添加任務的代碼。。。 if (macrotasks.length) { requestIdleCallback(performWork); }}

ReactFiber相當於偽造了一個瀏覽器,因此有自己調度器,事件列隊。於是你可以看到macrotasks,microtasks,batchedtasks, boundaries, effects等列隊。

  1. macrotasks,宏列隊,主進程,一個頁面只有一個, ReactDOM.render就會將第一個參數丟進去。

2. microtasks,微列隊,子進程,每棵虛擬DOM樹都有一個,放在根節點中。當組件執行setState後,它會找到根節點的microtasks,然後放進去。然後在下次喚起performWork時,會從所有根節點中收集它們。

3. batchedtasks,批量處理的任務,它們不能被合併。microtasks中的任務,都是由setState產生的,我們知道對某個組件進行多次setState,React在一次生命周期中會執行一次更新。batchedtasks則不一樣,它們是延後到下次生命周期,因此不能在這次生命周期中就被執行了。

4. boundaries,放著邊界組件,邊界組件會有很高的優先順序,確保它們下次在performWork中,加入macrotasks的最前面。

5. effects,它是commit階段執行的macrotasks 列隊。

workLoop有兩個DFS遍歷,reconcileDFS與commitDFS。reconcileDFS負責更新虛擬DOM,commitDFS負責更新真實DOM。為什麼強調使用DFS,因為這東西對我們存取context, container非常方便。

注意:

anujs改動比較大,導致原先的DevTool不能用,這也是後繼的工作重點了。

雖然這次改動這麼大,它的體積還是相當迷你的。

與1.3x一樣,它是對IE8友好的,它所有使用的es6新特性都可以被babel polyfill及使用es3ify優雅降級。目前已經有IE8專用的腳手架可用:gitee.com/menhal/React_

剩下就是需要大家同心協力發掘兼容性很好的React路由庫與UI庫。

RubyLouvre/anu

npm i anujs

webpack.config中如何代替原來用React編寫的項目

resolve: { alias: { react: anujs, react-dom: anujs, // 若要兼容 IE 請使用以下配置 // react: anujs/dist/ReactIE, // react-dom: anujs/dist/ReactIE, // 如果引用了 prop-types 或 create-react-class // 需要添加如下別名 prop-types: anujs/lib/ReactPropTypes, create-react-class: anujs/lib/createClass //如果你在移動端用到了onTouchTap事件 react-tap-event-plugin: anujs/lib/injectTapEventPlugin, }},

推薦閱讀:

將 React 應用優化到 60fps
React 16 發布,Facebook 如約解除了專利條款
React源碼分析3 — React生命周期詳解
關於在react中request到底是應該寫在哪裡?
react服務端渲染如何將數據同步到客戶端?

TAG:React |