资讯专栏INFORMATION COLUMN

Vue的数据依赖实现原理简析

quietin / 1692人阅读

摘要:在实例化的过程中,提供的构造函数就使用我们传入的去完成数据的依赖管理,初始化的过程只有一次,但是在你自己的程序当中,数据的依赖管理的次数不止一次。当响应式属性调用函数时,通过方法去遍历所有的依赖,调用去完成数据的动态响应。

首先让我们从最简单的一个实例Vue入手:

</>复制代码

  1. const app = new Vue({
  2. // options 传入一个选项obj.这个obj即对于这个vue实例的初始化
  3. })

通过查阅文档,我们可以知道这个options可以接受:

选项/数据

data

props

propsData(方便测试使用)

computed

methods

watch

选项 / DOM

选项 / 生命周期钩子

选项 / 资源

选项 / 杂项

具体未展开的内容请自行查阅相关文档,接下来让我们来看看传入的选项/数据是如何管理数据之间的相互依赖的。

</>复制代码

  1. const app = new Vue({
  2. el: "#app",
  3. props: {
  4. a: {
  5. type: Object,
  6. default () {
  7. return {
  8. key1: "a",
  9. key2: {
  10. a: "b"
  11. }
  12. }
  13. }
  14. }
  15. },
  16. data: {
  17. msg1: "Hello world!",
  18. arr: {
  19. arr1: 1
  20. }
  21. },
  22. watch: {
  23. a (newVal, oldVal) {
  24. console.log(newVal, oldVal)
  25. }
  26. },
  27. methods: {
  28. go () {
  29. console.log("This is simple demo")
  30. }
  31. }
  32. })

我们使用Vue这个构造函数去实例化了一个vue实例app。传入了props, data, watch, methods等属性。在实例化的过程中,Vue提供的构造函数就使用我们传入的options去完成数据的依赖管理,初始化的过程只有一次,但是在你自己的程序当中,数据的依赖管理的次数不止一次。

Vue的构造函数到底是怎么实现的呢?Vue

</>复制代码

  1. // 构造函数
  2. function Vue (options) {
  3. if (process.env.NODE_ENV !== "production" &&
  4. !(this instanceof Vue)) {
  5. warn("Vue is a constructor and should be called with the `new` keyword")
  6. }
  7. this._init(options)
  8. }
  9. // 对Vue这个class进行mixin,即在原型上添加方法
  10. // Vue.prototype.* = function () {}
  11. initMixin(Vue)
  12. stateMixin(Vue)
  13. eventsMixin(Vue)
  14. lifecycleMixin(Vue)
  15. renderMixin(Vue)

当我们调用new Vue的时候,事实上就调用的Vue原型上的_init方法.

</>复制代码

  1. // 原型上提供_init方法,新建一个vue实例并传入options参数
  2. Vue.prototype._init = function (options?: Object) {
  3. const vm: Component = this
  4. // a uid
  5. vm._uid = uid++
  6. let startTag, endTag
  7. // a flag to avoid this being observed
  8. vm._isVue = true
  9. // merge options
  10. if (options && options._isComponent) {
  11. // optimize internal component instantiation
  12. // since dynamic options merging is pretty slow, and none of the
  13. // internal component options needs special treatment.
  14. initInternalComponent(vm, options)
  15. } else {
  16. // 将传入的这些options选项挂载到vm.$options属性上
  17. vm.$options = mergeOptions(
  18. // components/filter/directive
  19. resolveConstructorOptions(vm.constructor),
  20. // this._init()传入的options
  21. options || {},
  22. vm
  23. )
  24. }
  25. /* istanbul ignore else */
  26. if (process.env.NODE_ENV !== "production") {
  27. initProxy(vm)
  28. } else {
  29. vm._renderProxy = vm
  30. }
  31. // expose real self
  32. vm._self = vm // 自身的实例
  33. // 接下来所有的操作都是在这个实例上添加方法
  34. initLifecycle(vm) // lifecycle初始化
  35. initEvents(vm) // events初始化 vm._events, 主要是提供vm实例上的$on/$emit/$off/$off等方法
  36. initRender(vm) // 初始化渲染函数,在vm上绑定$createElement方法
  37. callHook(vm, "beforeCreate") // 钩子函数的执行, beforeCreate
  38. initInjections(vm) // resolve injections before data/props
  39. initState(vm) // Observe data添加对data的监听, 将data转化为getters/setters
  40. initProvide(vm) // resolve provide after data/props
  41. callHook(vm, "created") // 钩子函数的执行, created
  42. // vm挂载的根元素
  43. if (vm.$options.el) {
  44. vm.$mount(vm.$options.el)
  45. }
  46. }

