[予約|翻訳]どのように重複したレンダリングを特定し、解決する方法が反応

転載及びNayeemレザの記事でfreecodeacmp翻訳:識別し、無駄な決意をレンダリングする方法で反応する
記述のリンクを:https://www.freecodecamp.org/news/how-to-identify-and-resolve-wasted-renders -in-反応-cc4b1e910d10を/

最近、私は私が開発しています、問題が突然、いくつかのパフォーマンス指標を設定することを考え、パフォーマンス分析アプリケーションを反応させると思います。私が発見した最初の事は、私がしたい各ページ・レンダリング・廃棄物問題を解決することです。あなたは、廃棄物をレンダリングしているものを疑問に思うかもしれませんか?さんはさらに見てみましょう。

最初は、Web開発の哲学を変更するだけでなく、フロントエンド開発者の考え方を変え反応します。VirtualDOMの導入後、更新UIは、効率的な可能Webアプリケーションの経験がきれいになります反応します。あなたがあなたのアプリケーションをより速く反応させることか考えたことはありますか?なぜ、中規模のWebアプリケーションやパフォーマンスの低下に反応?問題は、私たちが反応を使用する方法です。

それがどのように動作し反応

より迅速に私たちのアプリケーションを作成していない近代的なフロントエンドライブラリのように反応します。まず、開発者は、それがどのように動作するかリアクト理解する必要があります。アプリケーションのライフサイクル内のコンポーネントのライフサイクルを通じてどのように部品?だから、さらに最適化技術の前に、私たちはより良い理解を持っているに反応する方法で作業する必要があります。

コアに反応し、我々はJSX文法やビルド、比較VirtualDOM強力な機能を持っています。彼らのリリース後に、他の多くのフロントエンドライブラリに影響を与えた反応します。例えば、VueのもVirtualDOMに依存します。

各アプリケーションは、ルート成分から始まる反応します。我々は、各ノードが構成要素である、木構造などのアプリケーション全体であることができます。UIをレンダリングするために、データに基づいてコンポーネントを反応させます。ことを意味し、それは小道具や状態を受け取り、

UI = CF(data) //コンポーネント機能

データを変更するには、UIを介してユーザとの対話。ユーザーは、当社のアプリケーションですべてを行うことができ、インタラクティブです。たとえば:ボタン、スライド画像、ドラッグアンドドロップリストの項目をクリックし、APIへのAJAXリクエストを呼び出します。すべてのこれらの相互作用はデータのみを変更し、UIを変更することはありません。

ここでは、定義が唯一の状態データを適用します。私たちは、このようなボックスをチェックするかどうかを現在選択されているタブ、または金として、データベース内だけで何か、あるいは異なるフロントエンド状態にない存在し、すべてのデータの一部。データの変更は、唯一の事実上の再レンダリングするUIコンポーネントの機能によって反応するが、たび:

UI1 = CF(data1) UI2 = CF(data2)

異なる新しいVirtualDOM UIおよびUI(再レンダリング後のUI)DOMdiffアルゴリズムを流れる電流との間の比較を反応させます。

Changes = Difference(UI1, UI2)

次いで、成分の変化に関連するデータは、実際の必要性はDOMを更新するかどうかを決定反応するUIは、唯一のブラウザUIの実部に適用変化に反応します。このことを回避するには、例えば、ブラウザに多くの」expensive'DOM動作に反応:DOMノード、既存のノードへの不必要なアクセスを作成します。

コンポーネントとレンダリングを区別繰り返していずれかの主要な発生源の一つであり、アプリケーションのパフォーマンスの問題が反応します。DOMのdiffアルゴリズムの場合にアプリケーションを構築するためには反応を効果的に調整することができない、経験の遅い感覚を得る、その後、廃棄物をレンダリングされたアプリケーション全体のレンダリングを、繰り返すようにつながります。

最初のレンダリング処理では、このように作られたDOMに反応

