简单实现 MVVM 的订阅与数据劫持

MVVM 的设计模式非常多见,这里就不细说其出处了。

学生反馈一个面试题,说实现一个简单的 类似 vue 的 MVVM demo,自己尝试了一下,做个记录吧。

响应式原理

首先,先要理解 vue 的响应式原理,是数据劫持,即数据变化的时候,自动重新渲染相关页面。

其实这个需求是非常容易的,只要理解了 Object.defineProperty,大部分人都可以实现。

let data = {}, temp = ''

Object.defineProperty(data, key1, {
	set(value){
		console.log('this is a new value: ' + value)
		temp = value
		//some code like $('div').html(value) will automatic execute when key1 changed
	},
	get(){
		return temp
	}
})

data.key1 = 'Jack'

订阅模式

订阅器,我们可以简单的将其理解为一个队列,队列内都是即将在某个时刻执行的函数。

当然,为了方便查找,我们可以将其定义为一个对象类型,其中的每个属性,都是数组类型。

let Deep = {
  deepList: {},

  listen(key, fn){
    if(!this.deepList[key])
      this.deepList[key] = []
    
    this.deepList[key].push(fn)
  },

  trigger(){
    let key = Array.prototype.shift.call(arguments)

    let fnList = this.deepList[key]

    if(!key || !fnList || !fnList.length)
      return false
    
    for(let i=0, fn; fn = fnList[i++];) {
      fn.apply(this.arguments)
    }
  }
}

将订阅器与数据劫持绑定到一起

这里就是要实现,数据劫持发生后,去执行订阅器内相应的代码。

这样,就可以实现类似 vue 的,改变了某个 message,页面能同步渲染最新的结果。

let dataHijack = ({data, tag, datakey, selector}) => {
  let value = '', el = document.querySelector(selector);

  Object.defineProperty(data, datakey, {
    get(){
      return value
    },
    set(newVlaue){
      value = newVlaue
      Deep.trigger(tag, newVlaue)
    }
  })

  Deep.listen(tag, content=>{
    el.innerHTML = content
  })
}

这部分代码的逻辑十分简单

首先,通过Deep.listen 将页面标签与内容绑定到一起并放入到订阅器中
然后,在数据劫持中调用订阅器来实现更新html的功能

猜你喜欢

转载自blog.csdn.net/zhai_865327/article/details/106682492