其中在this._init()方法中调用initState(vm),完成对vm这个实例的数据的监听,也是本文所要展开说的具体内容。

</>复制代码

  1. export function initState (vm: Component) {
  2. // 首先在vm上初始化一个_watchers数组,缓存这个vm上的所有watcher
  3. vm._watchers = []
  4. // 获取options,包括在new Vue传入的,同时还包括了Vue所继承的options
  5. const opts = vm.$options
  6. // 初始化props属性
  7. if (opts.props) initProps(vm, opts.props)
  8. // 初始化methods属性
  9. if (opts.methods) initMethods(vm, opts.methods)
  10. // 初始化data属性
  11. if (opts.data) {
  12. initData(vm)
  13. } else {
  14. observe(vm._data = {}, true /* asRootData */)
  15. }
  16. // 初始化computed属性
  17. if (opts.computed) initComputed(vm, opts.computed)
  18. // 初始化watch属性
  19. if (opts.watch) initWatch(vm, opts.watch)
  20. }
initProps

我们在实例化app的时候,在构造函数里面传入的options中有props属性:

</>复制代码

  1. props: {
  2. a: {
  3. type: Object,
  4. default () {
  5. return {
  6. key1: "a",
  7. key2: {
  8. a: "b"
  9. }
  10. }
  11. }
  12. }
  13. }

</>复制代码

  1. function initProps (vm: Component, propsOptions: Object) {
  2. // propsData主要是为了方便测试使用
  3. const propsData = vm.$options.propsData || {}
  4. // 新建vm._props对象,可以通过app实例去访问
  5. const props = vm._props = {}
  6. // cache prop keys so that future props updates can iterate using Array
  7. // instead of dynamic object key enumeration.
  8. // 缓存的prop key
  9. const keys = vm.$options._propKeys = []
  10. const isRoot = !vm.$parent
  11. // root instance props should be converted
  12. observerState.shouldConvert = isRoot
  13. for (const key in propsOptions) {
  14. // this._init传入的options中的props属性
  15. keys.push(key)
  16. // 注意这个validateProp方法,不仅完成了prop属性类型验证的,同时将prop的值都转化为了getter/setter,并返回一个observer
  17. const value = validateProp(key, propsOptions, propsData, vm)
  18. // 将这个key对应的值转化为getter/setter
  19. defineReactive(props, key, value)
  20. // static props are already proxied on the component"s prototype
  21. // during Vue.extend(). We only need to proxy props defined at
  22. // instantiation here.
  23. // 如果在vm这个实例上没有key属性,那么就通过proxy转化为proxyGetter/proxySetter, 并挂载到vm实例上,可以通过app._props[key]这种形式去访问
  24. if (!(key in vm)) {
  25. proxy(vm, `_props`, key)
  26. }
  27. }
  28. observerState.shouldConvert = true
  29. }

接下来看下validateProp(key, propsOptions, propsData, vm)方法内部到底发生了什么。

</>复制代码

  1. export function validateProp (
  2. key: string,
  3. propOptions: Object, // $options.props属性
  4. propsData: Object, // $options.propsData属性
  5. vm?: Component
  6. ): any {
  7. const prop = propOptions[key]
  8. // 如果在propsData测试props上没有缓存的key
  9. const absent = !hasOwn(propsData, key)
  10. let value = propsData[key]
  11. // 处理boolean类型的数据
  12. // handle boolean props
  13. if (isType(Boolean, prop.type)) {
  14. if (absent && !hasOwn(prop, "default")) {
  15. value = false
  16. } else if (!isType(String, prop.type) && (value === "" || value === hyphenate(key))) {
  17. value = true
  18. }
  19. }
  20. // check default value
  21. if (value === undefined) {
  22. // default属性值,是基本类型还是function
  23. // getPropsDefaultValue见下面第一段代码
  24. value = getPropDefaultValue(vm, prop, key)
  25. // since the default value is a fresh copy,
  26. // make sure to observe it.
  27. const prevShouldConvert = observerState.shouldConvert
  28. observerState.shouldConvert = true
  29. // 将value的所有属性转化为getter/setter形式
  30. // 并添加value的依赖
  31. // observe方法的分析见下面第二段代码
  32. observe(value)
  33. observerState.shouldConvert = prevShouldConvert
  34. }
  35. if (process.env.NODE_ENV !== "production") {
  36. assertProp(prop, key, value, vm, absent)
  37. }
  38. return value
  39. }

</>复制代码

  1. // 获取prop的默认值
  2. function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any {
  3. // no default, return undefined
  4. // 如果没有default属性的话,那么就返回undefined
  5. if (!hasOwn(prop, "default")) {
  6. return undefined
  7. }
  8. const def = prop.default
  9. // the raw prop value was also undefined from previous render,
  10. // return previous default value to avoid unnecessary watcher trigger
  11. if (vm && vm.$options.propsData &&
  12. vm.$options.propsData[key] === undefined &&
  13. vm._props[key] !== undefined) {
  14. return vm._props[key]
  15. }
  16. // call factory function for non-Function types
  17. // a value is Function if its prototype is function even across different execution context
  18. // 如果是function 则调用def.call(vm)
  19. // 否则就返回default属性对应的值
  20. return typeof def === "function" && getType(prop.type) !== "Function"
  21. ? def.call(vm)
  22. : def
  23. }

Vue提供了一个observe方法,在其内部实例化了一个Observer类,并返回Observer的实例。每一个Observer实例对应记录了props中这个的default value的所有依赖(仅限object类型),这个Observer实际上就是一个观察者,它维护了一个数组this.subs = []用以收集相关的subs(订阅者)(即这个观察者的依赖)。通过将default value转化为getter/setter形式,同时添加一个自定义__ob__属性,这个属性就对应Observer实例。

说起来有点绕,还是让我们看看我们给的demo里传入的options配置:

</>复制代码

  1. props: {
  2. a: {
  3. type: Object,
  4. default () {
  5. return {
  6. key1: "a",
  7. key2: {
  8. a: "b"
  9. }
  10. }
  11. }
  12. }
  13. }

在往上数的第二段代码里面的方法obervse(value),即对{key1: "a", key2: {a: "b"}}进行依赖的管理,同时将这个obj所有的属性值都转化为getter/setter形式。此外,Vue还会将props属性都代理到vm实例上,通过vm.key1,vm.key2就可以访问到这个属性。

此外,还需要了解下在Vue中管理依赖的一个非常重要的类: Dep

</>复制代码

  1. export default class Dep {
  2. constructor () {
  3. this.id = uid++
  4. this.subs = []
  5. }
  6. addSub () {...} // 添加订阅者(依赖)
  7. removeSub () {...} // 删除订阅者(依赖)
  8. depend () {...} // 检查当前Dep.target是否存在以及判断这个watcher已经被添加到了相应的依赖当中,如果没有则添加订阅者(依赖),如果已经被添加了那么就不做处理
  9. notify () {...} // 通知订阅者(依赖)更新
  10. }

Vue的整个生命周期当中,你所定义的响应式的数据上都会绑定一个Dep实例去管理其依赖。它实际上就是观察者订阅者联系的一个桥梁。

刚才谈到了对于依赖的管理,它的核心之一就是观察者Observer这个类:

</>复制代码

  1. export class Observer {
  2. value: any;
  3. dep: Dep;
  4. vmCount: number; // number of vms that has this object as root $data
  5. constructor (value: any) {
  6. this.value = value
  7. // dep记录了和这个value值的相关依赖
  8. this.dep = new Dep()
  9. this.vmCount = 0
  10. // value其实就是vm._data, 即在vm._data上添加__ob__属性
  11. def(value, "__ob__", this)
  12. // 如果是数组
  13. if (Array.isArray(value)) {
  14. // 首先判断是否能使用__proto__属性
  15. const augment = hasProto
  16. ? protoAugment
  17. : copyAugment
  18. augment(value, arrayMethods, arrayKeys)
  19. // 遍历数组,并将obj类型的属性改为getter/setter实现
  20. this.observeArray(value)
  21. } else {
  22. // 遍历obj上的属性,将每个属性改为getter/setter实现
  23. this.walk(value)
  24. }
  25. }
  26. /**
  27. * Walk through each property and convert them into
  28. * getter/setters. This method should only be called when
  29. * value type is Object.
  30. */
  31. // 将每个property对应的属性都转化为getter/setters,只能是当这个value的类型为Object时
  32. walk (obj: Object) {
  33. const keys = Object.keys(obj)
  34. for (let i = 0; i < keys.length; i++) {
  35. defineReactive(obj, keys[i], obj[keys[i]])
  36. }
  37. }
  38. /**
  39. * Observe a list of Array items.
  40. */
  41. // 监听array中的item
  42. observeArray (items: Array) {
  43. for (let i = 0, l = items.length; i < l; i++) {
  44. observe(items[i])
  45. }
  46. }
  47. }

walk方法里面调用defineReactive方法:通过遍历这个objectkey,并将对应的value转化为getter/setter形式,通过闭包维护一个dep,在getter方法当中定义了这个key是如何进行依赖的收集,在setter方法中定义了当这个key对应的值改变后,如何完成相关依赖数据的更新。但是从源码当中,我们却发现当getter函数被调用的时候并非就一定会完成依赖的收集,其中还有一层判断,就是Dep.target是否存在。

</>复制代码

  1. /**
  2. * Define a reactive property on an Object.
  3. */
  4. export function defineReactive (
  5. obj: Object,
  6. key: string,
  7. val: any,
  8. customSetter?: Function
  9. ) {
  10. // 每个属性新建一个dep实例,管理这个属性的依赖
  11. const dep = new Dep()
  12. // 或者属性描述符
  13. const property = Object.getOwnPropertyDescriptor(obj, key)
  14. // 如果这个属性是不可配的,即无法更改
  15. if (property && property.configurable === false) {
  16. return
  17. }
  18. // cater for pre-defined getter/setters
  19. const getter = property && property.get
  20. const setter = property && property.set
  21. // 递归去将val转化为getter/setter
  22. // childOb将子属性也转化为Observer
  23. let childOb = observe(val)
  24. Object.defineProperty(obj, key, {
  25. enumerable: true,
  26. configurable: true,
  27. // 定义getter -->> reactiveGetter
  28. get: function reactiveGetter () {
  29. const value = getter ? getter.call(obj) : val
  30. // 定义相应的依赖
  31. if (Dep.target) {
  32. // Dep.target.addDep(this)
  33. // 即添加watch函数
  34. // dep.depend()及调用了dep.addSub()只不过中间需要判断是否这个id的dep已经被包含在内了
  35. dep.depend()
  36. // childOb也添加依赖
  37. if (childOb) {
  38. childOb.dep.depend()
  39. }
  40. if (Array.isArray(value)) {
  41. dependArray(value)
  42. }
  43. }
  44. return value
  45. },
  46. // 定义setter -->> reactiveSetter
  47. set: function reactiveSetter (newVal) {
  48. const value = getter ? getter.call(obj) : val
  49. /* eslint-disable no-self-compare */
  50. if (newVal === value || (newVal !== newVal && value !== value)) {
  51. return
  52. }
  53. if (setter) {
  54. setter.call(obj, newVal)
  55. } else {
  56. val = newVal
  57. }
  58. // 对得到的新值进行observe
  59. childOb = observe(newVal)
  60. // 相应的依赖进行更新
  61. dep.notify()
  62. }
  63. })
  64. }