我々はこの部分データ成分に直接依存し再レンダリングすることを期待する、あるいは他の成分の差分の処理をスキップし、変更データの一部を仮定する。再レンダリング、子コンポーネントA、B、C、Dの各再レンダリングされる場合、上記の図2.にR Bから送信された2部品データの変更、データ、Rにそこを仮定する。このプロセスは、実際にやってリアクトは(以前と比べるとノート)であります

上の図は、すべての黄色のノードは、時間の浪費につながり、コンピューティングリソース、再レンダリングとの差分です。これは、私たちの主な最適化---各コンポーネントは、必要に応じて再レンダリングおよび差分するように設定されています。このリサイクルは、CPUサイクルを無駄に。まず、私たちのアプリケーションが無駄にレンダリングするために識別する方法を調べてみましょう。

認識レンダリング廃棄物

識別するために、いくつかの異なる方法があります。最も簡単なオプションは、アップデートが中のdevのツールを反応させるのハイライト表示することです。

当与app交互时,就会有颜色的边框闪烁提示。这就能看出重复渲染的组件了。这可以让我们发现不需要的重渲染。

跟随下面的例子。

注意,当我们输入第二个todo项时,每次按键,第一个todo还在闪烁。这意味着它连同输入被React重新渲染。这就是所谓的“渲染浪费”。我们知道这是不必要的,因为第一个todo的内容并没有改变,但是React可不知道。

即使React只更新了改变的DOM节点,重渲染仍花了一些时间。在许多情况下,这并不是问题,但是当交互变得缓慢时,我们就要考虑以下阻止这些冗余渲染了。

使用shouldComponentUpdate方法

默认情况下,React会渲染虚拟DOM并在树中每个组件props和state变化时比较它们的不同。这显然是不合理的:随着应用的增长,每次变化都试图重新渲染并比较整个虚拟DOM最终会拖慢应用。

React提供了一个简单的生命周期方法shouldComponentUpdate来指示一个组件是否需要重新渲染,它在重新渲染开始前触发,默认返回为 true.

function shouldComponentUpdate(nextProps, nextState) {
    return true
}

当任意组件的shouldComponentUpdate函数返回true时,它允许触发diff渲染过程,这就给了我们控制重新渲染的能力。假设我们要阻止组件被重新渲染,我们只需在函数中返回false即可。正如我们看到的,我们可以比较当前和下一个props和state来决定是否重新渲染。

function shouldComponentUpdate(nextProps, nextState) {
    return nextProps.id !== this.props.id
}

使用纯组件pure component

你使用React,你一定知道React.Component是什么,但是React.PureComponent是个啥玩意儿?上面我们讨论了shouldComponentUpdate函数,在纯组件中,有一个默认的shouldComponentUpdate实现,进行浅层的prop和state比较。所以,纯组件就是只会在props和state与之前不一样时才重新渲染的组件。

function shouldComponentUpdate(nextProps, nextState) {
    return shallowCompare(this.props, nextProps) && shallowCompare(this.state, nextState)
}

在浅层比较中,基本数据类型像字符串,布尔,数字,通过值进行比较;复杂数据类型,比如数组、对象、函数通过引用比较(地址)

但是,如果我们有一个函数无状态组件在每次冲渲染之前要实现比较怎么办?React有一个高阶组件React.memo.它和React.PureComponent类似,只不过是用于函数组件。

const functionalComponent = (props) => {
    /* render using props */
}

export default React.memo(functionalComponent)

默认情况下,它做的事情和shouldComponentUpdate()一样,只是浅层的比较props对象,但是如果我们想要控制整个比较过程呢?可以自定义比较函数作为第二个参数。

const functionalComponent = (props) => {
    /* render using props */
}

const areEqual = (prevProps, nextPoprs) => {
    /*
        传入nextProps得到的渲染结果和prevProps一样时返回true,否则返回false
    */
}

export default React.memo(functionalComponent, areEqual)

使数据不可变

