react实现原理
合成事件原理
事件合成的好处?
- 批量异步更新
在事件、生命周期开始的时候 updateQueue.isBatchingUpdate = true
setState
执行时只追加更新内容,但不进行更新
在事件、生命周期结束的时候 updateQueue.isBatchingUpdate = false
结束时将更新器全部进行批量更新
在React17
之前
,所有的事件都委托到document
上
在React17
之后
,所有的事件都委托到容器
上 这是因为之前如果页面中有多个React应用,事件绑定容易冲突;
<div id="container"></div>
<div id="container1"></div> // ReactDOM.render(<h1>, container1)
<div id="container2"></div> // ReactDOM.render(<h2>, container2)
- 对浏览器进行兼容性处理
把不同浏览器的API不一致的,把不同的事件对象做成一个标准化的事件对象,提供标准的API供用户使用
例如
function stopPropagation(event){
if(!event){ // IE下
window.event.cancelBubble = true;
}
if(event.stopPropagation){ // w3c标准
event.stopPropagation()
}
}
原理
event.js
import { updateQueue } from './Component'
export function addEvent (dom, eventType, eventHandler) {
let store;
if (dom._store) {
store = dom._store;
} else {
dom._store = store = {};
}
// store.onclick = handleClick;
store[eventType] = eventHandler;
// document.onclick = dispatchEvent;
if (!document[eventType]) {
document[eventType] = dispatchEvent
}
}
/**
* 不管点什么按钮,触发什么事件,最终都会执行dispatchEvent方法
* 在合成事件的处理函数中,状态的更新是批量的
* @param {*} event 原生的事件对象,不同的浏览器可能不一样
*/
function dispatchEvent (event) {
// type: 'click' type: 'click'
const { target, type } = event;
const eventType = `on${type}`;
// 先把批量更新的标识改为true
updateQueue.isBatchingUpdate = true;
let syntheticEvent = createSyntheticEvent(event);
let currentTarget = target;
// 模拟冒泡
while (currentTarget) {
// 获取事件源DOM对象上的store属性
const { _store } = currentTarget;
const eventHandler = _store && _store[eventType];
if (eventHandler) {
syntheticEvent.target = target;
syntheticEvent.currentTarget = currentTarget;
eventHandler.call(currentTarget, syntheticEvent);
}
currentTarget = currentTarget.parentNode;
}
updateQueue.isBatchingUpdate = false;
updateQueue.batchUpdate(); // 真正的更新
}
function createSyntheticEvent (nativeEvent) {
const syntheticEvent = { nativeEvent };
for (const key in nativeEvent) {
syntheticEvent[key] = nativeEvent[key];
}
// 此处会有一些兼容性处理
return syntheticEvent;
}
import { addEvent } from './event'
/**
* 把新的属性更新到真实DOM上
* @param {*} dom 真实DOM
* @param {*} oldProps 旧的属性对象
* @param {*} newProps 新的属性对象
*/
function updateProps (dom, oldProps, newProps) {
for (let key in newProps) {
if (key === 'children') {
continue; // 此处忽略子节点的处理
} else if (key === 'style') {
let styleObj = newProps[key];
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr];
}
} else if (key.startsWith('on')) {
addEvent(dom, key.toLocaleLowerCase(), newProps[key]); // => 合成事件
} else {
dom[key] = newProps[key]; // className
}
}
}
setState实现原理
setState
什么时候是同步的,什么时候是异步的?
- 在React能管辖的地方就是批量异步的。比如事件处理函数,比如生命周期函数
- 在React管不到的地方,就是同步的。比如
setTimeout
setInterval
原生的事件处理函数
实现原理
import { compareTwoVdom, findDOM } from './react-dom'
// 更新队列
export let updateQueue = {
isBatchingUpdate: false, // 默认不批量更新,同步的
updaters: [], // 更新器的数组
batchUpdate () { // 批量更新
for (const updater of updateQueue.updaters) {
updater.updateComponent()
}
updateQueue.updaters.length = 0;
updateQueue.isBatchingUpdate = false;
}
}
class Updater {
constructor(classInstance) {
this.classInstance = classInstance;
this.pendingStates = []; // 等待生效的数组
}
addState (partialState) {
this.pendingStates.push(partialState)
this.emitUpdate(); // 触发更新
}
emitUpdate () {
// 有可能是批量异步更新,也有可能是同步更新
if (updateQueue.isBatchingUpdate) { // 批量异步更新
updateQueue.updaters.push(this) // 不刷新视图了,把当前的updater放到更新队列中
} else { // 同步更新
this.updateComponent();
}
}
updateComponent () {
const { classInstance, pendingStates } = this;
if (pendingStates.length > 0) {
sholdUpdate(classInstance, this.getState());
}
}
getState () {
const { classInstance, pendingStates } = this;
let { state } = classInstance;
pendingStates.forEach((partialState) => {
if (typeof partialState === 'function') {
partialState = partialState(state);
}
state = { ...state, ...partialState }
})
pendingStates.length = 0;
return state;
}
}
function sholdUpdate (classInstance, nextState) {
classInstance.state = nextState;
classInstance.forceUpdate();
}
class Component {
static isReactComponent = true
constructor(props) {
this.props = props;
this.state = {};
this.updater = new Updater(this);
}
setState (partialState) {
this.updater.addState(partialState);
}
forceUpdate () {
let oldRenderVdom = this.oldRenderVdom;
// let oldDOM = oldRenderVdom.dom;
let oldDOM = findDOM(oldRenderVdom);
// 基于新的属性和状态,计算新的虚拟DOM
let newRenderVdom = this.render()
compareTwoVdom(oldDOM.parentNode, oldRenderVdom, newRenderVdom);
this.oldRenderVdom = newRenderVdom;
}
}
export default Component
例子
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { number: 0, age: 10 }
}
handleClick = (event) => {
// setState参数是新的状态对象,这个新状态对象会合并到老状态对象上。
// 老状态没有的属性会添加,老状态有的属性会被覆盖
/* this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 0
this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 0
Promise.resolve().then(() => {
this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 2
this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 3
})
*/
this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 0
this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 0
setTimeout(() => {
this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 2
this.setState({ number: this.state.number + 1 })
console.log(this.state.number); // => 3
}, 1000);
// this.setState((state) => ({ number: state.number + 1 }))
/*
// 如果直接修改state的话,this.state确实改变了,但视图不会更新
this.state.number += 1;
*/
// Cannot add property title, object is not extensible
// this.props.title = '新标题';
}
render () {
return (
<div>
<p>{this.props.title}</p>
<p>number:{this.state.number}</p>
<button onClick={this.handleClick}>+</button>
</div>
)
}
}