VM原理(个人见解)

本文探讨了Vue中VM视图数据模型的原理,解释了如何通过事件订阅和发布实现数据变化时视图的自动更新。通过创建一个简单的Fue实例模拟Vue的机制,展示了数据绑定的工作流程。尽管实际开发中这种方法不常见,但它揭示了MVVM框架底层架构的设计思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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()方法,多见于底层架构设计,平常开发貌似很少(几乎没有)用到。
这些是结合一些网上的文章和源码的见解,如果有错还请指正…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值