Vue源码研究之数据驱动(一)

本文最后更新于:1 年前

研究vue源码,造轮子,最终模仿vue做出一个自动替换节点内容的效果,主要目的是实现一个简单的模仿vue的数据驱动。
这部分我之前想过用dom的方式来实现,写过类似的demo,但是没有保存,现在好好写一写,内容尽可能全面,把握每一个细节。

Vue的数据驱动流程

  1. 获得模板(模板中有数据的占位)
  2. 利用Vue构造函数中所提供的数据来替换占位符,得到渲染后的模板
  3. 将渲染后的模板替换到页面中

    节点渲染

    vue构造函数前后用console.log来输出root节点,来检查root节点是否是原来的节点
    1
    2
    3
    4
    5
    6
    7
    8
    9
    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++) {
/**
* nodeType 1:元素节点 3:文本节点
*/
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'));
/**
* 步骤拆解
* 1.拿到模板
* 2.拿到数据
* 3.结合数据,得到dom元素
* 4.替换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++) {
/**
* nodeType 1:元素节点 3:文本节点
*/
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是一模一样的。