国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

為什么你應(yīng)該放棄老的React Context API改用新的Context API

william / 1274人閱讀

摘要:發(fā)布了新的,并且已經(jīng)確認(rèn)了將在下一個(gè)版本廢棄老的。所以大家更新到新的是無可厚非的事情。

React16.3發(fā)布了新的Context API,并且已經(jīng)確認(rèn)了將在下一個(gè)版本廢棄老的Context API。所以大家更新到新的Context API是無可厚非的事情。而這篇文章會(huì)從原理的角度為大家分析為什么要用新的API--不僅僅是因?yàn)镽eact官方要更新,畢竟更新了你也可以用16版本的React來使用老的API--而是因?yàn)樾碌腁PI性能比老API 高出太多
用法

我們先來看一下兩個(gè)版本的Context API如何使用

// old version
class Parent extends Component{
  getChildContext() {
    return {type: 123}
  }
}

Parent.childContextType = {
  type: PropTypes.number
}

const Child = (props, context) => (
  

{context.type}

) Child.contextTypes = { type: PropTypes.number }

通過在父組件上聲明getChildContext方法為其子孫組件提供context,我們稱其ProviderComponent。注意必須要聲明Parent.childContextType才會(huì)生效,而子組件如果需要使用context,需要顯示得聲明Child.contextTypes

// new version
const { Provider, Consumer } = React.createContext("defaultValue")

const Parent = (props) => (
  
    {props.children}
  
)

const Child = () => {
  
    {
      (value) => 

{value}

}
}

新版本的API,React提供了createContext方法,這個(gè)方法會(huì)返回兩個(gè)組件ProviderConsumber,Provider用來提供context的內(nèi)容,通過向Provider傳遞value這個(gè)prop,而在需要用到對應(yīng)context的地方,用相同來源的Consumer來獲取context,Consumer有特定的用法,就是他的children必須是一個(gè)方法,并且context的值使用參數(shù)傳遞給這個(gè)方法。

性能對比

正好前幾天React devtool發(fā)布了Profiler功能,就用這個(gè)新功能來查看一下兩個(gè)API的新能有什么差距吧,先看一下例子

不知道Profiler的看這里

// old api demo
import React from "react"
import PropTypes from "prop-types"

export default class App extends React.Component {
  state = {
    type: 1,
  }

  getChildContext() {
    return {
      type: this.state.type
    }
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        type: this.state.type + 1
      })
    }, 500)
  }

  render() {
    return this.props.children
  }
}

App.childContextTypes = {
  type: PropTypes.number
}

export const Comp = (props, context) => {
  const arr = []
  for (let i=0; i<100; i++) {
    arr.push(

{i}

) } return (

{context.type}

{arr}
) } Comp.contextTypes = { type: PropTypes.number }
// new api demo
import React, { Component, createContext } from "react"

const { Provider, Consumer } = createContext(1)

export default class App extends Component {

  state = {
    type: 1
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        type: this.state.type + 1
      })
    }, 500)
  }

  render () {
    return (
      
        {this.props.children}
      
    )
  }

}

export const Comp = () => {
  const arr = []
  for (let i=0; i<100; i++) {
    arr.push(

{i}

) } return (
{(type) =>

{type}

}
{arr}
) }
// index.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";

import App, {Comp} from "./context/OldApi"

// import App, { Comp } from "./context/NewApi"

ReactDOM.render(
  ,
  document.getElementById("root")
)

代碼基本相同,主要變動(dòng)就是一個(gè)interval,每500毫秒給type加1,然后我們來分別看一下Profiler的截圖

不知道Profiler的看這里

老API

新API

可見這兩個(gè)性能差距是非常大的,老的API需要7點(diǎn)幾毫秒,而新的API只需要0.4毫秒,而且新的API只有兩個(gè)節(jié)點(diǎn)重新渲染了,而老的API所有節(jié)點(diǎn)都重新渲染了(下面還有很多節(jié)點(diǎn)沒截圖進(jìn)去,雖然每個(gè)可能只有0.1毫秒或者甚至不到,但是積少成多,導(dǎo)致他們的父組件Comp渲染時(shí)間很長)

進(jìn)一步舉例

在這里可能有些同學(xué)會(huì)想,新老API的用法不一樣,因?yàn)槔螦PI的context是作為Comp這個(gè)functional Component的參數(shù)傳入的,所以肯定會(huì)影響該組件的所有子元素,所以我在這個(gè)基礎(chǔ)上修改了例子,把數(shù)組從Comp組件中移除,放到一個(gè)新的組件Comp2

