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等列隊。
- 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專用的腳手架可用:https://gitee.com/menhal/React_IE8_boilerplate
剩下就是需要大家同心協力發掘兼容性很好的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 |