中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

React組件性能怎么提升

發布時間:2023-03-11 15:31:39 來源:億速云 閱讀:203 作者:iii 欄目:開發技術

這篇文章主要介紹了React組件性能怎么提升的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇React組件性能怎么提升文章都會有所收獲,下面我們一起來看看吧。

    react組件的性能優化的核心是減少渲染真實DOM節點的頻率,減少Virtual DOM比對的頻率。

    組件卸載前執行清理操作

    在組件中為window 注冊的全局事件,以及定時器,在組件卸載前要清理掉。防止組件卸載后繼續執行影響應用性能。

    import React from 'react'
    import { useEffect } from 'react'
    import { observer } from 'mobx-react-lite'
    function TestAdvance () {
      useEffect(() => {
        let timer = setInterval(() => {
          console.log('定時器被觸發了')
        }, 1000)
        // 返回一個卸載時會被觸發的函數來對timer進行清理
        return () => clearInterval(timer)
      }, [])
      return <div>Test</div>
    }
    export default observer(TestAdvance)

    通過純組件提升組件性能(類組件)

    什么是純組件(PureComponent)

    純組件會對組件輸入的數據進行淺層比較,如果當前輸入數據和上次輸入數據相同,組件不會重新渲染。

    什么是淺層比較

    • 比較引用數據類型在內存中的引用地址是否相同;

    • 比較基本數據類型的值是否相同。

    如何實現純組件

    • 類組件集成 PureComponent 類

    • 函數組件使用 memo 方法

    import React from 'react'
    class App extends React.Component {
      constructor () {
        super()
        this.state = {
          person: { name: '張三', age: 20, job: 'waiter' }
        }
      }
      updateName () {
        setInterval(() => {
          this.setState({ person: { ...this.state.person, name: '張三' } })
        }, 3000)
      }
      componentDidMount () {
        console.log('componentDidMount')
        this.updateName()
      }
      render () {
        return (
          <div>
            <RegularComponent name={this.state.person.name} />
            <PureComponent name={this.state.person.name} />
          </div>
        )
      }
    }
    class RegularComponent extends React.Component {
      render () {
        console.log('RegularComponent')
        return <div>{this.props.name}</div>
      }
    }
    class PureComponent extends React.PureComponent {
      render () {
        console.log('PureComponent')
        return <div>{this.props.name}</div>
      }
    }
    export default App

    淺層比較和深度diff的性能對比,為什么需要先進行淺層比較,而不直接進行diff比較呢。

    和進行diff 比較相比,淺層比較將消耗更少的性能。diff 操作會重新遍歷整棵 Virtual DOM樹,而淺層比較只操作當前組件的 state 和 props。

    React組件性能怎么提升

    可以看到PureComponent 當狀態值沒有改變時是不會被重新渲染的。

    通過shouldComponentUpdate生命周期函數提升組件性能

    shouldComponentUpdate是類組件當中的一個生命周期函數,它允許我們在這個方法當中通過返回true 或者 false,來決定是否要重新渲染組件。

    純組件只能進行淺層比較,要進行深層比較的話,需要使用到shouldComponentUpdate,它用于編寫自定義比較邏輯。

    函數的第一個參數時nextProps, 第二個參數是nextState。

    在內存中當中有兩個對象,即使這兩個對象的長得一摸一樣,實際上它們有不同的引用地址,這個時候再怎么比較他們都不相同。

    在我們的自定義邏輯中,如果返回true,則需要重新渲染組件;如果返回false,則不需要重現渲染組件了。

    import React from 'react'
    class App extends React.Component {
      constructor () {
        super()
        this.state = {
          person: { name: '張三', age: 20, job: 'waiter' }
        }
      }
      updateName () {
        setInterval(() => {
          this.setState({ person: { ...this.state.person, job: 'Writer' } })
        }, 3000)
      }
      componentDidMount () {
        console.log('componentDidMount')
        this.updateName()
      }
      /* shouldComponentUpdate (nextProps, nextState) {
        if (
          nextState.person.age !== this.state.person.age ||
          nextState.person.name !== this.state.person.name
        ) {
          return true
        } else {
          return false
        }
      } */
      render () {
        return (
          <div>
            <RegularComponent name={this.state.person.name} />
            <PureComponent name={this.state.person.name} />
          </div>
        )
      }
    }
    class RegularComponent extends React.Component {
      render () {
        console.log('RegularComponent')
        return <div>{this.props.name}</div>
      }
      shouldComponentUpdate (nextProps, nextState) {
        console.log(this.props, nextProps)
        if (nextProps.name !== this.props.name) {
          return true
        } else {
          return false
        }
      }
    }
    class PureComponent extends React.PureComponent {
      render () {
        console.log('PureComponent')
        return <div>{this.props.name}</div>
      }
    }
    export default App

    函數組件使用memo 減少渲染次數

    memo的基本使用

    將函數組件變為純組件,將當前props和上一次的props進行淺層比較,如果相同就阻止組件重新渲染。

    // 未使用memo方法包裹組件
    import React, { useEffect, useState } from 'react'
    function App () {
      const [name] = useState('張三')
      const [index, setIndex] = useState(0)
      useEffect(() => {
        let timer = setInterval(() => {
          setIndex(prev => prev + 1)
        }, 1000)
        return () => {
          clearInterval(timer)
        }
      }, [index])
      return (
        <div>
          {index}
          <ShowName name={name} />
        </div>
      )
    }
    function ShowName ({ name }) {
      console.log('render...')
      return <div>{name}</div>
    }
    export default App

    React組件性能怎么提升

    使用memo封裝子組件,讓子組件減少不必要的渲染。

    // 使用memo封裝子組件
    import React, { memo, useEffect, useState } from 'react'
    function App () {
      const [name] = useState('張三')
      const [index, setIndex] = useState(0)
      useEffect(() => {
        let timer = setInterval(() => {
          setIndex(prev => prev + 1)
        }, 1000)
        return () => {
          clearInterval(timer)
        }
      }, [index])
      return (
        <div>
          {index}
          <ShowName name={name} />
        </div>
      )
    }
    const ShowName = memo(function ({ name }) {
      console.log('render...')
      return <div>{name}</div>
    })
    export default App

    React組件性能怎么提升

    可以看到僅初次渲染時,渲染了一次。

    為memo 方法傳遞自定義比較邏輯

    在memo方法內部,其實也是進行的淺層比較。這個淺層比較對于引用數據類型來說,比較的是數據的引用地址。所以如果遇到引用數據類型的話,我們需要去傳遞自定義比較邏輯。

    memo方法是可以接收第二個參數的,第二個參數是一個函數,我們可以通過這個函數參數來編寫我們自定義比較邏輯。該函數參數接收兩個參數,分別是prevProps和nextProps。

    與shouldComponentUpdate的返回值邏輯相反,返回true,則不重新渲染;返回false的話則需要重新渲染。如果想讓這個組件重新渲染的話就返回false,否則返回true。

    // 給memo傳遞第二個參數,自定義比較邏輯
    import React, { memo, useEffect, useState } from 'react'
    function App () {
      const [person, setPerson] = useState({ name: '張三', age: 20, job: 'waiter' })
      const [index, setIndex] = useState(0)
      useEffect(() => {
        let timer = setInterval(() => {
          setIndex(prev => prev + 1)
          setPerson({ ...person, job: 'chef' })
        }, 1000)
        return () => {
          clearInterval(timer)
        }
      }, [index, person])
      return (
        <div>
          {index}
          <ShowName person={person} />
        </div>
      )
    }
    function compare (prevProps, nextProps) {
      if (
        prevProps.person.name !== nextProps.person.name ||
        prevProps.person.age !== nextProps.person.age
      ) {
        return false
      }
      return true
    }
    const ShowName = memo(function ({ person }) {
      console.log('render...')
      return (
        <div>
          {person.name} {person.age}
        </div>
      )
    }, compare)
    export default App

    React組件性能怎么提升

    通過組件懶加載提供應用性能

    使用組件懶加載,可以減少bundle 文件大小,加快組件呈遞速度。

    路由組件懶加載

    import React, { lazy, Suspense } from 'react'
    import { BrowserRouter, Routes, Route, Link } from 'react-router-dom'
    // import Home from './pages/Home'
    // import List from './pages/List'
    // import NotFound from './pages/NotFound'
    const Home = lazy(() => import(/* webpackChunkName: "Home" */ './pages/Home'))
    const List = lazy(() => import(/* webpackChunkName: "List" */ './pages/List'))
    const NotFound = lazy(() => import('./pages/NotFound'))
    function App () {
      return (
        <BrowserRouter>
          <Link to='/'>首頁 </Link>
          <Link to='/list'>列表頁</Link>
          <Suspense fallback={<div>loading...</div>}>
            <Routes>
              <Route path='/' element={<Home />} />
              <Route path='/list' element={<List />} errorElement={<NotFound />} />
            </Routes>
          </Suspense>
        </BrowserRouter>
      )
    }
    export default App

    React組件性能怎么提升

    可以看到,List頁面chunk 只有在頁面被加載渲染時才被被請求下載。

    根據條件進行組件懶加載

    適用于組件不會隨條件頻繁切換。

    import React, { lazy } from 'react'
    import { Suspense } from 'react'
    function Test () {
      let LazyComponent = null
      if (false) {
        LazyComponent = lazy(() =>
          import(/* webpackChunkName: "Home-Test" */ './Home')
        )
      } else {
        LazyComponent = lazy(() =>
          import(/* webpackChunkName: "List-Test" */ './List')
        )
      }
      return (
        <Suspense fallback={<div>Test loading...</div>}>
          <LazyComponent />
        </Suspense>
      )
    }
    export default Test

    通過使用占位符標記提升React組件的渲染性能

    使用Fragment 避免額外標記

    React組件中返回的jsx 如果有多個同級元素,多個同級元素必須要有一個共同的父級。

    <div>
    ...
    </div> 
    // 上面會多出一個無意義標記
    // 應該改為 
    <fragment>
    ...
    </fragment>
    // 或者寫成下面這樣也是可以的
    <>
    ...
    </>

    為了滿足上面的條件,我們通常都會在最外層添加一個div,但是這樣的話就會多出一個無意義的標記。如果每個組件都多出這樣的一個無意義標記的話,瀏覽器渲染引擎的負擔就會加劇。

    為了解決這個問題,react推出了fragment占位符標記。使用占位符標記,既滿足了擁有共同的父級的要求,又不會多出額外的無意義標記。

    另外,fragment標記對也可以簡寫成 :<></>

    通過避免使用內聯函數提升組件性能

    因為在使用內聯函數后,render 方法每次進行時都會創建該函數的新實例,導致 React 在進行Virtual DOM比對時,新舊函數比對不相等,導致總是為元素綁定新的函數實例,而舊的函數實例又要交給垃圾回收器處理。

    正確的做法是:在組件中單獨定義函數,將函數綁定給事件。

    render(){return(<input onChange={e => this.setState({inputValue: e.target.value})} />)}
    // 在類組件中,應該采用下面的方式來改寫從而避免該元素被重新渲染
    setInputvalue = e => {
        this.setState({inputValue: e.target.value})
    }
    render(){
        return (<input onChange={this.setInputValue} />)
    }

    這樣一來,無論render方法被重新執行多少次,類的屬性是不會發生變化的,所以在這個地方即使render方法被重新執行n次,那它每次都不會產生新的函數實例,所以它每次不會給onChange去添加新的函數。

    在構造函數中進行this指向的更正

    在類組件中如果使用fn(){}這種方式定義函數,函數this默認指向 undefined 。也就是說函數內部的 this 指向需要被更正。

    可以在構造函數中對函數的this 進行更正,也可以在行內進行更正。兩者看起來沒有太大的區別,但是對性能的影響是不同的。

    對于行內更正來說,每一次render方法在執行的時候它都會調用bind方法生成新的函數實例,也就是上邊提到的內聯函數對性能的影響是一樣的。

    因此比較推薦的是在構造函數當中去更正this的指向。因為構造函數只執行一次,也就是函數的this 指向只更正一次,效率較高。

    import React, { Component } from 'react'
    export default class index extends Component {
      constructor () {
        super()
        // 方法一:(推薦使用)
        // 構造函數只執行一次,所以函數this 指向更正的代碼也只執行一次。
        this.handleClick = this.handleClick.bind(this)
      }
      handleClick () {
        console.log(this)
      }
      handleClick2= () {
        console.log(this)
      }
      render () {
        // 方式二:跟內聯函數類似,不推薦,應避免使用
        // 問題:render 放啊每次執行時都會調用bind方法生成新的函數實例。
        return <button onClick={this.handleClick.bind(this)}>按鈕</button>
      }
    }

    類組件中的箭頭函數

    在類組件中使用箭頭函數不會存在this 指向問題。因為箭頭函數本身并不綁定this.

    handleClick2= ()=> console.log(this)

    箭頭函數在this 指向問題上是比較占優勢的,但是同時也有不利的一面。

    當使用箭頭函數時,該函數被添加為類的實例對象屬性,而不是原型對象屬性。如果組件被多次重用,每個組件實例對象中都會有一個相同的函數實例,降低了函數實例的可重用性,造成了資源浪費。

    綜上所述, 更正函數內部this 指向的最佳做法是: 在構造函數中使用 bind 方法進行綁定。 10. 避免使用內聯樣式屬性以提升組件性能

    當使用內聯style 為元素添加樣式時,內聯style 會被編譯成 JavaScript 代碼,通過JavaScript代碼將樣式規則映射到元素的身上,瀏覽器就會花費更多的時間執行腳本和渲染UI,從而增加了組件的渲染時間。

    // 例如:這段代碼的樣式會涉及到腳本的執行,效率低,資源開銷大
    <div style={{background: 'red'}}>Style in line</div>// 例如:這段代碼的樣式會涉及到腳本的執行,效率低,資源開銷大
    <div style={{background: 'red'}}>Style in line</div>

    更好的辦法是:

    將CSS 文件導入樣式組件,能通過CSS直接做的事情就不要通過JavaScript去做。因為JavaScript操作DOM非常慢。而CSS默認開啟了GPU的渲染加速,更加高效。

    優化條件渲染以提升組件性能

    頻繁的掛載和卸載組件,是一項耗性能的操作。為了確保應用程序的性能,應該減少組件掛載和卸載的次數。

    在react中,我們經常會根據條件渲染不同的組件,條件渲染是一項必做的優化操作。

    import React from 'react'
    import Home from './Home'
    import List from './List'
    import Test from './Test'
    function App () {
      if (true) {
        return (
          <>
            <Test />
            <Home />
            <List />
          </>
        )
      } else {
        return (
          <>
            <Home />
            <List />
          </>
        )
      }
    }
    export default App

    在上面的代碼中,顯然這種大范圍的條件渲染不太合理,存在優化的空間,整個頁面隨著判斷條件改變而變化的部分只有Test 組件,因此,可以僅對Test組件進行條件渲染判斷,從而減少不必要的組件卸載和掛載的次數。

    import React from 'react'
    import Home from './Home'
    import List from './List'
    import Test from './Test'
    function App () {
      return (
        <>
          {true && <Test />}
          <Home />
          <List />
        </>
      )
    }
    export default App

    避免重復的無限渲染

    當應用程序狀態發生更改時,react 就會調用render 方法,如果在render 方法中繼續更改應用程序狀態,就會發生render 方法遞歸調用導致應用報錯。

    import React, { Component } from 'react'
    export default class index extends Component {
      constructor () {
        super()
        this.state = { name: '張三' }
        // 方法一:(推薦使用)
        // 構造函數只執行一次,所以函數this 指向更正的代碼也只執行一次。
        this.handleClick = this.handleClick.bind(this)
      }
      handleClick () {
        console.log(this)
      }
      render () {
        this.setState({ name: '李四' })
        // 方式二:跟內聯函數類似,不推薦,應避免使用
        // 問題:render 放啊每次執行時都會調用bind方法生成新的函數實例。
        return <button onClick={this.handleClick.bind(this)}>按鈕</button>
      }
    }

    React組件性能怎么提升

    與其他生命周期函數不同,render方法應該被作為純函數。

    這意味著,在render方法中不要做以下的事情:

    • 不要調用setState方法;

    • 不要使用其他手段查詢更改原生DOM元素;

    • 以及不要做其他更改應用程序的任何操作。

    render 方法對的執行要根據狀態的改變,這樣可以保持組件的行為和渲染方式一致。

    所以,在react當中,不要在render方法當中,不要在componentWillUpdate這個生命周期函數當中,不要在componentDidUpdated這個生命周期函數當中繼續調用setState方法去更新狀態。否則將導致重復的無限渲染,應用程序崩潰。

    為應用程序創建錯誤邊界

    默認情況下,組件渲染錯誤會導致整個應用程序的中斷,創建錯誤邊界可確保在特定組件發生錯誤時應用程序不會中斷。從而增加應用程序的健壯性(魯棒性)。

    錯誤邊界是一個React組件,可以捕獲子級組件在渲染時發生的錯誤,當錯誤發生時,可以將錯誤記錄下來,可以顯示備用UI界面。

    錯誤邊界涉及兩個生命周期函數,分別為:getDerivedStateFromError 和 componentDidCatch.

    getDerivedStateFromError: 是一個靜態方法,方法中返回一個對象,該對象會和state 對象進行合并,用于更改應用程序的狀態,從而給我們提供顯示備用UI界面的機會。

    componentDidCatch:該方法用于記錄應用程序的錯誤信息,該方法的參數就是錯誤對象。

    import React, { Component } from 'react'
    import ErrorTrigger from '../components/ErrorTrigger'
    export default class ErrorBoundaries extends Component {
      constructor () {
        super()
        this.state = {
          hasError: false
        }
      }
      componentDidCatch (error) {
        // 可以將程序錯誤信息記錄到遠端服務器
        console.log('componentDidCatch')
      }
      static getDerivedStateFromError () {
        console.log('getDerivedStateFromError')
        return {
          // 該返回對象會和state 對象進行合并
          hasError: true
        }
      }
      render () {
        if (this.state.hasError) {
          return <>我是備用UI界面</>
        }
        return (
          <>
            <ErrorTrigger />
          </>
        )
      }
    }
    import React, { Component } from 'react'
    export class ErrorTrigger extends Component {
      render () {
        throw new Error('錯誤邊界內發生錯誤了')
        return <div>ErrorTrigger</div>
      }
    }
    export default ErrorTrigger

    注意??:錯誤邊界不能捕獲異步錯誤,例如點擊按鈕時發生的錯誤。

    React組件性能怎么提升

    React組件性能怎么提升

    避免數據結構突變

    組件中的props和state 的數據結構應該保持一致,數據結構突變會導致輸出不一致。

    import React, { Component } from 'react'
    export default class App extends Component {
      constructor () {
        super()
        this.state = {
          person: {
            name: '張三',
            age: 20,
            job: 'waiter'
          }
        }
      }
      render () {
        const { name, age, job } = this.state.person
        return (
          <>
            <p>
              {name} {age} {job}
            </p>
            <button
              onClick={() =>
                this.setState({
                  ...this.state,
                  person: {
                    age: 30
                  }
                })
              }
            >
              更新信息
            </button>
          </>
        )
      }
    }

    React組件性能怎么提升

    點擊更新信息按鈕前

    React組件性能怎么提升

    點擊更新信息按鈕后

    這是因為數據狀態在發生更改時,發生了數據結構突變導致的數據丟失。

    使用setState更改狀態代碼應該修改為:

    this.setState({
      ...this.state,
      // 這里的person 數據結構應該和原來的保持一致,避免數據丟失
      // person: {
      //   age: 30
      // }
      person: {
        ...this.state.person,
        age: 30
      }
    })

    React組件性能怎么提升

    修改更新數據保持結構一致的代碼后點擊更新信息按鈕后的顯示

    優化依賴項大小

    在程序應用開發中,常常會依賴第三方包,但我們不想引用包中所有的代碼,我們只想用到哪些代碼就包含哪些代碼。

    此時,可以使用插件對依賴項進行優化,優化資源。

    拿lodash舉例:

    應用基于create-react-app 腳手架創建:

    下載依賴

    npm install react-app-rewired customize-cra lodash babel-plugin-lodash

    react-app-rewired: 覆蓋create-react-app的默認配置

    module.exports = function(oldConfig){
      return newConfig
    }
    // 參數中的oldConfig 就是默認的webpack config

    customize-cra: 導出了一些輔助方法,可以讓以上寫法更加簡潔。

    const {override, useBabelRc} from 'customize-cra'
    module.exports = override(
    (oldConfig) => newConfig,
    (oldConfig) => newConfig
    )
    // override 可以接收多個參數,每個參數都是一個配置函數,函數接收oldConfig,返回 newConfig
    // useBabelRc 允許使用 .babelrc 文件進行 babel 配置

    babel-plugin-lodash: 對應用中的lodash 進行精簡

    在項目根目錄下新建 config-overrides.js 并加入配置代碼

    const {override, useBabelRc} from 'customize-cra'
    module.exports = override(useBabelRc())
    // 這里使用 .babelrc 文件進行 babel 配置

    修改package.json 中的文件構建命令

    把原來命令中的 react-scripts 改為 react-app-rewired

      "scripts": {
        "start": "react-app-rewired start",
        "build": "react-app-rewired build",
        "test": "react-app-rewired test",
        "eject": "react-scripts eject" 
      },

    創建 .babelrc 文件并加入配置

    {
        "plugins":["lodash"]
    }

    命令打包生成的生產環境下的三種js文件

    main.[hash].chunk.js 主應用程序代碼,App.js 等

    1.[hash].chunk.js 第三方依賴包的代碼,包含在node_modules中導入的模塊

    runtime~main.[hash].js webpack的運行時代碼

    優化前后結果比對:

    React組件性能怎么提升

    關于“React組件性能怎么提升”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“React組件性能怎么提升”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    舒兰市| 无极县| 巴塘县| 金山区| 保山市| 阿图什市| 沙河市| 曲沃县| 新和县| 郧西县| 广宗县| 南江县| 长宁县| 江阴市| 全州县| 报价| 菏泽市| 昌宁县| 基隆市| 五指山市| 邵阳市| 买车| 尼勒克县| 休宁县| 崇仁县| 中牟县| 什邡市| 金阳县| 凤山县| 青川县| 社旗县| 青岛市| 宿松县| 应用必备| 耿马| 富平县| 湘阴县| 深州市| 策勒县| 林州市| 大厂|