VM视图数据模型的原理(个人拙见)
在学习Vue的过程中会好奇,为何视图可以随着视图数据的改变而自动改变?MVVM的VM是如何做到的?
在看了相关的文章和教程后发现其实是一系列非常有意思的代码协同操作,所以我把自己的了解记录下来
首先需要书写一个事件的订阅和发布函数
function EmitEvent () { //注册一个回调对象,储存事件处理函数,通过键值对的方式,一个事件名称可以储存多个事件处理函数 { '事件名': [ [Func], [Func] ] } this.callbacks = {} } // 注册事件处理函数 EmitEvent.prototype.on = function (eventName, eventHandler) { if (!this.callbacks[eventName]) { // 如果改事件名称没有注册事件处理函数,则注册一个新的事件处理函数数组 this.callbacks[eventName] = [] } // 将新注册的事件处理函数 添加到事件处理函数数组 this.callbacks[eventName].push(eventHandler) } //模拟事件的发布(即触发事件) EmitEvent.prototype.emit = function (eventName) { // 如果该事件名没有注册事件处理函数,则不能发布 if (!this.callbacks[eventName]) { return } //解析拿到参数数组,不包含第0项(事件名称) var args = [].slice.call(arguments, 1) //遍历事件处理函数,并执行 this.callbacks[eventName].forEach(function (handler) { //调用处理函数,通过apply展开数组 handler.apply(null, args) }) }
这是让我们来模拟一个Fue实例 (fake Vue简写)
//先创建一个事件订阅与发布的实例对象 var FueEvent = new EmitEvent() //接着创建一个具有对视图数据进行订阅和发布的函数 function Fue(options) { //获取根节点和数据data var el = options.el var data = options.data //绑定根节点 var rootEl = document.querySelector(el) //获取根节点的子节点 var childNodes = rootEl.childNodes subs(childNodes) //调用遍历订阅事件 //封装遍历子节点 function subs(childNodes) { for (var i = 0; i < 7; i++) { var node = childNodes[i] var nodeType = node.nodeType if (nodeType === 1) { //如果是标签节点,则递归遍历其子节点 subs(node.childNodes) } else if (nodeType === 3) { //文本节点 var content = /{{(.+)}}/.exec(node.textContent) //如果为空则退出 if (content) { //注册订阅事件 var dataKey = content[1].trim() FueEvent.on(dataKey, function () { node.textContent = data[dataKey] }) } } } } //通过对象高级属性设置,数据观察 for (var key in data) { Object.defineProperty(this, key , { get: function () { return data[key] }, set: function (val) { //当data的数据被修改时,获取修改值并设置 data[key] = val //设置后,发布事件(通知订阅了该事件的DOM) FueEvent.emit(key) } }) } }
测试调用
html:
<div id="app">
<h1>{{message}}</h1>
<h2>{{message}}</h2>
<div>
<p>{{abc}}</p>
</div>
</div>
javascript:
window.app = new Fue({
el: '#app',
data: {
message: 'aa',
abc: 'bb'
}
})
此时页面渲染的则是对应的数据,并且通过控制台修改数据时,Fue就会通过EmitEvent.emit('事件名称')
自动重新渲染了
ps: 对于Object.defineProperty()
方法,多见于底层架构设计,平常开发貌似很少(几乎没有)用到。
这些是结合一些网上的文章和源码的见解,如果有错还请指正…