在上文中提到了Dep类是链接观察者订阅者的桥梁。同时在Dep的实现当中还有一个非常重要的属性就是Dep.target,它事实就上就是一个订阅者,只有当Dep.target(订阅者)存在的时候,调用属性的getter函数的时候才能完成依赖的收集工作。

</>复制代码

  1. Dep.target = null
  2. const targetStack = []
  3. export function pushTarget (_target: Watcher) {
  4. if (Dep.target) targetStack.push(Dep.target)
  5. Dep.target = _target
  6. }
  7. export function popTarget () {
  8. Dep.target = targetStack.pop()
  9. }

那么Vue是如何来实现订阅者的呢?Vue里面定义了一个类: Watcher,在Vue的整个生命周期当中,会有4类地方会实例化Watcher

Vue实例化的过程中有watch选项

Vue实例化的过程中有computed计算属性选项

Vue原型上有挂载$watch方法: Vue.prototype.$watch,可以直接通过实例调用this.$watch方法

Vue生成了render函数,更新视图时

</>复制代码

  1. constructor (
  2. vm: Component,
  3. expOrFn: string | Function,
  4. cb: Function,
  5. options?: Object
  6. ) {
  7. // 缓存这个实例vm
  8. this.vm = vm
  9. // vm实例中的_watchers中添加这个watcher
  10. vm._watchers.push(this)
  11. // options
  12. if (options) {
  13. this.deep = !!options.deep
  14. this.user = !!options.user
  15. this.lazy = !!options.lazy
  16. this.sync = !!options.sync
  17. } else {
  18. this.deep = this.user = this.lazy = this.sync = false
  19. }
  20. this.cb = cb
  21. this.id = ++uid // uid for batching
  22. this.active = true
  23. this.dirty = this.lazy // for lazy watchers
  24. ....
  25. // parse expression for getter
  26. if (typeof expOrFn === "function") {
  27. this.getter = expOrFn
  28. } else {
  29. this.getter = parsePath(expOrFn)
  30. if (!this.getter) {
  31. this.getter = function () {}
  32. }
  33. }
  34. // 通过get方法去获取最新的值
  35. // 如果lazy为true, 初始化的时候为undefined
  36. this.value = this.lazy
  37. ? undefined
  38. : this.get()
  39. }
  40. get () {...}
  41. addDep () {...}
  42. update () {...}
  43. run () {...}
  44. evaluate () {...}
  45. run () {...}

Watcher接收的参数当中expOrFn定义了用以获取watchergetter函数。expOrFn可以有2种类型:stringfunction.若为string类型,首先会通过parsePath方法去对string进行分割(仅支持.号形式的对象访问)。在除了computed选项外,其他几种实例化watcher的方式都是在实例化过程中完成求值及依赖的收集工作:this.value = this.lazy ? undefined : this.get().在Watcherget方法中:

!!!前方高能

</>复制代码

  1. get () {
  2. // pushTarget即设置当前的需要被执行的watcher
  3. pushTarget(this)
  4. let value
  5. const vm = this.vm
  6. if (this.user) {
  7. try {
  8. // $watch(function () {})
  9. // 调用this.getter的时候,触发了属性的getter函数
  10. // 在getter中进行了依赖的管理
  11. value = this.getter.call(vm, vm)
  12. console.log(value)
  13. } catch (e) {
  14. handleError(e, vm, `getter for watcher "${this.expression}"`)
  15. }
  16. } else {
  17. // 如果是新建模板函数,则会动态计算模板与data中绑定的变量,这个时候就调用了getter函数,那么就完成了dep的收集
  18. // 调用getter函数,则同时会调用函数内部的getter的函数,进行dep收集工作
  19. value = this.getter.call(vm, vm)
  20. }
  21. // "touch" every property so they are all tracked as
  22. // dependencies for deep watching
  23. // 让每个属性都被作为dependencies而tracked, 这样是为了deep watching
  24. if (this.deep) {
  25. traverse(value)
  26. }
  27. popTarget()
  28. this.cleanupDeps()
  29. return value
  30. }

一进入get方法,首先进行pushTarget(this)的操作,此时Vue当中Dep.target = 当前这个watcher,接下来进行value = this.getter.call(vm, vm)操作,在这个操作中就完成了依赖的收集工作。还是拿文章一开始的demo来说,在vue实例化的时候传入了watch选项:

