Open zwhu opened 6 years ago
// /roki/index.js const roki = function () { let oldvnode return { h, render } function isString(s) { return typeof s === 'string' } function h(type, props = {}, children = []) { children = children.map((child) => { if (isString(child)) return createTextVNode(child, void 0) return child }) return createVNode(type, props, props.key, children, void 0) } function patch(oldvnode = {}, vnode) { let element if (vnode.type) { if (oldvnode && oldvnode.type === vnode.type) { element = oldvnode.dom updateProps(element, vnode.props, oldvnode.props) } else { element = createElement(vnode.type, vnode.props) } vnode.children && patchChildren(oldvnode.children, vnode.children, element) } else if (vnode.text) { if (oldvnode.text) { element = oldvnode.dom if (vnode.text !== oldvnode.text) element.nodeValue = vnode.text } else { element = createTextNode(vnode.text) } } vnode.dom = element return vnode } function patchChildren(oldChildren = [], children = [], parentElement) { children.forEach((child, index) => { let oldChild = null if (oldChildren) oldChild = oldChildren[index] if (oldChild) { let newNode = patch(oldChild, child) if (oldChild.type !== child.type) { replaceChild(parentElement, oldChild.dom, newNode.dom) } } else { let newNode = patch(undefined, child) appendChild(parentElement, newNode.dom) } }) oldChildren.slice(children.length).forEach((child) => removeChild(parentElement, child.dom)) } function render(vnode, container) { vnode = patch(oldvnode, vnode) if (!oldvnode) appendChild(container, vnode.dom) oldvnode = vnode } function createVNode(type, props, key, children, dom) { return { type, children, props, dom, key } } function createTextVNode(text, dom) { return { text, dom } } function removeChild(element, child) { element.removeChild(child) } function appendChild(element, child) { element.appendChild(child) } function replaceChild(element, oldChild, newChild) { element.replaceChild(newChild, oldChild) } function createElement(tag, props = {}) { const element = document.createElement(tag) updateProps(element, props) return element } function updateProps(element, props = {}, oldProps = {}) { Object.keys(oldProps) .filter((key) => !key.startsWith('on')) .forEach((key) => { element.removeAttribute(key) }) Object.keys(oldProps) .filter((key) => key.startsWith('on')) .forEach((key) => { const eventType = key.toLowerCase().substring(2); element.removeEventListener(eventType, oldProps[key]); }) Object.keys(props) .filter((key) => !key.startsWith('on')) .forEach((key) => { element.setAttribute(key, props[key]) }) Object.keys(props) .filter((key) => key.startsWith('on')) .forEach((key) => { const eventType = key.toLowerCase().substring(2); element.addEventListener(eventType, props[key]); }) } function createTextNode(text) { return document.createTextNode(text) } } window.Roki = roki()
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <button id="update">update</button> <div id="root"></div> <script src="./roki/index.js"></script> <script> const { render, h } = Roki let vnode = h('div', {}, [ h('span', { style: 'font-weight:bold;', onclick: function () { console.log('old span') } }, ['This is bold']), ' and this is just normal text', h('a', { href: '/foo' }, ['I\'ll take you places!']), h('br'), 'this will delete' ]) let newVnode = h('div', { onclick: function () { console.log('new div') } }, [ h('span', { style: 'font-weight: normal; font-style: italic;' }, ['This is now italic type']), ' and this is still just normal text', h('a', { href: '/bar' }, ['I\'ll take you places!!!']), h('br'), ]); render(vnode, root) update.addEventListener('click', () => { console.log(222) render(newVnode, root) }) </script> </body> </html>