Vue.js源码分析——单步调试响应式

本文深入探讨Vue.js响应式系统的实现,从初始化实例到数据转换为响应式,包括依赖收集、对象属性的getter/setter改造,以及$mount过程中的模板渲染。通过源码分析,揭示Vue如何确保数据变化时视图的自动更新。
摘要由CSDN通过智能技术生成

同样是Vue.js专栏系列,本文算是自己的查缺补漏,如果有不正确的地方欢迎大家指正,如果觉得在文中有所收获也欢迎大家点上宝贵的👍 写文不易,请多鼓励
参考文章:图解Vue响应式原理

准备代码

这个文章很简单,主要是看下面的代码运行的过程

<script src="../../dist/vue.js"></script>
<body>
  <div id="app">
    {{ msg }}
  </div>
  <script>
    const vm = new Vue({
      el: '#app',
      data: {
        msg: 'Hello!',
      }
    });
  </script>
</body>

开始调试

所在目录调试函数要做的事
src/core/instance/index.jsVue发现他调用了this._init()方法(此时this上已经挂载了很多方法,关于这些挂载的方法,以后再出文)
src/core/instance/init.js_init()调用initState去初始化实例状态, 进入initState
src/core/instance/state.jsinitState()_data、_props、_methods, 调用 initData(vm), 进入initData
src/core/instance/state.jsinitData()把data中的成员注入到vue实例,并把她转换成响应式
initData()获取data的所有属性名,判断是否和props,methods重名,如果不重名把data中的成员注入到vue实例 _data中
initData()执行observe(data,true /* asRootData */) 开始响应式的处理,进入observer函数
src/core/observer/index.jsobserver()判断value是否为对象或者是VNode,不是对象直接返回
observe()如果value有__ob__(observer对象)属性并且value.ob 是Observer实例,返回实例
observe()否则 new Observer(value) ,并且返回, 下面我们进入Observer看下里面发生了什么
src/core/observer/index.jsObserver类在构造函数内部,看到了this.dep = new Dep() 进行依赖收集,进入Dep
src/core/observer.dep.jsDep类发现里面定义了一些方法,和subs数组,准备进行依赖收集,注意:此时还没收集呢,只是定义,我们回到 Observer类
src/core/observer/index.jsObserver类执行def函数,给value对象设置__ob__属性,值为当前实例,然后判断value是否为数组,如果是数组,执行对应的响应式处理,如果是对象,执行this.walk(value),进入walk函数
walk遍历每个对象属性,调用defineReactive(obj,key[i])设置为响应式数据,下面我们进入defineReactive
defineReactice创建依赖对象实例 、获取obj的属性描述符对象, 通过描述符对象提取用户设置的get、set,判断是否递归观察子对象(根据shallow去判断是否需要深度监听),并将子对象属性都转换成getter/setter, 返回子观察对象
defineReactice执行Object.defineProperty方法为对象设置get和set, 这个部分是响应式的关键,下面附上了这部分的代码及相应注释

附上核心实现源码

Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      // 如果用户设置了getter,则value等于getter调用的返回值, 否则直接赋予属性值
      const value = getter ? getter.call(obj) : val
      // 下面的部分来收集依赖
      // 当访问值得时候会进行依赖收集,依赖收集其实就是把依赖该属性的watcher对象添加到dep对象的subs数组中,将来数据变化的时候通知所有的watcher
      // 如果存在当前依赖目标,即 watcher对象,则建立依赖
      if (Dep.target) {
        // 进行依赖收集
        dep.depend()
        // 如果子观察目标存在,建立子对象的依赖关系
        if (childOb) {
          childOb.dep.depend()
          // 如果属性是数组,则特殊处理收集数组对象依赖
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      // 返回属性值
      return value
    },
    set: function reactiveSetter (newVal) {
      // 如果用户设置了getter,则value等于getter调用的返回值, 否则直接赋予属性值
      const value = getter ? getter.call(obj) : val
      // 如果新值等于旧值 或者 新值旧值都为NaN则不执行
      /* eslint-disable no-self-compare */
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      /* eslint-enable no-self-compare */
      if (process.env.NODE_ENV !== 'production' && customSetter) {
        customSetter()
      }
      // 如果没有 setter 直接返回
      // #7981: for accessor properties without setter
      if (getter && !setter) return
      if (setter) {
        // 如果用户设置了setter则调用setter
        setter.call(obj, newVal)
      } else {
        // setter/getter 都不存在,更新新值
        val = newVal
      }
      // 如果新值是对象,观察子对象并返回 子的 observer对象
      childOb = !shallow && observe(newVal)
      // 派发更新(发布更改通知)
      dep.notify()
    }
  })

当响应式数据处理完,在src/core/instance/init.js中继续向下执行,执行$mount

  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
    // 此函数里面主要是解析模板,如果用户不传递render函数,解析模板
  }

src/platforms/web/entry-runtime-with-compiler.js 下面重写了$mount并进行模板的渲染

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值