刚刚项目已经能勉强跑起来了,但是本菜鸡还是看不懂代码里那么多东西,怎么办呢,只能从头开始了,从起初的 commit 看一下吧
![](https://img.haomeiwen.com/i25023219/6d036ca4530b9d46.png)
让我们将时间线切回到第三次提交
![](https://img.haomeiwen.com/i25023219/de0da12e04849a7e.png)
可以看到,作者进行了尝试,用短短 70 行代码就实现了 vue 的最初版雏形
那来看一下具体的代码
![](https://img.haomeiwen.com/i25023219/7c56d2ccab076f46.png)
可以看到,最初的尝试就是现在锁熟知的 vue 的样子
运行项目
![](https://img.haomeiwen.com/i25023219/0934aa6905e53cc2.png)
可以看到,三个 msg 被转化为下面 msg 中的变量的值,另外两个没有的变量则没有任何反应
var bindingMark = 'data-element-binding'
function Element (id, initData) {
var self = this,
el = self.el = document.getElementById(id)
bindings = {} // the internal copy
data = self.data = {} // the external interface
content = el.innerHTML.replace(/\{\{(.*)\}\}/g, markToken)
el.innerHTML = content
for (var variable in bindings) {
bind(variable)
}
if (initData) {
for (var variable in initData) {
data[variable] = initData[variable]
}
}
function markToken (match, variable) {
bindings[variable] = {}
return '<span ' + bindingMark + '="' + variable +'"></span>'
}
function bind (variable) {
console.log('binding', variable)
bindings[variable].els = el.querySelectorAll('[' + bindingMark + '="' + variable + '"]')
;[].forEach.call(bindings[variable].els, function (e) {
e.removeAttribute(bindingMark)
})
Object.defineProperty(data, variable, {
set: function (newVal) {
[].forEach.call(bindings[variable].els, function (e) {
bindings[variable].value = e.textContent = newVal
})
},
get: function () {
return bindings[variable].value
}
})
}
}
var app = new Element('test', {
msg: 'hello'
})
首先看调用形式
var app = new Element('test', {
msg: 'hello'
})
第一个参数是 id , 第二个数据是初始数据
var self = this,
el = self.el = document.getElementById(id)
bindings = {} // the internal copy
data = self.data = {} // the external interface
content = el.innerHTML.replace(/\{\{(.*)\}\}/g, markToken)
el.innerHTML = content
然后在函数内部初始化了一大堆变量
将函数本身指向 self , 去获取 id 为 test 的 dom 元素,创建一个空对象 bindings,定义 data 和 self.data,并且将这俩指向同一个地址,定义 content 等于 el.innerHTML.replace(/\{\{(.*)\}\}/g, markToken),然后,直接将 dom 元素的内容赋值为刚刚初始化完成的 content
这段代码带给我疑惑的地方就是 content 变量的初始化,第一个给定的正则是匹配 {{xxxxx}} 这种形式的
其中使用了
经查询,可以了解到其用法,进而得知 markToken,是一个函数
然后看一下这个函数
function markToken (match, variable) {
bindings[variable] = {}
return '<span ' + bindingMark + '="' + variable +'"></span>'
}
// 依赖的 外部变量是这个
var bindingMark = 'data-element-binding'
经过我的 log 信息可知,此函数的收到的参数是这样子的
![](https://img.haomeiwen.com/i25023219/08da6fbafe12984c.png)
那自然可以推出来,这个函数会把 bindings 变成下面的样子
![](https://img.haomeiwen.com/i25023219/0003c73df9ad9ebc.png)
然后函数的返回值是这样子的
![](https://img.haomeiwen.com/i25023219/900832fe106fc276.png)
用 span 标签将花括号的东西全部替换掉,可以看到,渲染出来的东西也是这样子的
![](https://img.haomeiwen.com/i25023219/908e6d7ae07a5650.png)
然后返回主线,看下一段代码
for (var variable in bindings) {
bind(variable)
}
遍历了 bindings ,上面的代码可知, bindings 是被赋值过的,那么 key 值就是刚刚的 msg what hey
function bind (variable) {
bindings[variable].els = el.querySelectorAll('[' + bindingMark + '="' + variable + '"]')
;[].forEach.call(bindings[variable].els, function (e) {
e.removeAttribute(bindingMark)
})
Object.defineProperty(data, variable, {
set: function (newVal) {
[].forEach.call(bindings[variable].els, function (e) {
bindings[variable].value = e.textContent = newVal
})
},
get: function () {
return bindings[variable].value
}
})
}
然后执行 bind 函数,先是使用 querySelectorAll 去获取一个类数组对象,也就是节点列表,将获取到的节点列表赋值到 bindings[variable] 的 els 属性上,赋值完成后将自定义的标签数据进行了移除,现在的 bindings 是这个样子的
![](https://img.haomeiwen.com/i25023219/dbe369674ac9a7e5.png)
这个 函数使用 [].forEach.call 这种方式而不是直接 forEach 的原因也是因为 els 本身是一个类数组,其本身是没有 forEach 方法的,并且在此我了解到,那个时候还没有箭头函数,所以都要使用 function 的写法
下面就执行了我们最最最熟悉的响应式的地方,这里就不再多说了,网上的文章已经将这东西将烂了
这里做的就是,在给最起初定义的 data 对象赋值 hey msg what 等属性的时候,去给 bindings[variable].value 赋值,同时将 dom 元素的文本内容赋值为 newVal , get 的时候则会返回其值
继续回到主线
if (initData) {
for (var variable in initData) {
data[variable] = initData[variable]
}
}
然后最后一段逻辑就是给 data 赋值,触发刚刚定义好的 data setValue 的行为
这时候打开浏览器就可以看到,三个 msg 的花括号是渲染出来了的,其他两个是空值
![](https://img.haomeiwen.com/i25023219/e89c52fa0fcdb143.png)
拓展一下,写一个按钮,去操作刚刚的 msg 字段,页面应该可以实时更新
![](https://img.haomeiwen.com/i25023219/32461ee309273484.png)
经测试逻辑通过
![](https://img.haomeiwen.com/i25023219/3686db74c6f65a0b.png)
本文完
本文使用 文章同步助手 同步
网友评论