如果我们可以使用React.PureComponent,但仍然有一个有效的方式来判断任何复杂的props或state(如数组、对象等)何时自动改变。这就是不可变数据结构使生命周期更简单。

使用不可变数据结构的背后思想很简单。正如我们之前提到的,对于复杂的数据类型,比较在它们的引用上执行。当一个包含复杂数据的对象改变时,不是改变原有对象,而是复制原有对象的内容到新建的对象,对新建对象做出改变。

ES6的对象结构运算可以实现这一操作。

const profile = { name: 'Lebrown James', age: 35}

const updateProfile = (profile) => {
    return { ...profile, gender: 'male' }
}

也可以用数组

this.state = { languages: ['French', 'English' ]}

this.setState(state => ({
    words: [ ...state.languages, 'Spanish' ]
}))

对于同一个旧数据,避免传入新的引用

我们知道每当组件的props改变就会初发重新渲染。有时props没有改变,但我们用了一种React认为确实改变了props的方式编写代码,就会造成渲染浪费。所以,我们要确定传递不同的引用作为不同数据的props.同样,对于同样的数据也要避免传递新的引用。现在,我们来看一下这种问题的例子:

class BookInfo extends Component {
    render() {
        const book = {
            name: '1984',
            author: 'George Orwell',
            publishedAt: 'June 8, 1949',
        }

        return (
            <div>
                <BookDescription book={book} />
                <BookReview reviews={this.props.reviews} />
            </div>
        )
    }
}

我们在BookInfo组件中渲染了BookDescription和BookReview两个组件。代码是正确的,工作正常,但是有一个问题:每当我们获取一个新的reviews数据作为props时,BookDescription就会重新渲染。为什么会这样?BookInfo组件一接收到新的props,render函数就被调用来创建其元素树。render函数创建了一个新的book常量,也就是创建了一个新的引用。所以,BookDescription将会得到这个book常量作为引用,BookDescription也就会重新渲染。我们可以将这段代码分解为:

class BookInfo extends Component {
    
    const book = {
        name: '1984',
        author: 'George Orwell',
        publishedAt: 'June 8, 1949',
    }
    
    render() {
        return (
            <div>
                <BookDescription book={this.book} />
                <BookReview reviews={this.props.reviews} />
            </div>
        )
    }
}

现在,引用就总是一样的了-- this.book并且渲染时不会创建新的对象了。重新渲染的概念适用于每一个prop包括事件处理,比如:

class Foo extends Component {
    handleClickOne() {
        console.log('Click happened in One')
    }

    handleClickTwo() {
        console.log('Click happened in Two')
    }

    render() {
        return (
            <Fragment>
                <button onClick={this.handleClickOne.bind(this)}>
                    Click me One!
                </button>
                <button onClick={() => this.handleClickTwo()}>
                    Click me Two!
                </button>
            </Fragment>
        )
    }
}

这里,我们用了两种不同的方式(在render中使用bind方法和箭头函数)来调用事件处理方法但是每次在组件重新渲染时两种方法都会创建一个新的函数。所以,为了解决这个问题,我们可以在constructor中进行方法绑定

class Foo extends Component {
    constructor() {
        this.handleClickOne = this.handleClickOne.bind(this)
    }
    
    handleClickOne() {
        console.log('Click happened in One')
    }

    handleClickTwo() => {
        console.log('Click happened in Two')
    }

    render() {
        return (
            <Fragment>
                <button onClick={this.handleClickOne}>
                    Click me One!
                </button>
                <button onClick={this.handleClickTwo}>
                    Click me Two!
                </button>
            </Fragment>
        )
    }
}

总结

在内部,React使用了一些聪明的技术来最小化更新UI所需的代价高昂的DOM操作的数量。对于许多应用,使用React都能得到一个快速的用户界面,并且不用做太多的工作来优化性能。尽管如此,如果能遵循前面提到的技术来解决渲染的浪费,对于大型应用,也能在性能方面有非常流畅的体验。

おすすめ

転載: www.cnblogs.com/wydumn/p/12149216.html