// Comp2
export class Comp2 extends React.Component {
  render() {
    const arr = []
    for (let i=0; i<100; i++) {
      arr.push(

{i}

) } return arr } } // new old api Comp export const Comp = (props, context) => { return (

{context.type}

) } // new new api Comp export const Comp = () => { return (
{(type) =>

{type}

}
) }

現(xiàn)在受context影響的渲染內(nèi)容新老API都是一樣的,只有

{type}

,我們再來看一下情況

老API

新API

忽視比demo1時(shí)間長的問題,應(yīng)該是我電腦運(yùn)行時(shí)間長性能下降的問題,只需要橫向?qū)Ρ刃吕螦PI就可以了

從這里可以看出來,結(jié)果跟Demo1沒什么區(qū)別,老API中我們的arr仍然都被重新渲染了,導(dǎo)致整體的渲染時(shí)間被拉長很多。

事實(shí)上,這可能還不是最讓你震驚的地方,我們再改一下例子,我們在App中不再修改type,而是新增一個(gè)statenum,然后對其進(jìn)行遞增

// App
export default class App extends React.Component {
  state = {
    type: 1,
    num: 1
  }

  getChildContext() {
    return {
      type: this.state.type
    }
  }

  componentDidMount() {
    setInterval(() => {
      this.setState({
        num: this.state.num + 1
      })
    }, 500)
  }

  render() {
    return (
      

inside update {this.state.num}

{this.props.children}
) } }
老API

新API

可以看到老API依然沒有什么改觀,他依然重新渲染所有子節(jié)點(diǎn)。

再進(jìn)一步我給Comp2增加componentDidUpdate生命周期鉤子

componentDidUpdate() {
  console.log("update")
}

在使用老API的時(shí)候,每次App更新都會(huì)打印

而新API則不會(huì)

總結(jié)

從上面測試的結(jié)果大家應(yīng)該可以看出來結(jié)果了,這里簡單的講一下原因,因?yàn)橐唧w分析會(huì)很長并且要涉及到源碼的很多細(xì)節(jié),所以有空再寫一片續(xù),來詳細(xì)得講解源碼,大家有興趣的可以關(guān)注我。

要分析原理要了解React對于每次更新的處理流程,React是一個(gè)樹結(jié)構(gòu),要進(jìn)行更新只能通過某個(gè)節(jié)點(diǎn)執(zhí)行setState、forceUpdate等方法,在某一個(gè)節(jié)點(diǎn)執(zhí)行了這些方法之后,React會(huì)向上搜索直到找到root節(jié)點(diǎn),然后把root節(jié)點(diǎn)放到更新隊(duì)列中,等待更新。

所以React的更新都是從root往下執(zhí)行的,他會(huì)嘗試重新構(gòu)建一個(gè)新的樹,在這個(gè)過程中能復(fù)用之前的節(jié)點(diǎn)就會(huì)復(fù)用,而我們現(xiàn)在看到的情況,就是因?yàn)閺?fù)用算法根據(jù)不同的情況而得到的不同的結(jié)果

我們來看一小段源碼

if (
  !hasLegacyContextChanged() &&
  (updateExpirationTime === NoWork ||
    updateExpirationTime > renderExpirationTime)
) {
  // ...
  return bailoutOnAlreadyFinishedWork(
    current,
    workInProgress,
    renderExpirationTime,
  );
}

如果能滿足這個(gè)判斷條件并且進(jìn)入bailoutOnAlreadyFinishedWork,那么有極高的可能這個(gè)節(jié)點(diǎn)以及他的子樹都不需要更新,React會(huì)直接跳過,我們使用新的context API的時(shí)候就是這種情況,但是使用老的context API是永遠(yuǎn)不可能跳過這個(gè)判斷的

老的context API使用過程中,一旦有一個(gè)節(jié)點(diǎn)提供了context,那么他的所有子節(jié)點(diǎn)都會(huì)被視為有side effect的,因?yàn)镽eact本身并不判斷子節(jié)點(diǎn)是否有使用context,以及提供的context是否有變化,所以一旦檢測到有節(jié)點(diǎn)提供了context,那么他的子節(jié)點(diǎn)在執(zhí)行hasLegacyContextChanged的時(shí)候,永遠(yuǎn)都是true的,而沒有進(jìn)入bailoutOnAlreadyFinishedWork,就會(huì)變成重新reconcile子節(jié)點(diǎn),雖然最終可能不需要更新DOM節(jié)點(diǎn),但是重新計(jì)算生成Fiber對象的開銷還是又得,一兩個(gè)還好,數(shù)量多了時(shí)間也是會(huì)被拉長的。

以上就是使用老的context API比新的API要慢很多的原因,大家可以先不深究得理解一下,在我之后的源碼分析環(huán)節(jié)會(huì)有更詳細(xì)的講解。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/97980.html

相關(guān)文章

  • react new context API的一次實(shí)踐(補(bǔ)充)

    摘要:作為數(shù)據(jù)的發(fā)布方,它擁有一個(gè)名為的屬性,用于維護(hù)數(shù)據(jù)內(nèi)容,通過傳遞給的數(shù)據(jù)會(huì)被發(fā)布出去。最后,組件將自己的原封不動(dòng)的傳遞給。但是通過這次的實(shí)踐,也算是熟悉的的用法,對也加深了了解吧。 這是一篇我發(fā)在掘金上的文章,原文有一個(gè)我沒有解決的問題,在網(wǎng)友的解答下我找到了答案,我把文章重新修改編輯后,同步發(fā)送到這里,希望能對大家有所幫助。本文原發(fā)布于掘金:https://juejin.im/po...

    kaka 評論0 收藏0
  • [譯]開發(fā)類 redux 庫來理解狀態(tài)管理

    摘要:第一個(gè)功能是普通經(jīng)典類組件,也就是眾所周知的有狀態(tài)組件。我們準(zhǔn)備創(chuàng)建一個(gè)上下文環(huán)境來存放全局狀態(tài),然后把它的包裹在一個(gè)有狀態(tài)組件中,然后用來管理狀態(tài)。接下來我們需要用有狀態(tài)組件包裹我們的,利用它進(jìn)行應(yīng)用狀態(tài)的管理。 原文地址對于想要跳過文章直接看結(jié)果的人,我已經(jīng)把我寫的內(nèi)容制作成了一個(gè)庫:use-simple-state,無任何依賴(除了依賴 react ),只有3kb,相當(dāng)輕量。 ...

    KoreyLee 評論0 收藏0
  • “別更新了,學(xué)不動(dòng)了” 之:全棧開發(fā)者 2019 應(yīng)該學(xué)些什么?

    摘要:但是,有一件事是肯定的年對全棧開發(fā)者的需求量很大。有一些方法可以解決這個(gè)問題,例如模式,或者你可以這么想,其實(shí)谷歌機(jī)器人在抓取單頁應(yīng)用程序時(shí)沒有那么糟糕。谷歌正在這方面努力推進(jìn),但不要指望在年會(huì)看到任何突破。 對于什么是全棧開發(fā)者并沒有一個(gè)明確的定義。但是,有一件事是肯定的:2019 年對全棧開發(fā)者的需求量很大。在本文中,我將向你概述一些趨勢,你可以嘗試根據(jù)這些趨勢來確定你可能要投入的...

    NervosNetwork 評論0 收藏0
  • “別更新了,學(xué)不動(dòng)了” 之:全棧開發(fā)者 2019 應(yīng)該學(xué)些什么?

    摘要:但是,有一件事是肯定的年對全棧開發(fā)者的需求量很大。有一些方法可以解決這個(gè)問題,例如模式,或者你可以這么想,其實(shí)谷歌機(jī)器人在抓取單頁應(yīng)用程序時(shí)沒有那么糟糕。谷歌正在這方面努力推進(jìn),但不要指望在年會(huì)看到任何突破。 對于什么是全棧開發(fā)者并沒有一個(gè)明確的定義。但是,有一件事是肯定的:2019 年對全棧開發(fā)者的需求量很大。在本文中,我將向你概述一些趨勢,你可以嘗試根據(jù)這些趨勢來確定你可能要投入的...

    sutaking 評論0 收藏0
  • “別更新了,學(xué)不動(dòng)了” 之:全棧開發(fā)者 2019 應(yīng)該學(xué)些什么?

    摘要:但是,有一件事是肯定的年對全棧開發(fā)者的需求量很大。有一些方法可以解決這個(gè)問題,例如模式,或者你可以這么想,其實(shí)谷歌機(jī)器人在抓取單頁應(yīng)用程序時(shí)沒有那么糟糕。谷歌正在這方面努力推進(jìn),但不要指望在年會(huì)看到任何突破。 對于什么是全棧開發(fā)者并沒有一個(gè)明確的定義。但是,有一件事是肯定的:2019 年對全棧開發(fā)者的需求量很大。在本文中,我將向你概述一些趨勢,你可以嘗試根據(jù)這些趨勢來確定你可能要投入的...

    ormsf 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<