内容简介:对于常用的框架,如果仅限于会用,我觉得还是远远不够,至少要理解它的思想,这样才不会掉入各种坑里面,这篇文章是基于react-lite源码来写的。在react里面,经过babel的解析后,jsx会变成createElement执行后的结果。
对于常用的框架,如果仅限于会用,我觉得还是远远不够,至少要理解它的思想,这样才不会掉入各种坑里面,这篇文章是基于react-lite源码来写的。
createElement和component
在react里面,经过babel的解析后,jsx会变成createElement执行后的结果。
const Test = (props) => <h1>hello, {props.name}</h1>; <Test name="world" /> 复制代码
<Test name="world" />
经过babel解析后会变为createElement(Test, {name: "world}),这里的Test就是上面的Test方法,name就是Test方法里面接受的props中的name。
实际上当我们从开始加载到渲染的时候做了下面几步:
// 1. babel解析jsx <Test name="world"> -> createElement(Test, {name: "world"}) // 2. 对函数组件和class组件进行处理 // 如果是类组件,不做处理,如果是函数组件,增加render方法 const props = {name: world}; const newTest = new Component(props); newTest.render = function() { return Test(props); } // 3. 执行render方法 newTest.render(); 复制代码
这样也很容易理解, const Test = <div>hello, world</div>
和const Test = () => <div>hello, world</div>
的区别了。
key
react中的diff会根据子组件的key来对比前后两次virtual dom(即使前后两次子组件顺序打乱),所以这里的key最好使用不会变化的值,比如id之类的,最好别用index,如果有两个子组件互换了位置,那么index改变就会导致diff失效。
cloneElement
原来对cloneElement的理解就是类似cloneElement(App, {})这种写法,现在看了实现之后才理解。原来第一个参数应该是一个reactElement,而不是一个reactComponent,应该是 <App />
,而不是App,这个也确实是我没有好好看文档。
shouldComponentUpdate
当shouldComponentUpdate返回false的时候,组件没有重新渲染,但是更新后的state和props已经挂载到了组件上面,这个时候如果打印state和props,会发现拿到的已经是更新后的了。
setState
react里面setState后不会立即更新,但在某些场景下也会立即更新,下面这几种情况打印的值你都能回答的上来吗?
class App extends React.Component { state = { count: 0; } test() { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为0 this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为0 } test2() { setTimeout(() => { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为1 this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为2 }) } test3() { Promise.resolve().then(() => { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为1 this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为2 }) } test4() { this.setState(prevState => { console.log(prevState.count); // 0 return { count: prevState.count + 1 }; }); this.setState(prevState => { console.log(prevState.count); // 1 return { count: prevState.count + 1 }; }); } async test4() { await 0; this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为1 this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 此时为2 } } 复制代码
在react中为了防止多次setState导致多次渲染带来不必要的性能开销,会将待更新的state放到队列中,等到合适的时机(生命周期钩子和事件)后进行batchUpdate,所以在setState后无法立即拿到更新后的state。所以很多人说setState是异步的,setState表现确实是异步,但是里面没有用异步代码实现。而且不是等主线程代码执行结束后才执行的,而是需要手动触发。 如果是给setState传入一个函数,这个函数是执行前一个setState后才被调用的,所以函数返回的参数可以拿到更新后的state。 但是如果将setState在异步方法中(setTimeout、Promise等等)调用,由于这些方法是异步的,会导致生命周期钩子或者事件方法先执行,执行完这些后会将更新队列的pending状态置为false,这个时候在执行setState后会导致组件立即更新。从这里也能说明setState本质并不是异步的,只是模拟了异步的表现。
ref
ref用到原生的标签上,可以直接在组件内部用this.refs.xxx的方法获取到真实DOM。 ref用到组件上,需要用ReactDOM.findDOMNode(this.refs.xxx)的方式来获取到这个组件对应的DOM节点,this.refs.xxx获取到的是虚拟DOM。
合成事件
react里面将可以冒泡的事件委托到了document上,通过向上遍历父节点模拟了冒泡的机制。 比如当触发onClick事件时,会先执行target元素的onClick事件回调函数,如果回调函数里面阻止了冒泡,就不会继续向上查找父元素。否则,就会继续向上查找父元素,并执行其onClick的回调函数。 当跳出循环的时候,就会开始进行组件的批量更新(如果没有收到新的props或者state队列为空就不会进行更新)。
参考:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【1】JavaScript 基础深入——数据类型深入理解与总结
- 深入理解java虚拟机(1) -- 理解HotSpot内存区域
- 深入理解 HTTPS
- 深入理解 HTTPS
- 深入理解 SecurityConfigurer
- 深入理解 HTTP 协议
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
图片转BASE64编码
在线图片转Base64编码工具
RGB CMYK 转换工具
RGB CMYK 互转工具