</>复制代码

  1. props: {
  2. a: {
  3. type: Object,
  4. default () {
  5. return {
  6. key1: "a",
  7. key2: {
  8. a: "b"
  9. }
  10. }
  11. }
  12. }
  13. },
  14. watch: {
  15. a (newVal, oldVal) {
  16. console.log(newVal, oldVal)
  17. }
  18. },

VueinitState()开始执行后,首先会初始化props的属性为getter/setter函数,然后在进行initWatch初始化的时候,这个时候初始化watcher实例,并调用get()方法,设置Dep.target = 当前这个watcher实例,进而到value = this.getter.call(vm, vm)的操作。在调用this.getter.call(vm, vm)的方法中,便会访问props选项中的a属性即其getter函数。在a属性的getter函数执行过程中,因为Dep.target已经存在,那么就进入了依赖收集的过程:

</>复制代码

  1. if (Dep.target) {
  2. // Dep.target.addDep(this)
  3. // 即添加watch函数
  4. // dep.depend()及调用了dep.addSub()只不过中间需要判断是否这个id的dep已经被包含在内了
  5. dep.depend()
  6. // childOb也添加依赖
  7. if (childOb) {
  8. childOb.dep.depend()
  9. }
  10. if (Array.isArray(value)) {
  11. dependArray(value)
  12. }
  13. }

dep是一开始初始化的过程中,这个属性上的dep属性。调用dep.depend()函数:

</>复制代码

  1. depend () {
  2. if (Dep.target) {
  3. // Dep.target为一个watcher
  4. Dep.target.addDep(this)
  5. }
  6. }

Dep.target也就刚才的那个watcher实例,这里也就相当于调用了watcher实例的addDep方法: watcher.addDep(this),并将dep观察者传入。在addDep方法中完成依赖收集:

</>复制代码

  1. addDep (dep: Dep) {
  2. const id = dep.id
  3. if (!this.newDepIds.has(id)) {
  4. this.newDepIds.add(id)
  5. this.newDeps.push(dep)
  6. if (!this.depIds.has(id)) {
  7. dep.addSub(this)
  8. }
  9. }
  10. }

这个时候依赖完成了收集,当你去修改a属性的值时,会调用a属性的setter函数,里面会执行dep.notify(),它会遍历所有的订阅者,然后调用订阅者上的update函数。

initData过程和initProps类似,具体可参见源码。

initComputed

以上就是在initProps过程中Vue是如何进行依赖收集的,initData的过程和initProps类似,下来再来看看initComputed的过程.
computed属性初始化的过程当中,会为每个属性实例化一个watcher:

</>复制代码

  1. const computedWatcherOptions = { lazy: true }
  2. function initComputed (vm: Component, computed: Object) {
  3. // 新建_computedWatchers属性
  4. const watchers = vm._computedWatchers = Object.create(null)
  5. for (const key in computed) {
  6. const userDef = computed[key]
  7. // 如果computed为funtion,即取这个function为getter函数
  8. // 如果computed为非function.则可以多带带为这个属性定义getter/setter属性
  9. let getter = typeof userDef === "function" ? userDef : userDef.get
  10. // create internal watcher for the computed property.
  11. // lazy属性为true
  12. // 注意这个地方传入的getter参数
  13. // 实例化的过程当中不去完成依赖的收集工作
  14. watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions)
  15. // component-defined computed properties are already defined on the
  16. // component prototype. We only need to define computed properties defined
  17. // at instantiation here.
  18. if (!(key in vm)) {
  19. defineComputed(vm, key, userDef)
  20. }
  21. }
  22. }

但是这个watcher在实例化的过程中,由于传入了{lazy: true}的配置选项,那么一开始是不会进行求值与依赖收集的: this.value = this.lazy ? undefined : this.get().在initComputed的过程中,Vue会将computed属性定义到vm实例上,同时将这个属性定义为getter/setter。当你访问computed属性的时候调用getter函数:

