研究vue源码,造轮子,最终模仿vue做出一个自动替换节点内容的效果,主要目的是实现一个简单的模仿vue的数据驱动。
这部分我之前想过用dom的方式来实现,写过类似的demo,但是没有保存,现在好好写一写,内容尽可能全面,把握每一个细节。
Vue的数据驱动流程
- 获得模板(模板中有数据的占位)
- 利用Vue构造函数中所提供的数据来替换占位符,得到渲染后的模板
- 将渲染后的模板替换到页面中
节点渲染
vue构造函数前后用console.log来输出root节点,来检查root节点是否是原来的节点 | console.log(document.querySelector('#root')); var app = new Vue({ el: '#root', data: { name: '张三', age: 18 } }); console.log(document.querySelector('#root'));
|
当鼠标放到第二个输出的节点上的时候,网页节点会高亮,而开发者工具的效果是如果这个节点在界面上的话,鼠标放在代码上会高亮,此时第一个节点的时候并没有高亮,因此,发现输出的节点是不同的(不是同一个节点),这是因为Vue在渲染的时候,会把原来的节点替换掉,所以第二个输出的节点是不同的。
实现思路
获取到根节点以后,使用object的深拷贝来复制一份根节点,然后用compiler函数来替换掉模板中的数据,最后把替换后的节点替换掉原来的节点。
compiler函数是个利用递归实现的替换节点内容的函数,用for循环获取到节点内容以后,利用正则表达式匹配花括号内容,然后替换掉内容
compiler实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function compiler(template, data) { let childNodes = template.childNodes; for (let index = 0; index < childNodes.length; index++) {
if (childNodes[index].nodeType == 1) { compiler(childNodes[index], data); }else if (childNodes[index].nodeType == 3) { let txt = childNodes[index].nodeValue; txt = txt.replace(rPattern, function(match, key) { key = key.trim(); let value = data[key]; return value; }); childNodes[index].nodeValue = txt; } } }
|
我用data对象来表示vue中的data,然后使用compiler来编译模板,把数据填充进去,这样就实现和vue差不多的模板功能了。
下面是我的代码实现
js部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| console.log(document.querySelector('#root'));
let rPattern = /\{\{(.*?)\}\}/g; let tmpNode = document.querySelector('#root'); let generateNode = tmpNode.cloneNode(true); let data = { name: '张三', age: 18 };
function compiler(template, data) { let childNodes = template.childNodes; for (let index = 0; index < childNodes.length; index++) {
if (childNodes[index].nodeType == 1) { compiler(childNodes[index], data); }else if (childNodes[index].nodeType == 3) { let txt = childNodes[index].nodeValue; txt = txt.replace(rPattern, function(match, key) { key = key.trim(); let value = data[key]; return value; }); childNodes[index].nodeValue = txt; } } } compiler(generateNode, data); tmpNode.parentNode.replaceChild(generateNode, tmpNode); console.log(document.querySelector('#root'));
|
html部分
1 2 3 4 5 6
| <div id="root"> <div> <span>{{name}}</span> <span>{{age}}</span> </div> </div>
|
可以看到效果和vue是一模一样的。