深入Vue响应式原理

深入Vue.js响应式原理

一、创建一个Vue应用

new Vue({
  data() {
    return {
      name: ‘yjh‘,
    };
  },
  router,
  store,
  render: h => h(App),
}).$mount(‘#app‘);

二、实例化一个Vue应用到底发生了什么?

  1. this._init()
  2. callHook(vm, ‘beforeCreate‘)
  3. observe(vm._data)
vm._data = vm.$options.data()

proxy(vm, _data, key) 代理到vm上访问

function proxy(vm, _data, key)() {
  Object.defineProperty(target, key, {
    get() {
      return vm._data.key
    },
    set(val) {
      vm._data.key = val
    }
  })
}
  1. callHook(vm, ‘created‘)
  2. mountComponent(vm.$mount执行后执行mountComponent)
  3. callHook(vm, ‘beforeMount‘)
  4. new Watcher(vm, updateComponent)
const updateComponent = () => {
  // 创建虚拟dom
  const vnode = vm._render()

  // 创建虚拟dom的过程等同于如下代码行
  // const vnode = vm.$options.render.call(vm, vm.$createElement)

  // 更新$el
  vm._update(vnode)
}
  1. callHook(vm, ‘mount‘)

在以上发生的行为当中,第3步与第7步两者相辅相成;也是我们最需要关心的,弄清楚这两者,vue响应式原理就基本掌握了

三、如何追踪数据变化

我们都知道 数据发生变化视图也随之更新,那么首先我们得知道如何监听数据的变化

class Observer {
  constructor(value) {
    this.value = value
    this.walk(value)
  }

  walk(obj) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
}
function defineReactive(obj, key) {
  Object.defineProperty(obj, key, {
    get() {
      // 数据被访问
      return obj.key
    },
    set(val) {
      if (val === obj.key) {
        return
      }
      // 数据更新了
      obj.key = val
    }
  })
}

四、定义一个发布订阅的Dep类

当我们在创建虚拟dom的过程中,也就是执行vm.$createElement方法,可能会在多个地方使用到同一个数据字段(如:vm.name),即多个订阅者订阅了name的更新,因此在Vue中定义了一个发布订阅的Dep类

class Dep {
  constructor() {
    this.subs = []
  }

  addSub(sub) {
    this.subs.push(sub)
  }

  depend() {
    if (Dep.target) {
      this.addSub(Dep.target)
    }
  }

  notify() {
    this.subs.forEach(sub => sub.update())
  }

  removeSub(sub) {
    const i = this.subs.findIndex(sub)
    if (i > -1) {
      this.subs.splice(i, 1)
    }
  }
}

五、数据订阅者

订阅数据更新的到底是谁,我们先看看如下场景

<!-- 场景1 -->
<div>名字:{{ userInfo.name }},全名:{{ fullName }}</div>
export default {
  data() {
    return {
      userInfo: {
        name: ‘junhua‘,
      },
    }
  },
  mounted() {
    // 场景2
    this.$watch(‘name‘, (newVal, val) => {
      // ...
    })
  },
  // 场景2
  watch: {
    name(newVal, val) {
      // ...
    }
  },
  computed() {
    // 场景3
    fullName() {
      return `yang${this.userInfo.name}`
    }
  }
}

从上面示例代码看,订阅数据更新的场景有:

  1. 模版插值 :new Watcher(vm, updateComponent)数据发生变化,更新组件
  2. vm.$watch : 监听单个数据做一些逻辑操作
  3. computed使用场景:计算属性

因此数据订阅者包含一个参数expOrFn([Function|String]),数据更新后需要执行的callback,如下:

class Watcher {
  constructor(vm, expOrFn, cb) {
    this.vm = vm
      if (typeof expOrFn === ‘function‘) {
      this.getter = expOrFn
    } else {
      this.getter = parsePath(expOrFn)
    }
    this.cb = cb || () => {}
    this.value = this.get()
  }

  get() {
    Dep.target = this
    const value = this.getter.call(this.vm, this.vm)
    Dep.target = undefined
    return value
  }

  update() {
    const val = this.value
    const newVal = this.get()
    this.cb.call(this.vm, newVal, val)
  }
}

六、最终的观察者Observer

class Observer {
  constructor(value) {
    this.value = value
    this.walk(value)
  }

  walk(obj) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i],)
    }
  }
}

function defineReactive(obj, key) {
  const dep = new Dep()
  Object.defineProperty(obj, key, {
    get() {
      // 依赖收集,收集订阅者Watcher实例
      dep.depend()
      // 数据被访问
      return obj.key
    },
    set(val) {
      if (val === obj.key) {
        return
      }
      // 数据更新了
      obj.key = val
      // 通知订阅者Watcher实例更新
      dep.notify()
    }
  })
}

七、总结

