observer/dep.js - 图1

    1. /* @flow */
    2. import type Watcher from './watcher'
    3. import {
    4. remove
    5. } from '../util/index'
    6. import config from '../config'
    7. let uid = 0
    8. /**
    9. * A dep is an observable that can have multiple
    10. * directives subscribing to it.
    11. */
    12. export default class Dep {
    13. static target: ? Watcher; // 静态属性,其实就是全局的一个变量
    14. id: number;
    15. subs: Array < Watcher > ;
    16. constructor() {
    17. this.id = uid++
    18. this.subs = []
    19. }
    20. /*添加一个观察者对象*/
    21. addSub(sub: Watcher) {
    22. this.subs.push(sub)
    23. }
    24. /*移除一个观察者对象*/
    25. removeSub(sub: Watcher) {
    26. remove(this.subs, sub)
    27. }
    28. /*
    29. * 依赖收集,当存在Dep.target的时候添加观察者对象
    30. * Dep.target是一个全局的对象,是watcher的一个实例
    31. * 所以会有addDep方法,把watcher本身push到dep.subs
    32. * */
    33. depend() {
    34. if (Dep.target) {
    35. Dep.target.addDep(this)
    36. }
    37. }
    38. /*通知所有订阅者*/
    39. notify() {
    40. // stabilize the subscriber list first
    41. const subs = this.subs.slice()
    42. if (process.env.NODE_ENV !== 'production' && !config.async) {
    43. // subs aren't sorted in scheduler if not running async
    44. // we need to sort them now to make sure they fire in correct
    45. // order
    46. // 如果不是异步运行,subs在调度器中没有排序,
    47. // 我们现在需要对它们进行排序,以确保它们以正确的顺序启动。
    48. subs.sort((a, b) => a.id - b.id)
    49. }
    50. for (let i = 0, l = subs.length; i < l; i++) {
    51. subs[i].update()
    52. }
    53. }
    54. }
    55. // The current target watcher being evaluated.
    56. // This is globally unique because only one watcher
    57. // can be evaluated at a time.
    58. /*
    59. 当前target的watcher依赖正在被收集,
    60. 一次只会收集一个watcher,因为Dep.target是全局的唯一变量
    61. */
    62. Dep.target = null
    63. const targetStack = []
    64. /*将watcher观察者实例设置给Dep.target,用以依赖收集。同时将该实例存入target栈中*/
    65. export function pushTarget(target: ? Watcher) {
    66. targetStack.push(target)
    67. Dep.target = target
    68. }
    69. /*存疑
    70. * 出栈的一个watcher
    71. * 和赋值Dep.target的明显不是一个watcher
    72. * 在生命周期方法和data的初始化中,都会禁用依赖收集
    73. * 使用的方式就是在Dep.target中放入一个undefined
    74. * 具体的原因,在子组件进行data初始化时,触发到getter会调用addDep
    75. * 会收集到父组件的render watcher,
    76. * 导致子组件更改 会令父组件触发更新,即使父组件没有更改
    77. * https://github.com/vuejs/vue/commit/318f29fcdf3372ff57a09be6d1dc595d14c92e70#diff-7060e3d9c5c616dbceee1f0d33ca5ff0
    78. * */
    79. export function popTarget() {
    80. targetStack.pop()
    81. Dep.target = targetStack[targetStack.length - 1]
    82. }
    1. // https://github.com/vuejs/vue/commit/318f29fcdf3372ff57a09be6d1dc595d14c92e70#diff-7060e3d9c5c616dbceee1f0d33ca5ff0
    2. it('props should not be reactive', done => {
    3. let calls = 0
    4. const vm = new Vue({
    5. template: `<child :msg="msg"></child>`,
    6. data: {
    7. msg: 'hello'
    8. },
    9. beforeUpdate () { calls++ },
    10. components: {
    11. child: {
    12. template: `<span>{{ localMsg }}</span>`,
    13. props: ['msg'],
    14. data () {
    15. return { localMsg: this.msg }
    16. },
    17. computed: {
    18. computedMsg () {
    19. return this.msg + ' world'
    20. }
    21. }
    22. }
    23. }
    24. }).$mount()
    25. const child = vm.$children[0]
    26. vm.msg = 'hi'
    27. waitForUpdate(() => {
    28. expect(child.localMsg).toBe('hello')
    29. expect(child.computedMsg).toBe('hi world')
    30. expect(calls).toBe(1)
    31. }).then(done)
    32. })