在React中实现双向数据绑定的性能影响

问题描述 投票:21回答:1

React新手的常见问题是为什么双向数据绑定不是内置功能,而通常的响应包括对单向数据流的解释以及出于性能原因而不总是需要双向数据绑定的想法。这是我想要更详细了解的第二点。

我目前正在为apollo-link-state(来自Apollo的新客户端状态管理工具)的表单库工作。除了使用apollo-link-state而不是redux作为状态管理器之外,这个概念与redux-form非常相似。 (请注意,表单状态与域实体的状态分开存储,但实体可以选择用于填充表单的初始状态。)

当用户在表单上进行更改时,库会立即通过onChange处理程序更新商店。我正考虑允许个别字段选择退出该行为,以防程序员关注性能,但后来我开始怀疑这何时会成为真正的性能问题。无论如何,浏览器都会触发oninput事件,因此我能想到的唯一性能考虑因素是商店是否随用户类型而更新。当然,执行突变而不是仅仅调用setState()会产生额外的开销,但这实际上只相当于一些额外的函数调用。让我们假设我没有使用apollo而只是调用一个直接更新某个全局存储的函数 - 那么性能考虑是什么呢?

我的想法是,如果表单支持在用户键入一个字段时立即更新表单状态,那么对于所有字段也可以这样做。用户一次只能键入一个字段,我看不到使某些字段有时更快(可能忽略不计)的页面的好处,有时候会慢一些。此外,我的库允许消费者使用他们想要的任何输入组件,因此如果程序员只想要更少的状态更新,他们可以编写一个组件来消除React的onChange事件或使用浏览器自己的changeblur事件。

我在这里错过了什么吗?在用户提交表单之前,我的库的用户是否还想忽略特定字段的更改?或者更有用的选择是忽略对整个表单的更改(直到提交)?

这是我当前方法背后的基本概念的基本(大大简化)说明:

// defined in a globally-accessible module
const formState = {
    // This somehow causes any dependent form components to re-render
    // when state changes
    update(formName, updatedState) {
        ...
    }
}
export default formState

...
// UserForm.js:

export default class UserForm extends PureComponent {
    componentDidMount() {
        formState.userForm = {
            firstName: '',
            lastName: '',
        }
    }

    handleChange(e) {
        const { target } = e
        formState.update('userForm', { [target.name]: target.value })
    }

    //...

    render() {
        const { userForm } = formState
        return (
            <form onSubmit={this.handleSubmit}>
                <label for="name">Name</label>
                <input id="name" type="text" onChange={this.handleChange} value={userForm.name} />

                <label for="email">Email</label>
                <input id="email" type="email" onChange={this.handleChange} value={userForm.email} />
            </form>
        )
    }
}

最后,为了完整起见,我应该提一下,还有一些API设计考虑因素。如果我没有提供选择退出自动双向绑定的选项,那么单个输入组件可能会有一个稍微简单的设计。如果有人有兴趣,我可以发布详细信息。

javascript performance reactjs flux
1个回答
21
投票

2 way data binding implications

从问题的第一部分开始,有两个主要原因可以解决双向数据绑定:

  1. React应用程序中数据更改的单一事实来源,因此错误的机会更少,调试更容易
  2. 性能优势

在React中,我们可以通过lifting the state up将不同子组件之间的状态共享到一个共同的父组件。更新共享状态时,所有子组件都可以自行更新。 Here是与表格相关的文档中的一个很好的例子。

谈到性能优势,在其他一些环境中的双向数据绑定(比如AngularJS)通过观察者观看不同的元素来工作。对于少量元素,这听起来更容易(并且代码比React的单向数据流更少),但随着UI组件/元素的数量增加,观察者的数量也增加。在这种情况下,单个更改会导致许多观察者启动以保持同步。这使得性能有点迟缓。在React的情况下,由于数据流只有一种方式,因此更容易确定哪些组件需要更新。

Handling state updates

转到问题的第二部分,您的状态库会向表单组件提供数据,从而导致任何相关组件更新状态更改,这是很好的。这是我的想法:

我正考虑允许个别字段选择退出该行为,以防程序员关注性能,但后来我开始怀疑这何时会成为真正的性能问题。

商店更新本身会很快发生。 JavaScript运行速度非常快,这是DOM更新,经常会导致瓶颈。因此,除非在同一页面上有数百个依赖表单元素并且所有这些元素都在更新,否则您将会很好。

让我们假设我没有使用apollo而只是调用一个直接更新某个全局存储的函数 - 那么性能考虑是什么呢?

我不认为它会有显着差异。

我的想法是,如果表单支持在用户键入一个字段时立即更新表单状态,那么对于所有字段也可以这样做。用户一次只能键入一个字段,我看不到使某些字段有时更快(可能是可忽略不计)的页面的好处,有时候使用其他字段更慢。

同意这一点。

我的库允许消费者使用他们想要的任何输入组件,因此如果程序员只想要更少的状态更新,他们可以编写一个组件来消除React的onChange事件或使用浏览器自己的更改或模糊事件。

我认为大多数用例都可以用简单的input解决。同样,我没有看到这里更少的状态更新带来的性能优势。如果我在输入上运行API调用(并且想要在用户停止输入之前等待),则Debounce可能很有用。

在用户提交表单之前,我的库的用户是否还想忽略特定字段的更改?或者更有用的选择是忽略整个表单的更改(直到提交)?

我没有看到忽略特定字段的更改或等待提交之前的好处。另一方面,在使用表单时,我遇到的常见用例是数据验证。例如,

  • 在用户创建密码时向用户提供反馈
  • 检查电子邮件是否有效
  • 执行API调用以查看用户名是否有效等。

这些情况需要在用户键入时更新状态。

tl;dr

在用户输入时更新状态应该没问题。如果您仍然关注性能,我建议profile your components隔离瓶颈,如果有的话:)

© www.soinside.com 2019 - 2024. All rights reserved.