npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

memo-render

v0.0.3

Published

A react-component for optimizing performance when it's parent re-renders.

Downloads

6

Readme

MemoRender

A react-component for optimizing performance when it's parent re-renders.

MemoRender是一个非常简单的 react 组件,它是为了某些追求性能场景下,阻止一些已经声明的组件在本身props未发生变化(指深度比较 deep diff 后)时频繁重复渲染。

安装

$ npm install memo-render --save

// yarn
$ yarn add memo-render

如何使用

MemoRender的使用非常简单,它默认情况下,你只需要将它嵌套包裹需要优化的组件节点即可。它会深度对比子节点对象的变化,以决定是否跳过react渲染。

-   <HeavyComponent />
+   <MemoRender>
+       <HeavyComponent />
+   </MemoRender>

disabled

是否启用渲染优化

deps

deps是可选的。类似useMemo useCallback等 hooks 的第二个参数,即需要进行对比的依赖项数组。如果deps={[]},则表示任何情况下都不进行渲染更新。

使用deps可以使对比更加高效。

<MemoRender deps={[this.state.name]}>
    <div className={this.state.name}>...</div>
</MemoRender>

children

需要优化的组件节点

原理

频繁的重复渲染是大多数事后导致应用下降的原因,而减少渲染这也正是 react 优化性能的最主要方向:Avoid Reconciliation 。大多数时候,我们应当尽可能的通过优化组件划分、组合逻辑、状态模型等,来避免非必要的组件被迫重复渲染。

但是受限于业务形态或者组件维护、业务逻辑限制等原因,有些组件无法从经常更新的上层组件中抽离。所以这时候就需要适用shouldComponentUpdate等优化手段了。shouldComponentUpdate仅适用于项目本身的组件,对于第三方组件无法适用该方法优化。

MemoRender就是用于这种优化场景,无需改造原有组件,直接将它放到需要优化的节点上层,即可达到一定的优化效果。这是因为组件传递的各种 props(包括 children),大多都是局部临时变量。对于 object 类型数据,局部临时生成,每次都是新的变量对象,这导致 react 内部的 diffing 比较不一致,持续进入下一层组件的ceconciliation阶段,即重复渲染。MemoRender正式通过深度比较children节点是否有变化来告诉 react 是否跳过本次渲染。

感谢react-fast-compareReactRender借助 react 本身的react.memo优化技巧,通过深度比较children节点,来告诉 react 是否跳过本次渲染。

请注意,这并非意味着children节点的组件不会运行,实际上children节点的组件依然会调用其render()方法,但是如果render()生成的react elements tree(Virtual DOM Tree)与上一次没有变化,则 react 会复用上一次的结果,不进入协调(Reconciliation)阶段。

对性能影响更大的是协调(Reconciliation)阶段的计算处理,包括更新真实 DOM。仅仅创建VDOM的开销相比是可以忽略不计的。

陷阱

首先,相信我,绝大多数情况下你都不需要MemoRender

MemoRender并不是适用于所有场景,首先第一原则与 shouldComponentUpdate / React.memo / React.PureComponent 的指导思想一致:

If the slowdown is noticeable?

即,应当仅在应用性能出现明显下降时,再考虑应用这些优化手段。过度优化,可能导致应用存在潜在的 bug(例如组件无法响应 props 或 state 变化的更新)、优化逆反(过度的深度比较可能比 react 本身的 diffing、reconciliation 更慢)等

另外如果children节点存在传递了局部内联函数(临时函数),MemoRender会无法起到优化作用,甚至起到反作用,导致应用反而更慢。

/**
 * Bad 错误示例
 * 下面两个示例套用MemoRender是无效的,甚至会降低性能
 * 因为 onChange 是一个始终变化的函数,而函数是无法深度比较的
 * 第二个例子虽然传递的options是一个对象,但是因为其包含临时函数属性onChange,因此也会导致优化失效
 */
function APP() {
    return (
        <div>
            <MemoRender>
                <HeavyComponent onChange={() => {}} />
            </MemoRender>

            <MemoRender>
                <HeavyComponent options={{ value: 'xx', onChange() {} }} />
            </MemoRender>
        </div>
    );
}

正确的做法是使用deps属性,或者创建不可变的 onChange 函数,例如放到组件实例(class 组件)或者适用 memoizeation 优化(function 组件、hooks):

/**
 * Good 优化后
 */
function APP() {
    const onChange = useCallback(() => {}, []);

    return (
        <div>
            <MemoRender>
                <HeavyComponent onChange={onChange} />
                <HeavyComponent options={{ value: 'xx', onChange }} />
            </MemoRender>

            <MemoRender deps={[]}>
                <HeavyComponent onChange={() => {}} />
            </MemoRender>
        </div>
    );
}