虚拟DOM到实体DOM的更新
toy-react.js
const RENDER_TO_DOM = Symbol('render to dom')export class Component {constructor() {this.props = Object.create(null);this.children = [];this._root = null;this._range = null;}setAttribute(name, value) {this.props[name] = value}appendChild(component) {this.children.push(component)}get vdom() {return this.render().vdom;}[RENDER_TO_DOM](range) {this._range = rangethis.render()[RENDER_TO_DOM](range)}rerender() {// this._range.deleteContents()// this[RENDER_TO_DOM](this._range)// 先插入再删除let oldRange = this._range;let range = document.createRange();range.setStart(oldRange.startContainer, oldRange.startOffset)range.setEnd(oldRange.startContainer, oldRange.startOffset)this[RENDER_TO_DOM](range)oldRange.setStart(range.endContainer, range.endOffset)oldRange.deleteContents()}setState (newState) {// console.log(newState)// 没有state 或者 不是对象, 短路if (this.state == null || typeof this.state != "object") {this.setState = newStatethis.rerender()return;}// 深拷贝let merge = (oldState, newState) => {for (let p in newState) {if (oldState[p] == null || typeof oldState[p] != "object") {oldState[p] = newState[p]} else {merge(oldState[p], newState[p])}}}merge(this.state, newState)this.rerender()}// get root() {// if (!this._root) {// this._root = this.render().root// }// return this._root// }}class ElementWrapper extends Component {constructor(type) {super(type)this.type = type// this.root = document.createElement(type)}// setAttribute(name, value) {// // 过滤事件, 如onClick// if (name.match(/^on([\s\S]+)$/)) {// // console.log(RegExp.$1)// // 绑定事件, Click转click// this.root.addEventListener(RegExp.$1.replace(/^[\s\S]/, c => c.toLowerCase()), value)// } else if (name == 'className'){// this.root.setAttribute('class', value)// } else {// this.root.setAttribute(name, value)// }// this.root.setAttribute(name, value)// }// appendChild(component) {// // this.root.appendChild(component.root)// let range = document.createRange()// range.setStart(this.root, this.root.childNodes.length)// range.setEnd(this.root, this.root.childNodes.length)// component[RENDER_TO_DOM](range)// }get vdom() {this.children = this.children.map(child => child.vdom);return this}[RENDER_TO_DOM](range){// range.deleteContents()// range.insertNode(this.root)// 实现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)}}class TextWrapper extends Component{constructor(content) {super(content)this.type = '#text'this.content = contentthis.root = document.createTextNode(content)}get vdom() {return this;}[RENDER_TO_DOM](range){range.deleteContents()range.insertNode(this.root)}}export function createElement(tagName, attributes, ...rest) {let elementif (typeof tagName == 'string') {element = new ElementWrapper(tagName)} else {element = new tagName}if (typeof attributes === 'object' && attributes instanceof Object) {for (const key in attributes) {element.setAttribute(key, attributes[key])}}let insertChildren = (children) => {// console.log(children)for(const child of children) {if (typeof child == 'string') {child = new TextWrapper(child)}if (child === null) {continue;}if ((typeof child == 'object') && (child instanceof Array)) {insertChildren(child)} else {element.appendChild(child)}}}insertChildren(rest)return element}export function render(component, parentElement) {// parentElement.appendChild(component.root)let range = document.createRange()range.setStart(parentElement, 0)range.setEnd(parentElement, parentElement.childNodes.length)component[RENDER_TO_DOM](range)}
