所谓的抽象语法树,就是由html与 {{xxx}} 拼接上的模板字符串
缓存抽象语法树的原因?
大家都知道vue中的数据是双向绑定的,也就是说,数据是能够被改变的,每当数据改变,重新生成虚拟dom,将数据加入到虚拟dom,重新渲染页面,这个过程是非常消耗性能的
-
定义一个Vue的构造函数
function JGVue(options) { this._template = document.querySelector(options.el); this._data = options.data; this.mount(); // 将html模板与数据都挂载到页面中 } -
将html模板与数据都挂载到页面中的mount函数
JGVue.prototype.mount = function() { // 将html模板与数据都挂载到页面中 let data_vnode = this.creteRenderFn(); // 缓存抽象语法树 data_vnode(); } -
缓存抽象语法树的函数
JGVue.prototype.creteRenderFn = function() { let vnode = getVNode(this._template); // 获取到虚拟DOM return () => { let tmp = combine(vnode,this._data); // 将带 {{xxx}} 的虚拟dom转换为 带真实数据的虚拟dom console.log('tmp',tmp); } } -
构造虚拟dom
class VNode { // 构造虚拟dom constructor(tag,data,value,type) { this._tag = tag && tag.toLowerCase(); this._data = data; this._value = value; this._type = type; this._children = []; } appendChild(vnode) { this._children.push(vnode); } } -
将真实的dom转换为虚拟dom
function getVNode(node) { // 将真实的dom转换为虚拟dom let nodeType = node.nodeType; let _vnode = null; if(nodeType === 1) { // 元素节点 let _tag = node.nodeName; let attrs = node.attributes; let attrsObj = {}; for (let i = 0; i < attrs.length; i++) { attrsObj[attrs[i].nodeName] = attrs[i].nodeValue; } _vnode = new VNode(_tag,attrsObj,undefined,nodeType); // 含有子节点 let _childres = node.childNodes; for (let j = 0; j < _childres.length; j++) { let childNode = getVNode(_childres[j]); _vnode.appendChild(childNode); } } else if(nodeType === 3) { // 文本节点 _vnode = new VNode(undefined,undefined,node.nodeValue,nodeType); } return _vnode; } -
将带 {{}} 的虚拟dom转换为 带数据的虚拟dom
function combine(vnode,data) { // vnode 指虚拟dom模板 data指data:{name:'zs',age:30,gender:'女'} 将带 {{}} 的虚拟dom转换为 带数据的虚拟dom let _tag = vnode._tag; let _data = vnode._data; let _children = vnode._children; let _type = vnode._type; let _value = vnode._value; let _vnode = null; if(_type === 1) { // 元素节点 _vnode = new VNode(_tag,_data,_value,_type); // 子节点 for (let i = 0; i < _children.length; i++) { let childNode = combine(_children[i],data); _vnode.appendChild(childNode); } } else if(_type === 3) { // 文本节点 /** 需要匹配到 {{xxxx}} 再次匹配到 xxx, 然后根据 xxx 从data中知道对应的值,并将其渲染到页面中 */ let reg = /\{\{(.+?)\}\}/g; _value = _value.replace(reg,($1,$2) => { return getValueByPath($2.trim(),data); }) _vnode = new VNode(_tag,_data,_value,_type); } return _vnode; } -
处理多级路径的问题 {{ name.first.second }}
function getValueByPath(path,data) { // path 对应的是多级路径 data对应的是 Vue中的data let res = data; let arr_path = path.split('.'); let props; while(props = arr_path.shift()) { res = data[props]; } return res; } -
将VUE实例化
let app = new JGVue({ el:'#root', data:{ name: 'zs', age: 30, gender:'女', hobby:{ eat:{ hamburg:'汉堡包' } } } })