Vue中最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号){{}}的形式来进行数据的绑定,我们这里来进行一个简单的复刻,并且用字符串路径来访问对象成员,完成一个简单的封装。
<div id="root">
<div>
<div>
<div>
{{name}}{{message}}
</div>
</div>
</div>
<p>{{name}}</p>
<p>{{message}}</p>
<p>{{frit.fritOne}}</p>
<p>{{frit.fritTwo}}</p>
</div>
<script type="text/javascript">
let rkuohao = /\{\{(.+?)\}\}/g; //正则表达式
function compiler(template, data) {
let childNodes = template.childNodes; //取出子元素
console.log(template)
for (let i = 0; i < childNodes.length; i++) {
let type = childNodes[i].nodeType; //nodeType用来描述节点的类型,当值为1时表示元素,为3时表示文本节点
if (type === 3) {
//当为文本节点的时候可以判断里面是否有{{}}的插值
let txt = childNodes[i].nodeValue; //取出文本|nodeValue该属性只有文本节点有意义
//判断文本里面有没有{{}}
txt = txt.replace(rkuohao, function(_, g) { //使用正则表达式匹配一次,函数就会调用一次(函数第0个参数表示匹配到的内容,第n个参数表示正则表达式的第n组)
let path = g.trim(); //得到{{}}里面的东西
let value = getValueByPath(data, path);
//将{{***}}用取到的值进行替换
return value;
})
//注意现在txt和Dom元素是没有关系的
childNodes[i].nodeValue = txt; //现在将元素重新加回去
} else if (type === 1) {
//当为元素的时候,需要考虑里面是否有子元素,需要对子元素进行判断是否插值
compiler(childNodes[i], data);
}
}
}
function Vuew(options) {
//定义数据
this._data = options.data;
this._el = options.el;
//定义模板
this._templateDOM = document.querySelector(this._el);
this._parent = this._templateDOM.parentNode;
//开始渲染
this.render()
}
//将模板和数据结合得到HTML加入到页面
Vuew.prototype.render = function() {
this.compiler();
}
//将模板与数据结合得到DOM元素
Vuew.prototype.compiler = function(tmpNode) {
let realHTMLDOM = this._templateDOM.cloneNode(true); //用模板拷贝一个准DOM
compiler(realHTMLDOM, this._data);
this.update(realHTMLDOM);
}
//将DOM元素放到页面中
Vuew.prototype.update = function(real) {
this._parent.replaceChild(real, document.querySelector('#root'))
}
//用字符串路径来访问对象成员
function getValueByPath(obj, path) {
let paths = path.split('.') //分割获取到数组
let res = obj;
let prop;
while (prop = paths.shift()) {
res = res[prop];
}
return res;
}
let app = new Vuew({
el: '#root',
data: {
name: '一个name',
message: '一个message',
frit: {
fritOne: '一个fritOne',
fritTwo: '一个fritTwo'
}
}
})
</script>
最后我们完成的效果为这样
?
?