简单实现Vue中的插值替换(二)
传送门:简单实现Vue中的插值替换(一)
在上一篇博客中,我们简简单单的梳理了一下,如何把一个插值表达式替换成对象里面真正的数据,但是呢,美中不足的地方也有很多,这次呢,我们从两个方面入手,简简单单的完善之前的代码。
方面一:仿造Vue的数据源
方面二:代码结构上仿造Vue
简单的聊一下方面一:
首先我们知道,我们在Vue中写数据源一般是这么写的:
let app = new Vue({
el: '#root',
data: {
name: 'Jim',
message: 'Hello!'
}
});
稍微一分析,我们的脑海里出现第一个念头,这个Vue
肯定是一个类,因为使用了关键字new
,所以代码应该是这样的:
class Vue {
constructor(option){
// 这里的 option 就包含了传进来的 el 和 data
console.log(option);
}
}
但是呢,嘿嘿,我们也可以不是 类
,我们还可以写成一个函数,嘻嘻,比如这样:
function Vue(options) {
// 这里的 option 就包含了传进来的 el 和 data
console.log(option);
}
接下来,我就以这第二种方法函数的为例来进行接下来的阐述。
简单的聊一下方面二:
了解Vue的都知道,Vue进行插值表达式替换的,我们简略的概括成三个步骤:
- 拿到或者创建DOM——render
- 编译DOM——complier
- 更新DOM——update
当然,我们在这里并不细扣,可能有些函数里面并不会干什么活,也不讨论什么虚拟DOM什么的【虚拟dom我下一篇博客会讲】
这里只是简单的模仿一下,由此,我们脑海里,就有了一个大概的关于简单实现Vue插值替换的代码结构:
// 数据源
let app = new Vue({
el: '#root',
data:{
name: 'Jim',
message: 'Hello!'
}
});
function Vue(option){
// 这里存储获取到的 el 和 data
// 然后调用一下render函数拿到dom,当然也可以在这个Vue函数里拿,都可以
this.render(); // 这里我们可以通过this调用,是因为我会把render函数、complier函数、update函数、挂载到Vue的原型上。
}
Vue.prototype.render = function(){} // dom
Vue.prototype.complier = function(){} // 编译
Vue.prototype.update = function(){} // 更新
所以剩下来的就是填充一下代码了。
完整代码展示:【注意看注释】
// 定义HTML模板
<body>
<div id="root">
<p>{{name}} — {{message}}</p>
</div>
</body>
<script>
// 定义一个Vue构造函数
function Vue(options) {
// 获取new时传进来的数据,【Vue源码中一般带 _ 下划线的都是私有的】
this._data = options.data;
this._el = options.el;
// 通过el获取到dom模板
this._templateDom = document.querySelector(this._el);
this._parent = this._templateDom.parentNode;// 同时也存储一下父节点,方便后续的替换
// 调用渲染函数
this.render();
}
Vue.prototype.render = function () {
this.complier(); // 调用一下编译函数
}
// 编译,将模板与数据结合,得到真正的dom元素
Vue.prototype.complier = function(){
let realHTMLDom = this._templateDom.cloneNode(true); // 拷贝DOM
// 替换dom中的插值表达式
complier(realHTMLDom, this._data);
// 将编译后的dom传给更新函数
this.update(realHTMLDom);
}
// 将我们在 上一篇博客中的 代码搞过来,可以直接用。【不会的可以通过上面的传送门去看一下】
let regx = /\{\{(.+?)\}\}/g; // 正则表达式匹配双花括号
// 定义编译函数,用来处理dom元素
function complier(template, data) {
// 获取子节点
let childNodes = template.childNodes;
// 遍历子节点
for (let i = 0; i < childNodes.length; i++) {
// 根据子节点类型,来确定此节点是文本节点,还是元素节点[3代表文本节点,1代表元素节点]
let type = childNodes[i].nodeType;
if (type === 3) {
// 获取文本节点的值
let txt = childNodes[i].nodeValue;
// 利用replace,结合正则表达式,来对插值进行替换
txt = txt.replace(regx, function (_, g) {
let key = g.trim();
let value = data[key];
return value;
});
childNodes[i].nodeValue = txt;
} else if (type === 1) {
// 如果是元素节点 递归调用
complier(childNodes[i], data);
}
}
}
// 将dom渲染到页面中【就是替换一下dom】
Vue.prototype.update = function(real){
this._parent.replaceChild(real, document.querySelector(this._el));
}
// 数据源
let app = new Vue({
el: '#root',
data: {
name: 'Jim',
message: 'Hello!'
}
})
</script>
这样是不是看起来就有了那么一点点Vue的意思了呢,嘻嘻。
下一篇,我们解决一个插值问题,多级对象的访问。
传送门:简单实现Vue中的插值替换(三)