我们再来回顾下实例化Vue应用的最重要的两点

observe(vm._data)
// vm.$mount()
const componentUpdateWatcher = new Watcher(vm, updateComponent)

updateComponent在更新渲染组件时,会访问1或多个数据模版插值,当访问数据时,将通过getter拦截器把componentUpdateWatcher作为订阅者添加到多个依赖中,每当其中一个数据有更新,将执行setter函数,对应的依赖将会通知订阅者componentUpdateWatcher执行update,即执行updateComponent;至此Vue数据响应式目的已达到,再来看官网的这张图片就很好理解了

github地址   文章来源:博客园-杨君华,转载请注明出处:杨君华

原文地址:https://www.cnblogs.com/yangjunhua/p/11374430.html

时间: 2024-10-11 17:00:27

深入Vue响应式原理的相关文章

深度解析 Vue 响应式原理

深度解析 Vue 响应式原理 该文章内容节选自团队的开源项目 InterviewMap.项目目前内容包含了 JS.网络.浏览器相关.性能优化.安全.框架.Git.数据结构.算法等内容,无论是基础还是进阶,亦或是源码解读,你都能在本图谱中得到满意的答案,希望这个面试图谱能够帮助到大家更好的准备面试. Vue 初始化 在 Vue 的初始化中,会先对 props 和 data 进行初始化 Vue.prototype._init = function(options?: Object) { // ...

vue响应式原理解析

# Vue响应式原理解析 首先定义了四个核心的js文件 - 1. observer.js 观察者函数,用来设置data的get和set函数,并且把watcher存放在dep中 - 2. watcher.js 监听者函数,用来设置dep.target开启依赖收集的条件,和触发视图的更新函数 - 3. compile.js 编译者函数,用来编译模版和实例化 watcher 函数 - 4. index.js 入口文件 注意dep函数就是一个壳子,用来存放watcher和触发watcher更新的 首先从

Vue响应式原理

前面的话 Vue最显著的特性之一便是不太引人注意的响应式系统(reactivity system).模型层(model)只是普通JS对象,修改它则更新视图(view).这会让状态管理变得非常简单且直观,不过理解它的工作原理以避免一些常见的问题也是很重要的本文将详细介绍Vue响应式系统的底层细节 追踪变化 把一个普通JS对象传给Vue实例的data选项,Vue将遍历此对象所有的属性,并使用Object.defineProperty把这些属性全部转为getter/setter.Object.defi

深入探讨vue响应式原理

现在是时候深入一下了!Vue 最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的 JavaScript 对象.而当你修改它们时,视图会进行更新.这使得状态管理非常简单直接,不过理解其工作原理同样重要,这样你可以避开一些常见的问题.在这个章节,我们将研究一下 Vue 响应式系统的底层的细节. 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 get

vue 响应式原理

Vue 采用声明式编程替代过去的类 Jquery 的命令式编程,并且能够侦测数据的变化,更新视图.这使得我们可以只关注数据本身,而不用手动处理数据到视图的渲染,避免了繁琐的 DOM 操作,提高了开发效率.不过理解其工作原理同样重要,这样可以回避一些常见的问题,下面我们来介绍一下 Vue 是如何侦测数据并响应视图的. Object.defineProperty Vue 数据响应核心就是使用了 Object.defineProperty 方法( IE9 + ) . var obj = {}; Obj

关于Vue响应式原理

Vue最独特的特性之一,是其非侵入性的响应式系统.数据模型仅仅是普通的javascript对象.而当你修改他们时,视图会进行更新.这使得状态管理非常简单直接,不过理解其工作原理同样重要. 当你把一个普通的javascript对象传入Vue实例作为data选项,Vue将遍历此对象所有属性,并使用Object.defineProperty 把这些属性全部转为getter/setter.Object.defineProperty 是es5中一个无法shim的特性,这也就是Vue不支持IE8以及更低版本

Vue响应式原理深入解析

Vue最明显的特性之一便是响应式系统,其数据模型即是普通的 JavaScript 对象.而当你读取或写入它们时,视图便会进行响应操作. 响应式data: <div id = "exp">{{ message }}</div> const vm = new Vue({ el: '#exp', data: { message: 'This is A' } }) vm.message = 'This is B' // 响应式 vm._message = 'This i

vue响应式原理整理

vue是数据响应性,这是很酷的一个地方.本文只为理清逻辑.详细请看官方文档 https://cn.vuejs.org/v2/guide/reactivity.html vue的data在处理数据时候,会遍历data内对象的所有属性,并使用Object.defineProperty将属性转为getter/setter. 这里的getter/setter对用户是不可见的,但是方便vue对数据进行内部跟踪,来维护数据. 用Object.defineProperty这是一个ES5无法支持特性,所有vue

浅析vue响应式原理

图很清晰 当我们把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter.这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化.每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher