摘要:然而之前的相當于從最頂層的組件開始,自頂向下遞歸調用,不會被中斷,這樣就會持續占用瀏覽器主線程。眾所周知,是單線程運行,長時間占用主線程會阻塞其他類似于樣式計算布局繪制等運算,從而出現掉幀的情況。
前言
首先歡迎大家關注我的Github博客,也算是對我的一點鼓勵,畢竟寫東西沒法獲得變現,能堅持下去也是靠的是自己的熱情和大家的鼓勵,希望大家多多關注呀!從今年年初離開React開發崗,React就慢慢淡出我的學習范圍?,F在想重拾一下React相關的知識,可能文章所提及的知識點已經算是過時了,僅僅算作是自己的學習體驗吧,
React 16.0發布于2017年九月,此次新版本作為一次大的版本升級,為我們許多新特性以及全新的內部架構,分別了解一下:
新的JavaScript環境支持React依賴于ES6中的Map與Set類型以及requestAnimationFrame函數(requestAnimationFrame函數用來告知瀏覽器在每次動畫重繪之前都調用給定的回調函數),如果你需要支持IE11以下的老版本瀏覽器和設備,React原生不再提供支持,必須引入polyfill。
對于Map與Set,我們可以在全局引入core-js處理,對于requestAnimationFrame而言,我們可以通過引入raf:
import "core-js/es6/map"; import "core-js/es6/set"; import "raf/polyfill"; import React from "react"; import ReactDOM from "react-dom"; ReactDOM.render(新特性 組件返回Hello, world!
, document.getElementById("root") );
React之前的版本中,組件render的返回值必須包含在一個根元素,因此我們經常都是將其包裹在一個div標簽中,在React16中我們直接在render函數中返回字符串和數組。
比如存在下面的場景,假設有以下兩個組件:
class Row extends Component{ render() { return (); } } class Table extends Component{ render() { return (React Vue Angular
在之前的版本中組件僅能返回一個根組件,Row中的組件不得已只能用div標簽包裹,但是因為td被div包裹會導致瀏覽器無法識別,當然我們可以將tr挪到Row中,但是React 16.0提供了直接返回數組的形式,因此我們可以直接方便的寫成:
class Row extends Component{ render() { return [React ,Vue ,Angular ]; } }
在組件中直接返回字符串相當于直接創建匿名文本。
異常處理處理React 16.0 增強了異常的處理能力,在之前的React中,組件內部的錯誤可能會使得狀態發生錯亂從而導致下一次渲染發生未知的錯誤,然而React沒有提供能優雅地捕捉這些錯誤并且從中恢復的方式。試想,部分程序的錯誤不應該干擾整個應用的流程,因而React16引入了新的概念: Error boundaries(錯誤邊界)。
所謂的錯誤邊界(Error boundaries )是指能夠捕獲子孫組件中錯誤,并提供打印這些錯誤和展示錯誤UI界面的組件。錯誤邊界能夠捕捉子孫組件render方法、生命周期以及構造函數中的錯誤。
舉個例子:
class MyComponent extends Component { render(){ throw new Error("I crashed!"); return "MrErHu"; } } class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { this.setState({ hasError: true }); } render() { if (this.state.hasError) { returnSomething went wrong.
; } return this.props.children; } } export default class App extends Component { render() { return (); } }
如上所示,含有componentDidCatch的組件被稱為錯誤邊界,其功能類似于JavaScript中的catch。值得注意是的,錯誤邊界僅僅能夠捕捉子孫組件的錯誤而不誤捕獲自身的錯誤。React 16.0引入了一個新的行為,任何未被捕獲的錯誤都會卸載整個React組件樹,雖然這個行為富有爭議,但React開發者們認為即使什么也不顯示,也比顯示一堆錯誤更好。當然了,錯誤邊界僅能捕捉我們上面所提到特定位置的錯誤,如果是事件處理中的錯誤,你還是得使用JavaScript的try和catch。
createPortalReact 16之前,并沒有提供Portal的功能,如果需要渲染類似于對話框的組件則必須借助于unstable_renderSubtreeIntoContainer與unmountComponentAtNode,例如我們想要實現一個對話框Dialog的組件:
class Dialog extends React.Component { render() { return null; } componentDidMount() { const doc = window.document; this.node = doc.createElement("div"); doc.body.appendChild(this.node); this.renderPortal(this.props); } componentDidUpdate() { this.renderPortal(this.props); } componentWillUnmount() { unmountComponentAtNode(this.node); window.document.body.removeChild(this.node); } renderPortal(props) { unstable_renderSubtreeIntoContainer( this,{props.children}, this.node ); } }
我們知道對話框是非常特殊的一種情況,不能渲染在父組件內而是需要直接渲染在body標簽下,為了解決了這個問題,在上面的代碼中render實際上并沒有返回任何組件,而是在componentDidMount生命周期中利用unstable_renderSubtreeIntoContainer方法將對應組件直接渲染在this.node下。需要注意的是,unstable_renderSubtreeIntoContainer渲染的組件需要手動卸載,否則可能會造成內存泄露,因此我們在componentWillUnmount中手動調用unmountComponentAtNode。
有ReactDom.createPortal,一切都變得簡單的起來,既不需要手動去卸載組件,也不需要擔心unstable的API會在后續的版本中移出,上面的例子,在React 16.0可以如下實現:
class Dialog extends React.Component { constructor(props) { super(props); const doc = window.document; this.node = doc.createElement("div"); doc.body.appendChild(this.node); } render() { return createPortal(renderToNodeStream{this.props.children}, this.node ); } componentWillUnmount() { window.document.body.removeChild(this.node); } }
React服務器渲染在React 16.0之前僅僅支持renderToString,后端用字符串的方式將渲染好的HTML發送給客戶端,而React 16.0則提供了renderToNodeStream,返回一個可讀流,二者有什么區別?
// using renderToString import { renderToString } from "react-dom/server" import App from "./App" app.get("/", (req, res) => { res.write("App "); res.write(""); res.write(renderToString("); res.end(); });)); res.write("
// using renderToNodeStream import { renderToNodeStream } from "react-dom/server" import App from "./App" app.get("/", (req, res) => { res.write("App "); res.write(""); const stream = renderToNodeStream("); res.end(); }); });); stream.pipe(res, { end: false }); stream.on("end", () => { res.write("
回答這個問題之前,我們需要了解一下什么是流(Stream),對于從事前端的同學而言,流這個概念相對比較陌生,流本質上是對輸入輸出設備的抽象,比如:
ls | grep *.js
ls產生的數據通過管道符號(|)流向了grep命令中,數據就像水流一樣在管道符號中流動。設備流向程序我們稱為readable,程序流向設備我們稱為writable,我們舉一個例子:
const fs = require("fs"); const FILEPATH = "./index"; const rs = fs.createReadStream(FILEPATH); const ws = fs.createWriteStream(DEST); rs.pipe(ws);
數據通過管道中從rs流向了ws,實現了復制的功能,并且數據在管道流動的過程中我們還可以對數據進行處理。那么流有哪些優點呢?首先數據不需要一次性從設備全部拿出,然后再寫入另外一個設備。流可以實現一點點的放入內存中,一點點的存入設備,帶來的就是內存開銷的下降。并且我們可以在管道中優雅的處理數據,方便程序拓展。
講了這么多流的優點,renderToNodeStream為服務器渲染帶來了什么呢?首先同樣的道理,renderToNodeStream可以降低渲染服務器的內存消耗,更重要的是帶來TTFB的降低。
TTFB(Time to First Byte):瀏覽器從最初的網絡請求被發起到從服務器接收到第一個字節前所花費的毫秒數
我們知道HTTP協議在傳輸層使用的TCP協議,而TCP協議每次會將應用層數據切割成一個個報文傳輸,因此使用流不必等待所有的渲染完成才傳輸,可以有效降低TTFB。
非標準DOM屬性的支持在React 16之前,React會忽視非標準DOM屬性,例如:
在React 15中僅會輸出:
在React 16中則會輸出:
允許使用非標準DOM屬性使得在集成第三方庫或者嘗試新的DOM API時更加的方便。
其他變化關于setState函數,setState(null)將不會再觸發更新,因此如果是以函數作為參數的形式調用setState,可以通過返回null的方式控制組件是否重新渲染,例如:
this.setState(function(state) { return null; })
需要注意的是,與之前不同,如果在render中直接調用setState會觸發更新,當前實際的情況是,你也不應該在render中直接觸發setState。并且,之前的setState的回調函數(第二個參數)是在所有組件重新渲染完之后調用,而現在會在componentDidMount和componentDidUpdate后立即調用。
關于生命周期中,如果一個組件從被替換成,那么React 16中B組件的componentWillMount一定總是先于A組件的componentWillUnmount,但是在React 16之前的版本某些情況下可能是相反的順序。還有,componentDidUpdate方法不會再接收到prevContext的參數。
關于React FiberReact歷經兩年的核心代碼重構,在16.0中推出了矚目的React Fiber。
React最引以自豪的應該就是Virtual Dom了,Virtual Dom的運用首先使得我們前端編碼的難度大大降低,所需要考慮的只有在特定狀態描述UI界面,也不需要考慮瀏覽器該如何處理。其次,正是因為Virtual Dom的引入,使得React具備了跨平臺的能力,既可以在瀏覽器運行(React Dom),也可以在移動端設備上運行(React Native),也就是React所宣稱的:
Write once, run anywhere
順著這個思路往下走,其實React的實現分為兩個部分:
不同狀態下不同的UI描述,React需要對比前后UI描述的差異性,明白界面到底實際發生了什么改變,這個過程在React中被稱為Reconciler。React 16.0版本之前屬于Stack Reconciler,現在則是Fiber Reconcile。
第二個則是Virtual Dom對真實環境的映射,在React Dom中是對瀏覽器的映射,在移動端是對特定平臺(iOS、Andriod)的映射,這部分屬于插件式實現,并不屬于React核心代碼。
正如上圖所示,React運行時首先會根據返回的JSX創建對應的Element,用以描述UI界面。然后通過Element則會對應創建組件實例Instance,也就是我們所說的Virtual Dom,最后通過Virtual Dom去映射真實的瀏覽器環境。在首次渲染之后,后序的更新Reac只需要找到(Reconciler)兩次Virtual Dom的差異性(diff),然后通過diff去更新真實DOM,這樣就實現了增量更新真實DOM,畢竟DOM的操作是非常昂貴的。
然而之前的Stach Reconcile相當于從最頂層的組件開始,自頂向下遞歸調用,不會被中斷,這樣就會持續占用瀏覽器主線程。眾所周知,JavaScript是單線程運行,長時間占用主線程會阻塞其他類似于樣式計算、布局繪制等運算,從而出現掉幀的情況。
Fiber Reconcile力圖解決這個問題,通過將Reconcile進行拆分成一個個小任務,當前任務執行結束后即使還有后序任務沒有執行,也會主動交還主線程的控制權,暫時將自己掛起,等到下次獲得主線程的控制權時再繼續執行,不僅如此,Fiber還可以對任務通過優先級進行排序,優先進行那些至關重要的操作,是不是非常類似操作系統的進程調度算法。這樣做的好處就是其他類似于頁面渲染的操作也能獲得執行,避免因此造成卡頓。
當然至于Fiber是如何實現如此強大的功能,已經超過文章的討論范圍,目前也超過了本人的能力范圍。不過,React 16帶來的性能改善和一系列新特性都讓我欣喜。重新使用React,看到如此多的變化,不禁想說一句:真香!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99848.html
摘要:修復了這一點,引入了的概念,中文譯為錯誤邊界,當某個組件發生錯誤時,我們可以通過捕獲到錯誤并對錯誤做優雅處理。擴展采用了最新的技術,你可以在點擊這里來閱讀官方文檔暫時就這么多,后續發現更多我再進行更新,希望大家喜歡 React v16.0 September 26, 2017 by Andrew Clark 新版本的render可以返回數組、字符串、react組件、數字、boolean...
摘要:大約一年前,團隊發布了。時至今日,已更新到。這其中有不少激動人心的特性如架構的引入新的周期函數全新等都值得開發者跟進學習。本文就以更新日志為引,選取幾個重要且用于工作的更新,和大家一起學習。所有示例代碼在配合文章一起食用更佳 大約一年前,React 團隊發布了 React 16.0。時至今日,已更新到 16.5 。這其中有不少激動人心的特性(如 Fiber 架構的引入、新的周期函數、全...
摘要:又雙叒更新啦這次是,其實在前段時間就知道最近要發布了。協議更新了。。。這樣做是為了阻止損壞數據的顯示。協議的協議已經是協議了,當然,也把已經發布的頁改成協議了。 React 又雙叒更新啦~ 這次是React v16.0,其實在前段時間就知道最近要發布了。協議更新了。。。來看看其他的變化吧。自己看著玩的。。期待官方中文文檔的更新。。 原文地址:React v16.0 我們很高興地宣布發...
閱讀 3461·2023-04-26 02:48
閱讀 1465·2021-10-11 10:57
閱讀 2490·2021-09-23 11:35
閱讀 1196·2021-09-06 15:02
閱讀 3294·2019-08-30 15:54
閱讀 1612·2019-08-30 15:44
閱讀 879·2019-08-30 15:44
閱讀 988·2019-08-30 12:52