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