实现虚拟Dom
vdom: type、props、children
ElementWrapper 、 TextWrapper 继承 Component (props、children来自父类)
class ElementWrapper extends Componentclass TextWrapper extends Component
Component添加get vdom方法 (由ren的决定, 递归调用)
get vdom() {return this.render().vdom;}
ElementWrapper存储type ,添加get vdom方法(type)
super(type)this.type = typeget vdom() {return {type: this.type,props: this.props,children: this.children.map(child => child.vdom);}// 可简写// this.children = this.children.map(child => child.vdom)// return this;}
TextWrapper存储content, 添加get vdom方法(type、content)
super(content)this.content = contentget vdom() {return {type: '#text',content: this.content};// 可简写, 构造函数中添加this.type='#text'// return this;}
虚拟DOM到实体DOM的更新
废弃this.root, setAttribute、appendChild在RENDER_TO_DOM中实现
[RENDER_TO_DOM](range){// 实现setAttributerange.deleteContents()let root = document.createElement(this.type)for (let name in this.props) {let value = this.props[name]if (name.match(/^on([\s\S]+)$/)) {// console.log(RegExp.$1)// 绑定事件, Click转clickroot.addEventListener(RegExp.$1.replace(/^[\s\S]/, c => c.toLowerCase()), value)} else {if (name == 'className') name = 'class'root.setAttribute(name, value)}}// 实现 appendChildfor (let child of this.children) {let childRange = document.createRange()childRange.setStart(root, root.childNodes.length)childRange.setEnd(root, root.childNodes.length)child[RENDER_TO_DOM](childRange)}range.insertNode(root)}
vBOM比对, 实现patch
update () {let isSame = (oldNode, newNode) => {if (oldNode.type != newNode.type) {return false}for (let name in newNode.props) {if (newNode.props[name] != oldNode.props[name]) {return false}}if (Object.keys(oldNode.props).length > Object.keys(newNode.props).length) {return false}if (newNode.type == '#text') {if (newNode.content != oldNode.content) {return false}}return true}let update = (oldNode, newNode) => {// 根结点type和props完全一致, #text需要对比contentif (!isSame(oldNode, newNode)) {newNode[RENDER_TO_DOM](oldNode._range)return}newNode._range = oldNode._rangelet newChildren = newNode.vchildrenlet oldChildren = oldNode.vchildrenif (!newChildren || !newChildren.length) {return}let tailRange = oldChildren[oldChildren.length - 1]._rangefor (let i = 0; i < newChildren.length; i++) {let newChild = newChildren[i]let oldChild = oldChildren[i]if (i < oldChildren.length) {update(oldChild, newChild)} else {let range = document.createRange()range.setStart(tailRange.endContainer, tailRange.endOffset)range.setEnd(tailRange.endContainer, tailRange.endOffset)newChild[RENDER_TO_DOM](range)tailRange = range}}}let vdom = this.vdomupdate(this._vdom, vdom)this._vdom = vdom}
ElementWrapper 、 TextWrapper render方法中处理, 先插入后删除。replaceContent单独封装
function replaceContent (range, node) {// 先插入后删除range.insertNode(node)range.setStartAfter(node)range.deleteContents()range.setStartBefore(node)range.setEndAfter(node)}
