我已经在打字稿中使用React编写了一个简单的treeview来渲染DOM。
每个节点都必须遵循简单的界面。
interface INode { label: string; children: INode[]; }
然后通过以下方式描述该树:
var tree = { label: "root", children: [ { label: "node 1", children: [ { label: "node 1.0", children: [] }, { label: "node 1.1", children: [] } ] }, { label: "node 2", children: [] } ] };
很简单。组件现在也非常简单。对于节点,我有:
class Node extends React.Component<INode, void>{ render() { console.log(`Rendering ${this.props.label}`); var list = this.props.children.map((node, i) => <li key={i}><Node {...node}/></li>); return ( <div> <span>{this.props.label}</span> {this.props.children.length > 0 ? <ul>{list}</ul> : ""} </div> ); } }
对于树的根,我使用状态而不是道具。但是1秒钟后,我刷新了状态。但是状态本身不会改变:
class Root extends React.Component<void, INode>{ constructor() { super(); this.state = tree; } componentWillMount() { setTimeout(() => { this.setState((prevState: INode, props: void) => { console.log(`Updating state of ${this.state.label}`); return prevState; }); }, 1000); } render() { var list = this.state.children.map((node, i) => <li key={i}><Node {...node} /></li>); return ( <div> <span>{this.state.label}</span> {this.state.children.length > 0 ? <ul>{list}</ul> : ""} </div> ); } }
要运行此应用程序,只需执行以下几行代码:
ReactDOM.render( <Root/>, document.getElementById("app") );
呈现的html看起来应该正确。我现在的问题是当我查看浏览器控制台时
渲染节点1 渲染节点1.0 渲染节点1.1 渲染节点2 更新根的状态 渲染节点1 渲染节点1.0 渲染节点1.1 渲染节点2
渲染节点1
渲染节点1.0
渲染节点1.1
渲染节点2
更新根的状态
整棵树被重新放置(进入虚拟DOM),而状态的值和子代的道具没有改变。
假设渲染过程将非常复杂且耗时。是否有可能停止将儿童重新提交到虚拟DOM中?
首先,除非您在应用程序中看到需要,否则不要花时间在此上。根据虚拟DOM进行检查是一项非常便宜的操作。但是,如果您需要优化应用程序,则可以通过一些策略来实现。
有两种广泛的方法可以这样做:
您可以避免使用shouldComponentUpdate进行重新渲染。为了shouldComponentUpdate提高效率,您可以使用框架来确保状态中只有不可变的对象,或者您可以简单地养成不变异对象,而是创建新对象的习惯。请参阅React不变性助手。在将nextState和nextProps与当前状态和props比较时,这将使您可以进行浅层检查(可以使用shallowCompare轻松完成)。
shouldComponentUpdate
这意味着仅当新值与当前值不同时才 更新 状态或道具。这种方法并不经常提出,但我认为它值得一提。
状态: 只有做到setState({ value: newValue }),如果newValue !== this.state.value。这可以通过多种方法来实现,但是一种方法是实际在进行检查之前进行检查setState()。
setState({ value: newValue })
newValue !== this.state.value
setState()
道具: 如果非变化来自道具,则通常是因为父组件拥有一个值(处于其状态)作为道具传递给其 另一个 子级中的一个。然后,父组件需要重新渲染自己及其所有子组件。如果只希望更改依赖于值的组件,则可以让这些组件监听 全局应用程序状态 的一部分,并仅注入对该组件有用的内容。这样,该值将直接传递给对它感兴趣的那些组件,而不会重新渲染其他组件。
(在多个组件依赖同一数据的情况下,这消除了将状态存储在公共父组件中的需求。除了提高效率之外,这通常还使得创建依赖于现有组件某些部分的新组件更加容易。应用状态。)
您可以使用Flux,Redux或Baobab维护全局应用程序状态 ,仅举几个例子。
使用 全局应用程序状态的 另一个优势是,您可以实现检查(在应用程序状态处理程序中),以确保仅通过实际更改来更新应用程序状态(即,如果设置的值与以前相同,则不设置值)。
减少重新渲染成本的另一种策略是使组件更小。然后,每个数据更改仅适用于DOM的一小部分,因为每个组件具有较少的要呈现的元素。
我认为您应该 始终尝试这样做 ,因为除了性能方面,它还使您的代码更易于理解,可重用和可维护。
每种策略都可以单独使用或结合使用,但是应用第一组中的两种策略通常不会显示出仅应用其中一种的显着改进。
我建议从缩小组件的角度开始,因为如上所述,这样做还具有其他优点。