</>复制代码

  1. function createComputedGetter (key) {
  2. return function computedGetter () {
  3. const watcher = this._computedWatchers && this._computedWatchers[key]
  4. if (watcher) {
  5. // 是否需要重新计算
  6. if (watcher.dirty) {
  7. watcher.evaluate()
  8. }
  9. // 管理依赖
  10. if (Dep.target) {
  11. watcher.depend()
  12. }
  13. return watcher.value
  14. }
  15. }
  16. }

watcher存在的情况下,首先判断watcher.dirty属性,这个属性主要是用于判断这个computed属性是否需要重新求值,因为在上一轮的依赖收集的过程当中,观察者已经将这个watcher添加到依赖数组当中了,如果观察者发生了变化,就会dep.notify(),通知所有的watcher,而对于computedwatcher接收到变化的请求后,会将watcher.dirty = true即表明观察者发生了变化,当再次调用computed属性的getter函数的时候便会重新计算,否则还是使用之前缓存的值。

initWatch

initWatch的过程中其实就是实例化new Watcher完成观察者的依赖收集的过程,在内部的实现当中是调用了原型上的Vue.prototype.$watch方法。这个方法也适用于vm实例,即在vm实例内部调用this.$watch方法去实例化watcher,完成依赖的收集,同时监听expOrFn的变化。

总结:

以上就是在Vue实例初始化的过程中实现依赖管理的分析。大致的总结下就是:

initState的过程中,将props,computed,data等属性通过Object.defineProperty来改造其getter/setter属性,并为每一个响应式属性实例化一个observer观察者。这个observer内部dep记录了这个响应式属性的所有依赖。

当响应式属性调用setter函数时,通过dep.notify()方法去遍历所有的依赖,调用watcher.update()去完成数据的动态响应。

这篇文章主要从初始化的数据层面上分析了Vue是如何管理依赖来到达数据的动态响应。下一篇文章来分析下Vue中模板中的指令和响应式数据是如何关联来实现由数据驱动视图,以及数据是如何响应视图变化的。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/83791.html

相关文章

  • Vue源码解析(5)-virtual-dom 实现简析

    传送门vdom原理

    darcrand 评论0 收藏0
  • 简析 js 碰撞检测原理与算法实现

    摘要:碰撞检测边界检测在前端游戏,以及涉及拖拽交互的场景应用十分广泛。这就涉及到碰撞检测或者叫边界检测的问题了。若四边均未发生重合,则未发生碰撞,反之则发生碰撞。目前业务有遇到碰撞需求,所以抽时间整理了下。 碰撞检测(边界检测)在前端游戏,以及涉及拖拽交互的场景应用十分广泛。 那么啥叫碰撞?JavaScript 又是如何检测 DOM 发生碰撞的呢? 碰撞,顾名思义,就是两个物体碰撞在了一起,...

    Miyang 评论0 收藏0
  • 简析 js 碰撞检测原理与算法实现

    摘要:碰撞检测边界检测在前端游戏,以及涉及拖拽交互的场景应用十分广泛。这就涉及到碰撞检测或者叫边界检测的问题了。若四边均未发生重合,则未发生碰撞,反之则发生碰撞。目前业务有遇到碰撞需求,所以抽时间整理了下。 碰撞检测(边界检测)在前端游戏,以及涉及拖拽交互的场景应用十分广泛。 那么啥叫碰撞?JavaScript 又是如何检测 DOM 发生碰撞的呢? 碰撞,顾名思义,就是两个物体碰撞在了一起,...

    Markxu 评论0 收藏0
  • Loader学习,简析babel-loader

    摘要:用来转换内容,内部调用了方法进行转换,这里简单介绍一下的原理将代码解析成,对进行转译,得到新的,新的通过转换成,核心方法在中的方法,有兴趣可以去了解一下。将函数传入参数和归并,得到。通常我们是用不上的,估计在某些中可能会使用到。 什么是Loader? 继上两篇文章webpack工作原理介绍(上篇、下篇),我们了解到Loader:模块转换器,也就是将模块的内容按照需求装换成新内容,而且每...

    wpw 评论0 收藏0

发表评论

0条评论

quietin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<