[React技术内幕] key带来了什么
首先欢迎大家关注我的掘金账号和Github博客,也算是对我的一点鼓励,毕竟写东西没法获得变现,能坚持下去也是靠的是自己的热情和大家的鼓励。 大家在使用React的过程中,当组件的子元素是一系列类型相同元素时,就必须添加一个属性 所以我们需要了解一下 import React from 'react' import ReactDOM from 'react-dom' function App() { return ( <ul> { [1,1,2,2].map((val)=><li key={val}>{val}</li>) } </ul> ) } ReactDOM.render(<App/>,document.getElementById('root')) 现在要提问了,上面的例子显示的是: 1,2还是1,2呢。事实上显示的只有1和2,所以我们不禁要问为什么? 一致性处理(Reconciliation) 我们知道每当组件的 启发式算法了解一下什么是启发式算法:
React启发式算法就是采用一系列前提和假设,使得比较前后元素树的时间复杂度由O(n3)降低为O(n),React启发式算法的前提条件主要包括两点:
元素类型的比较 函数 //old tree <div> <Counter /> </div> //new tree <span> <Counter /> </span> 上面表示前后两个render函数返回的元素树,由于 import React,{Component} from 'react' import ReactDOM from 'react-dom' class Counter extends Component { constructor(props){ super(props); } state = { value: 0 } componentWillMount(){ console.log('componentWillMount'); } componentDidMount(){ this.timer = setInterval(()=>{ this.setState({ value: this.state.value + 1 }) },1000) } componentWillUnmount(){ clearInterval(this.timer); console.log('componentWillUnmount'); } render(){ return( <div>{this.state.value}</div> ) } } function Demo(props) { return props.flag ? (<div><Counter/></div>) : (<span><Counter/></span>); } class App extends Component{ constructor(props){ super(props); } state = { flag: false } render(){ return( <div> <Demo flag = {this.state.flag}/> <button onClick={()=>{ this.setState({ flag: !this.state.flag }) }} > Click </button> </div> ) } } ReactDOM.render(<App/>,document.getElementById('root')) <div className="before" title="stuff" /> <div className="after" title="stuff" /> 那么React包保持底层DOM元素不变,仅更新改变的DOM元素属性,比如在上面的例子中,React仅会更新div标签的 如果前后的比较元素是组件类型,那么也会保持组件实例的不变,React会更新组件实例的属性来匹配新的元素,并在元素实例上调用 key属性 在上面的前后元素树比较过程中,如果某个元素的子元素是动态数组类型的,那么比较的过程可能就要有所区分,比如: //注意: //li元素是数组生成的,下面只是表示元素树,并不代表实际代码 //old tree <ul> <li>first</li> <li>second</li> </ul> //new tree <ul> <li>first</li> <li>second</li> <li>third</li> </ul> 当React同时迭代比较前后两棵元素树的子元素列表时,性能相对不会太差,因为前两个项都是相同的,新的元素树中有第三个项目,那么React会比较 //注意: //li元素是数组生成的,下面只是表示元素树,并不代表实际代码 //old tree <ul> <li>Duke</li> <li>Villanova</li> </ul> //new tree <ul> <li>Connecticut</li> <li>Duke</li> <li>Villanova</li> </ul> React在比较第一个 //注意: //li元素是数组生成的,下面只是表示元素树,并不代表实际代码 //old tree <ul> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> //new tree <ul> <li key="2014">Connecticut</li> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> 通过key值React比较 <ul> { [1,2].map((val)=><li>{val}</li>) } </ul> 我们会发现如果存在前后两个相同的 import React,{Component} from 'react' import ReactDOM from 'react-dom' function Demo(props) { return ( <div>{props.value}</div> ) } class App extends Component { constructor(props) { super(props); } render() { return ( <div> { [1,2].map((val,index) => { return ( <Demo key={val} value={val + '-' + index} /> ) }) } </div> ) } } ReactDOM.render(<App/>,document.getElementById('root')) 我们发现最后的显示效果是这样的:
到这里我们已经基本明白了 //case1 function App() { return ( <ul> { [ <li key={1}>1</li>,<li key={2}>2</li> ] } </ul> ) } //case2 function App() { return ( <ul> <li>1</li> <li>2</li> </ul> ) } 我们会发现,第一种场景是需要传入key值的,第二种就不需要传入key,为什么呢?其实我们可以看一下JSX编译之后的代码: //case1 function App() { return React.createElement('ul',null,[ React.createElement('li',{key: 1},"1"),React.createElement('li',{key: 2},"2") ]) } //case2 function App() { return React.createElement('ul',"2") ) } 我们发现第一个场景中,子元素的传入以数组的形式传入第三个参数,但是在第二个场景中,子元素是以参数的形式依次传入的。在第二种场景中,每个元素出现在固定的参数位置上,React就是通过这个位置作为天然的 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |