2020年11月18日
数据驱动模型(4)
生成带缓存的AST(createRenderFn),每次数据改变时就会生成一个新的虚拟dom,然后通过diff算法与原虚拟dom进行对比修改(update),然后更新html,渲染(render)

根据前3篇文章,可初步实现vue简化版本的数据渲染。(数据未响应化,后续会更新)
<script> function JGVue(option){ this._data = option.data; this._el = document.querySelector(option.el); this._parent = this._el.parentNode; // 挂载 this.mount(); } JGVue.prototype.mount = function(){ this.render = this.createRenderFn(); this.mountComponent(); } JGVue.prototype.mountComponent = function () { // 执行 mountComponent() 函数 // 挂载组件(需使用发布订阅模式,渲染和计算的行为交给watcher完成) let mount = () => { this.update( this.render() ) } mount.call( this ); // 本质应该交给 watcher 来调用, 但是还没有讲到这里 } JGVue.prototype.createRenderFn = function(){ let ast = getVnode(this._el); return function render(){ // 带数据的 ast let _tmp = combine( ast, this._data ); return _tmp; } } JGVue.prototype.update = function (vnode) { // 简化, 直接生成 HTML DOM replaceChild 到页面中 // 父元素.replaceChild(新,旧) let newDom = parseDom (vnode); this._parent.replaceChild(newDom,document.querySelector('#root'));//会将页面的DOM全部替换,vue是使用diff算法进行判断增删改。因为每次全部替换即每次需重新去获取跟元素 } class VNode{ constructor(tag, attr, value, type){ this.tag = tag && tag.toLowerCase(); this.attr = attr; this.value = value; this.type = type; this.children = []; } getData(data){ this.children.push(data); } } //虚拟dom function getVnode(el){ var type = el.nodeType; var tag,value; var attr = {}; var _vNode = null; if(type == 1){ // 元素节点 tag = el.nodeName; var attrs = el.attributes; for(var i = 0;i<attrs.length;i++){ attr[ attrs[i].nodeName ] = attrs[i].nodeValue; } _vNode = new VNode(tag, attr, value, type); var childNode = el.childNodes; for(var i=0;i<childNode.length;i++){ _vNode.getData( getVnode(childNode[i]) ); } }else if(type == 3){ // 文本节点 value = el.nodeValue; _vNode = new VNode(tag, attr, value, type); } return _vNode; } //带数据的虚拟dom var rz = /\{\{(.+?)\}\}/g; function combine(vnode, data){ var tag = vnode.tag; var attr = vnode.attr; var value = vnode.value; var type = vnode.type; var children = vnode.children; var _vNode = null; if(type == 1){ // 元素节点 _vNode =new VNode(tag, attr, value, type); for(var i=0;i<children.length;i++){ _vNode.getData( combine(children[i], data) ) } }else if(type == 3){ // 文本节点 var res = value.trim().replace(rz, function(_,g){ return getValueByPath(data, g); }) _vNode =new VNode(tag, attr, res, type); } return _vNode; } function getValueByPath(obj, path){ var arr = path.split('.'); let res = obj; let prop; // arr.shift()取出数组的第一个值 while( prop = arr.shift() ){ res = res[ prop ]; } return res; } //通过虚拟dom创建真正的dom function parseDom (res){ let tag = res.tag; let type = res.type; let value = res.value; let attr = res.attr; let children = res.children; if(type == 1){ if(tag != undefined){ var ele = document.createElement(tag); } if(attr != undefined){ for(var i in attr){ ele.setAttribute(i,attr[i]); } } if(children.length != 0){ for(var i=0;i<children.length;i++){ val = ele.appendChild(parseDom(children[i])); } } }else if(type == 3){ var ele = document.createTextNode(value); } return ele; } let app = new JGVue({ el: '#root', data: { name: '肉肉', message: { info: '还是嘎嘎' } } }) </script>