Vue 源码阅读学习
tips
:
学习课程地址:http://51hcit.cn/
学习资源分享:
链接:https://pan.baidu.com/s/10ikkZ4QGc3DQZDbyyU-gUg
提取码:41FE
数据驱动模型
Vue
与模板
vue的执行流程
1.获得模板,模板里面有“坑”
2.利用 vue
构造函数中所提供的数据来填坑,得到就可以在页面中显示“标签”
3.将标签替换页面中原有“坑”的标签
vue
利用提供的数据和页面中模板生成了一个新的Html标签(node元素),替换到了页面中放置模板的位置
<!-- 模板 -->
<div id="root">
<p>{{msg}}</p>
<!-- 浏览器 -->
<p>Hello</p>
</div>
<script>
//相当于 document.getElementById("root")
console.log(root);
let vm = new Vue({
el:'#root',
data:{
msg:'Hello'
}
});
console.log(root);
</script>
控制台输出:
第11行console.log(root)
输出元素无高亮
第18行console.log(root)
输出元素有高亮
本节目标:实现?
<!-- 模板 -->
<div id="root">
<p>{{msg}}</p>
</div>
<script>
//步骤拆解
//1.获取模板 获取元素
//2.获取数据 (data...)
//3.将数据与模板结合,得到的是HTML元素 (DON元素)
//4.放入页面中
</script>
步骤一:获取模板 获取元素
let tmpNode = document.querySelector("#root");
步骤二:获取数据 (data…)
let data = {
msg:'Hello';
}
步骤三:将数据与模板结合,得到的是HTML元素 (DOM元素)
也就是将数据放入模板 如何放入?
思路:一般使用递归
tips
: 大家一定要对原生的api操作要熟悉,不然很难看懂
//template 模板
function complier(template,data){
//取出子元素
let childNodes = template.childNodes;
//遍历子元素
for(let i = 0; i < childNodes.length; i++){
//判断template是什么数据类型
let type = childNodes[i].nodeType;
// type = 1 为元素节点
// type = 3 为文本节点...
if(type === 3){
//文本节点 判断里面是否有 {{}} 插值
let txt = childNodes[i].nodeValue;
//该属性只有文本节点 才有意义 获取节点值
//判断是否有{{}} 使用正则表达式
//todo 正则表达式匹配...
}
}
}
tips
:这一部分内容很重要,需要对正则表达式很熟悉!
let r = /\{\{(.+?)\}\}/g;
//todo 正则表达式匹配...
txt = txt.replace(r, function (_, g) {
//replace 使用正则匹配一次 函数就会调用一次
//函数的第 0 个 参数 表示匹配的内容
//函数的第 n 个 参数 表示正则中第n组
let key = g.trim();//{{}}里面的的内容
let value = data[key];
//将 {{xxxx}} 用这个值替换
return value;
});
注意: txt
现在与 DOM
元素是没有关系的
type
为 1 的情况
//template 模板
function complier(template,data){
//取出子元素
let childNodes = template.childNodes;
//遍历子元素
for(let i = 0; i < childNodes.length; i++){
//判断template是什么数据类型
let type = childNodes[i].nodeType;
// type = 1 为元素节点
// type = 3 为文本节点...
if(type === 3){
//文本节点 判断里面是否有 {{}} 插值
let txt = childNodes[i].nodeValue;
//该属性只有文本节点 才有意义 获取节点值
//判断是否有{{}} 使用正则表达式
//todo 正则表达式匹配...
txt = txt.replace(r, function (_, g) {
//replace 使用正则匹配一次 函数就会调用一次
//函数的第 0 个 参数 表示匹配的内容
//函数的第 n 个 参数 表示正则中第n组
let key = g.trim();//{{}}里面的的内容
let value = data[key];
//将 {{xxxx}} 用这个值替换
return value;
});
//注意:`txt` 现在与 `DOM` 元素是没有关系的
childNodes[i].nodeValue = txt;
} else if (type === 1){
//元素考虑它是否有子元素 判断是否要进行插值
compiler(childNodes[i], data);
}
}
}
注意:我们此时是没有生成新的template 是直接在页面就更新的数据,因为DOM是引用类型
console.log(tmpNode);
compiler(tmpNode, data);
console.log(tmpNode);
//这样处理模板就没有用了
//解决方式:将tmpNode拷贝一份
let generateNode = tmpNode.cloneNode(true);//DOM元素
console.log(tmpNode);
compiler(generateNode, data);
console.log(generateNode);
控制台输出:
步骤四:更新后的模板放入页面
root.parentNode.replaceChild(generateNode, root);
大功告成!
页面以及控制台输出:
存在的问题:
1.DOM
使用的是虚拟DOM
2.只考虑了单属性 {{name}}
而 Vue
中大量的使用层级
{{child.name.firstName}}
3.代码没有整合, 而 Vue
中使用的是构造函数
源码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root">
<p>{{msg}}</p>
</div>
<script>
let r = /\{\{(.+?)\}\}/g;
//步骤拆解
//1.拿到模板 获取元素
//2.拿到数据 (data ...)
//3.将数据与模板结合,得到的是HTML元素 (DON元素)
//4.放到页面中
let tmpNode = document.querySelector('#root');//获取元素 获取模板
let data = {
msg: 'message'
}
//将数据放入模板??
//思路:一般使用递归
function compiler(template, data) {
//判断template是什么数据类型
//现在的案例 template 是 DOM 元素
//在真正的 Vue 源码中 DOM --> 字符串模板 --> 抽象语法树 --> VNode --> 真正的DOM
let childNodes = template.childNodes;//取出子元素
for (let i = 0; i < childNodes.length; i++) {
let type = childNodes[i].nodeType;
// type 值为 1 元素节点, 3为文本节点
if (type === 3) {
//文本节点 可以判断里面是否有 {{}} 插值
let txt = childNodes[i].nodeValue; // 该属性只有文本节点 才有意义
//判断是否有双花括号
txt = txt.replace(r, function (_, g) {
//replace 使用正则匹配一次 函数就会调用一次
//函数的第 0 个 参数 表示匹配的内容
//函数的第 n 个 参数 表示正则中第n组
let key = g.trim();//{{}}里面的的内容
let value = data[key];
//将 {{xxxx}} 用这个值替换
return value;
});
//注意:txt现在和DOM元素是没有关系的
childNodes[i].nodeValue = txt;
} else if (type === 1) {
//元素 考虑它是否有子元素 是否需要将其子元素 判断是否要进行插值
compiler(childNodes[i], data);
}
}
}
let generateNode = tmpNode.cloneNode(true);//DOM元素
console.log(tmpNode);
compiler(generateNode, data);
console.log(generateNode);
//我们此时是没有生成新的template 所以这里看到的是直接在页面总就更新的数据,因为DOM是引用类型
//这样处理模板就没有了
//解决方式:将tmpNode拷贝一份
//第四步 放入页面中
root.parentNode.replaceChild(generateNode, root);
//有很大的问题
//1. DOM 使用的是虚拟DOM
//2. 只考虑了单属性 {{name}} 而 Vue 中大量的使用层级
// {{child.name.firstName}}
//3. 代码没有整合 Vue中使用的是构造函数
</script>
</body>
</html>