Open fi3ework opened 6 years ago
在看 React 的内联函数和性能 看到了一段很有意思的代码段,乍一看挺简单的代码,但是弄懂还是认真的想了一下,在这里分享一下思考的过程。
// 1. App 会传递一个 prop 给 From 表单 // 2. Form 将向下传递一个函数给 button // 这个函数与它从 App 得到的 prop 相接近 // 3. App 会在 mounting 之后 setState,并传递 // 一个**新**的 prop 给 Form // 4. Form 传递一个新的函数给 Button,这个函数与 // 新的 prop 相接近 // 5. Button 会忽略新的函数, 并无法 // 更新点击处理程序,从而提交陈旧的数据 class App extends React.Component { state = { val: "one" } componentDidMount() { this.setState({ val: "two" }) } render() { return <Form value={this.state.val} /> } } const Form = props => ( <Button onClick={() => { submit(props.value) }} /> ) class Button extends React.Component { shouldComponentUpdate() { // 让我们假装比较了除函数以外的一切东西 return false } handleClick = () => this.props.onClick() render() { return ( <div> <button onClick={this.props.onClick}>这个的数据是旧的</button> <button onClick={() => this.props.onClick()}>这个工作正常</button> <button onClick={this.handleClick}>这个也工作正常</button> </div> ) } }
在线把玩地址:这是一个运行该应用程序的沙箱
上面的三个 button。
第一个会打印 "one"。
第一个会打印 "two"。
奇怪,明明长得都差不多为什么会有区别呢?
首先,从上到下看,App 的 state 更新,导致 re-render。Form 是一个 stateless component,接受一个新的 prop 必然会 re-render。然后是关键的 Button,Button 将 shouldComponent 给直接 return false 了,这会导致 render 不会被再次调用,在 Button 第一次 render 后(事实上也只有一个 render,因为 shouldComponentUpdate 直接 return false 了),onClick 指向的是 prevProps 的 this.props.onClick。
在这里还需要将 JSX 还原一下方便理解,JSX 调用 React.createElemennt 生成的 VDOM 的简化版可以表达为
{ type: button, onClick: this.props.onClick, children: "这个的数据是旧的", ... }
此时,onClick 已经被赋值为了 prevProps.onClick 了,之后都不会再有任何改变。
prevProps.onClick 又是个什么样的函数呢?是这个样子的:
() => { submit(props.value) }
在第一次 render 传递给 Button 时,props.value 值为 "one",之后 Form re-render,会生成新的 onClick 函数传递给 Button,但是很遗憾,Button 内的 onClick 已经定死了,无法改变,所以总是会输出 "one"。
"one"
第二次和第三次是一个道理,这里只说第二次。
第二次相比第一次,区别就是不是直接去执行 props.onClick,而是每次都包一个新的箭头函数,在每一次执行的时候都会去获取一个新的 this.props.onClick,这就是一切的关键,虽然 shouldComponentUpdate 为 false,但是新的 props 还是已经来了,可以通过 this.props 引用。
这段代码对我们有什么启发吗?
文章中作者说可以写一个 PureComponentMinusHandlers 高阶组件,作用类似高阶组件,但是对类似 onClick 的 props 的更新函数不会触发 update(因为它们基本也不会变化),而只对数据类的 props 的变化进行 PureComponent 的 shallowCompare,这是一种 react 的优化方法。
通过之前的分析还可以玩出下面的花样: 主动拉取更新的子组件来进行性能优化:像上例中的第二种和第三种方法,将子组件的 shouldComponentUpdate 返回 false,然后在传入的 props 的 handler 外面包一层匿名函数,这样每次调用 handler 都会去访问最新的 this.props.handler 等“非计划更新的 props”(函数的 props),这些函数的 props 可以返回父组件的一些内部状态传递给子组件。如此一来,子组件就从单向状态流变成了子组件向父组件主动拉取。但这与 React 的单向数据理念相左,是属奇技淫巧。
写了这么久react发现有这样的行为,但大神,我对你后面的思考方面的知识不是很理解,可以看个具体的例子吗?
@fi3ework
之前写的比较乱,又整理了一下哈
前言
在看 React 的内联函数和性能 看到了一段很有意思的代码段,乍一看挺简单的代码,但是弄懂还是认真的想了一下,在这里分享一下思考的过程。
代码
在线把玩地址:这是一个运行该应用程序的沙箱
上面的三个 button。
第一个会打印 "one"。
第一个会打印 "two"。
第一个会打印 "two"。
奇怪,明明长得都差不多为什么会有区别呢?
解释
第一个
首先,从上到下看,App 的 state 更新,导致 re-render。Form 是一个 stateless component,接受一个新的 prop 必然会 re-render。然后是关键的 Button,Button 将 shouldComponent 给直接 return false 了,这会导致 render 不会被再次调用,在 Button 第一次 render 后(事实上也只有一个 render,因为 shouldComponentUpdate 直接 return false 了),onClick 指向的是 prevProps 的 this.props.onClick。
在这里还需要将 JSX 还原一下方便理解,JSX 调用 React.createElemennt 生成的 VDOM 的简化版可以表达为
此时,onClick 已经被赋值为了 prevProps.onClick 了,之后都不会再有任何改变。
prevProps.onClick 又是个什么样的函数呢?是这个样子的:
在第一次 render 传递给 Button 时,props.value 值为
"one"
,之后 Form re-render,会生成新的 onClick 函数传递给 Button,但是很遗憾,Button 内的 onClick 已经定死了,无法改变,所以总是会输出"one"
。第二次 & 第三次
第二次和第三次是一个道理,这里只说第二次。
第二次相比第一次,区别就是不是直接去执行 props.onClick,而是每次都包一个新的箭头函数,在每一次执行的时候都会去获取一个新的 this.props.onClick,这就是一切的关键,虽然 shouldComponentUpdate 为 false,但是新的 props 还是已经来了,可以通过 this.props 引用。
思考
这段代码对我们有什么启发吗?
文章中作者说可以写一个 PureComponentMinusHandlers 高阶组件,作用类似高阶组件,但是对类似 onClick 的 props 的更新函数不会触发 update(因为它们基本也不会变化),而只对数据类的 props 的变化进行 PureComponent 的 shallowCompare,这是一种 react 的优化方法。
通过之前的分析还可以玩出下面的花样: 主动拉取更新的子组件来进行性能优化:像上例中的第二种和第三种方法,将子组件的 shouldComponentUpdate 返回 false,然后在传入的 props 的 handler 外面包一层匿名函数,这样每次调用 handler 都会去访问最新的 this.props.handler 等“非计划更新的 props”(函数的 props),这些函数的 props 可以返回父组件的一些内部状态传递给子组件。如此一来,子组件就从单向状态流变成了子组件向父组件主动拉取。但这与 React 的单向数据理念相左,是属奇技淫巧。