import React from 'react' import ReactDOM from 'react-dom' class Counter extends React.Component { constructor(props) { super(props) this.state = { number: 0 } } add() { this.setState({ number: this.state.number + 1 }) console.log(this.state) //0 this.setState({ number: this.state.number + 1 }) console.log(this.state) //0 setTimeout(() => { this.setState({ number: this.state.number + 1 }) console.log(this.state) //2 this.setState({ number: this.state.number + 1 }) console.log(this.state) //3 }) } render() { return ( <button onClick={this.add.bind(this)}>{this.state.number}</button> ) } } ReactDOM.render(<Counter />, document.getElementById('root')) 复制代码
代码执行效果如下:
这里我们以计数器为例:(代码基于原生)
class Component { constructor(props) { this.props = props } // 根据render返回的html字符串生成dom结构 createDomFromHtmlString() { let htmlString = this.render() let divDom = document.createElement('div') divDom.innerHTML = htmlString let domElement = divDom.children[0] // 给dom元素,这里就是指button的click事件绑定方法 domElement.addEventListener('click', this.add.bind(this)) return domElement } // 将dom结构挂载到指定container上 mounted(container) { container.appendChild(this.createDomFromHtmlString()) } } class Counter extends Component { constructor(props) { super(props) this.state = { number: 0 } } add = () => { console.log('add') } render() { return `<button>${this.state.number}</button>` } } new Counter().mounted(document.getElementById('root')) 复制代码
代码执行效果如下图
实现setState方法,将新状态复制到旧状态上
实现updateComponent方法,新元素替换老元素
针对dom的事件绑定方法的处理
class Component { constructor(props) { this.props = props } // 根据render返回的html字符串生成dom结构 createDomFromHtmlString() { let htmlString = this.render() let divDom = document.createElement('div') divDom.innerHTML = htmlString // 保存生成的元素到实例上,作为老元素 this.domElement = divDom.children[0] // 2. 将当前组件的实例复制给创建的真实dom的component上 this.domElement.component = this return this.domElement } setState(partialState) { //状态更新:更新的是部分状态,将新状态复制到旧状态上 Object.assign(this.state, partialState) //重新渲染 this.updateComponet() } updateComponet() { // 新元素替换老元素 let oldElement = this.domElement let newElement = this.createDomFromHtmlString() oldElement.parentElement.replaceChild(newElement, oldElement) } // 将dom结构挂载到指定container上 mounted(container) { container.appendChild(this.createDomFromHtmlString()) } } window.trigger = (event, method) => { // 3. 获取当前dom所在的实例,进而获取事件绑定的方法 let component = event.target.component component[method].call(component) } class Counter extends Component { constructor(props) { super(props) this.state = { number: 0 } } add = () => { this.setState({ number: this.state.number + 1 }) } render() { // 1. 因为是模板字符串,无法映射到实例上,所以在此使用事件委托机制,将其委托到全局dom(window)元素上 return `<button onclick="trigger(event,'add')">${this.state.number}</button>` } } new Counter().mounted(document.getElementById('root')) 复制代码
代码执行效果如下:
// 批量更新策略 const batchingStrategy = { isBatchingUpdate: false, // 是否需要批量更新 dirtyComponents: [], //脏组件:组件的状态和界面上显示不一致 batchUpdate() { this.dirtyComponents.forEach((component) => { component.updateComponet() }) } } // 更新器 class Updater { constructor(component) { this.component = component this.pendingStates = [] } // 将需要更新的状态保存起来 addState(partialState) { this.pendingStates.push(partialState) // 判断是否是批量更新,如果是将需要更新状态的组件添加到了dirtyComponnet中,否则直接更新组件 batchingStrategy.isBatchingUpdate ? batchingStrategy.dirtyComponents.push(this.component) : this.component.updateComponet() } } class Component { constructor(props) { this.props = props this.$updater = new Updater(this) } // 根据render返回的html字符串生成dom结构 createDomFromHtmlString() { let htmlString = this.render() let divDom = document.createElement('div') divDom.innerHTML = htmlString // 保存生成的元素到实例上,作为老元素 this.domElement = divDom.children[0] // 2. 将当前组件的实例复制给创建的真实dom的component上 this.domElement.component = this return this.domElement } setState(partialState) { //状态更新:更新的是部分状态,将新状态复制到旧状态上 //Object.assign(this.state, partialState) //重新渲染 //this.updateComponet() // 将需要更新的状态保存起来 this.$updater.addState(partialState) } updateComponet() { // 批量更新状态,实现更新合并 this.$updater.pendingStates.forEach((partialState) => { Object.assign(this.state, partialState) }) // 将保存的状态情况 this.$updater.pendingStates.length = 0 // 新元素替换老元素 let oldElement = this.domElement let newElement = this.createDomFromHtmlString() oldElement.parentElement.replaceChild(newElement, oldElement) } // 将dom结构挂载到指定container上 mounted(container) { container.appendChild(this.createDomFromHtmlString()) } } window.trigger = (event, method) => { // 方法执行之前,把批量更新置为true batchingStrategy.isBatchingUpdate = true // 3. 获取当前dom所在的实例,进而获取事件绑定的方法 let component = event.target.component component[method].call(component) // 方法执行之后,把批量更新置为false batchingStrategy.isBatchingUpdate = false batchingStrategy.batchUpdate() // 进行批量更新,所有的脏组件根据各自的状态进行重新渲染 } class Counter extends Component { constructor(props) { super(props) this.state = { number: 0 } } add = () => { this.setState({ number: this.state.number + 1 }) console.log(this.state) //0 this.setState({ number: this.state.number + 1 }) console.log(this.state) //0 setTimeout(() => { this.setState({ number: this.state.number + 1 }) console.log(this.state) //2 this.setState({ number: this.state.number + 1 }) console.log(this.state) //3 }) } render() { // 1. 因为是模板字符串,无法映射到实例上,所以在此使用事件委托机制,将其委托到全局dom(window)元素上 return `<button onclick="trigger(event,'add')">${this.state.number}</button>` } } new Counter().mounted(document.getElementById('root')) 复制代码
代码执行效果如下:
class Transaction { constructor(wrappers) { this.wrappers = wrappers } perform(method) { this.wrappers.forEach((wrap) => wrap.initialize()) method() this.wrappers.forEach((wrap) => wrap.close()) } } // 批量更新策略 const batchingStrategy = { isBatchingUpdate: false, // 是否需要批量更新 dirtyComponents: [], //脏组件:组件的状态和界面上显示不一致 batchUpdate() { this.dirtyComponents.forEach((component) => { component.updateComponet() }) } } // 更新器 class Updater { constructor(component) { this.component = component this.pendingStates = [] } // 将需要更新的状态保存起来 addState(partialState) { this.pendingStates.push(partialState) // 判断是否是批量更新,如果是将需要更新状态的组件添加到了dirtyComponnet中,否则直接更新组件 batchingStrategy.isBatchingUpdate ? batchingStrategy.dirtyComponents.push(this.component) : this.component.updateComponet() } } class Component { constructor(props) { this.props = props this.$updater = new Updater(this) } // 根据render返回的html字符串生成dom结构 createDomFromHtmlString() { let htmlString = this.render() let divDom = document.createElement('div') divDom.innerHTML = htmlString // 保存生成的元素到实例上,作为老元素 this.domElement = divDom.children[0] // 2. 将当前组件的实例复制给创建的真实dom的component上 this.domElement.component = this return this.domElement } setState(partialState) { //状态更新:更新的是部分状态,将新状态复制到旧状态上 //Object.assign(this.state, partialState) //重新渲染 //this.updateComponet() // 将需要更新的状态保存起来 this.$updater.addState(partialState) } updateComponet() { // 批量更新状态,实现更新合并 this.$updater.pendingStates.forEach((partialState) => { Object.assign(this.state, partialState) }) // 将保存的状态情况 this.$updater.pendingStates.length = 0 // 新元素替换老元素 let oldElement = this.domElement let newElement = this.createDomFromHtmlString() oldElement.parentElement.replaceChild(newElement, oldElement) } // 将dom结构挂载到指定container上 mounted(container) { container.appendChild(this.createDomFromHtmlString()) } } let wrappers = [ { initialize() { // 方法执行之前,把批量更新置为true batchingStrategy.isBatchingUpdate = true }, close() { batchingStrategy.isBatchingUpdate = false batchingStrategy.batchUpdate() // 进行批量更新,所有的脏组件根据各自的状态进行重新渲染 } } ] const transaction = new Transaction(wrappers) window.trigger = (event, method) => { // 方法执行之前,把批量更新置为true // batchingStrategy.isBatchingUpdate = true // 3. 获取当前dom所在的实例,进而获取事件绑定的方法 let component = event.target.component // component[method].call(component) transaction.perform(component[method].bind(component)) // 方法执行之后,把批量更新置为false //batchingStrategy.isBatchingUpdate = false //batchingStrategy.batchUpdate() // 进行批量更新,所有的脏组件根据各自的状态进行重新渲染 } class Counter extends Component { constructor(props) { super(props) this.state = { number: 0 } } add = () => { this.setState({ number: this.state.number + 1 }) console.log(this.state) //0 this.setState({ number: this.state.number + 1 }) console.log(this.state) //0 setTimeout(() => { this.setState({ number: this.state.number + 1 }) console.log(this.state) //2 this.setState({ number: this.state.number + 1 }) console.log(this.state) //3 }) } render() { // 1. 因为是模板字符串,无法映射到实例上,所以在此使用事件委托机制,将其委托到全局dom(window)元素上 return `<button onclick="trigger(event,'add')">${this.state.number}</button>` } } new Counter().mounted(document.getElementById('root')) 复制代码