preface

  • Project address in this chapter
  • watchEach attribute innewaUser watcher (new watcher)
  • Start when the data is initializednew WatcherDep.target refers to the user watcher at this time, and the user watcher is added to this propertydep.addSub.push(watcher)
  • When the data in data changes, all watcher’s for that data are called
  • Watcher stores the old values and returns the new and old values to the expression cb when the data changes.

The sample

<div id="app">{{ name }}</div>

<script src="dist/vue.js"></script>
<script>
    var vm = new Vue({
        data: {
            name: 'one'
        },
        /** User watcher several ways */
        watch: {
            // name(newVal, oldVal) {
            // console.log(newVal, oldVal)
            // },
            // name: [
            // function(newVal, oldVal) {
            // console.log(newVal, oldVal)
            / /},
            // function(a, b) {
            // console.log(a, b)
            / /}
            / /,
            // 'obj.n'(newVal, oldVal) {
            // console.log(newVal, oldVal)
            // }
        }
    })

    vm.$mount('#app')

    vm.$watch('name'.function(newValue, oldValue) {
        console.log(newValue, oldValue)
    })
    setTimeout(() = > {
        vm.name = 'two'
    }, 2000)
Copy the code

To the chase

$watch

export function stateMixin(Vue) {
    Vue.prototype.$watch = function(key, handler, options={}) {
        // User created watcher
        options.user = true

        new Watcher(this, key, handler, options)
    }
}
Copy the code

Options. watch initialization starts

export function initState(vm) {
    const opts = vm.$options

    if (opts.watch) {
        initWatch(vm, opts.watch)
    }

}
Copy the code
/** initWatche module */
/ * * *@description Call $watch * /
function createWatcher(vm, key, handler) {
    return vm.$watch(key, handler)
}

/ * * *@description Initialize watch to split the function of the watch property *@description Create new Watcher */ for each observation property
function initWatch(vm, watch) {
    for (const key in watch) {
        const handler = watch[key]
        if (Array.isArray(handler)) {
            for (let i = 0; i < handler.length; i++) {
                createWatcher(vm, key, handler[i])
            }
        } else {
            createWatcher(vm, key, handler)
        }
    }
}
Copy the code

Watcher class (Key)

import { pushTarget, popTarget } from './dep'
import { queueWatcher } from './scheduler'

/** Give new Watcher an id */
let id = 0

/ * * *@description Data hijacking expects one property to correspond to multiple Watcher properties */
class Watcher {
    constructor(vm, exprOrFn, cb, options) {
        this.vm = vm
        this.exprOrFn = exprOrFn
        this.cb = cb
        this.options = options

        
        this.id = id++
        
        this.deps = []
        this.depsId = new Set(a)/** user watcher */
        this.user = !! options.user// See here to wrap the obtained function name into an expression
        if (typeof exprOrFn == 'string') {
            this.getter = function() {
                // vm value 'obj. N '-> ['obj', 'n'] -> vm['obj']['n']
                let path = exprOrFn.split('. ')
                let obj = vm
                for (let i = 0; i < path.length; i++) {
                    obj = obj[path[i]]
                }
                return obj
            }
        } else {
            this.getter = exprOrFn
        }

        /** User watcher gets the first value */ by default
        this.value = this.get()
    }

    /** render generates vNode dep.push(watcher) and updates view */
    get() {
        pushTarget(this)
        // Look here for hijacked data on the VM to push the user Watcher on this property
        const value = this.getter.call(this.vm)
        popTarget()

        return value
    }

    update() {
         queueWatcher(this)}run() {
        /** Consider 1 render component watcher */
        /** consider 2 user watcher(newValue oldValue) */
        let newValue = this.get()
        let oldValue = this.value
        this.value = newValue

        /** see here user watcher returns the value to cb and calls */
        if (this.user) {
            this.cb.call(this.vm, newValue, oldValue)
        }
    }

    /** Store the deP and exclude the same attribute from being called multiple times when a vNode is generated
    /** Second dep and watcher are no longer stored when attributes change
    addDep(dep) {
        let id = dep.id
        if (!this.depsId.has(id)) {
            this.depsId.add(id)
            this.deps.push(dep)
            dep.addSub(this)}}}export default Watcher

Copy the code

Dep class

/** Each hijacked attribute is uniquely identified */
let id = 0

/ * * *@description Each hijacked attribute new Dep */
class Dep {
    constructor() {
        this.id = id++
        this.subs = []
    }

    /** dep to watcher */
    depend() {
        if (Dep.target) {
            Dep.target.addDep(this)}}addSub(watcher) {
        this.subs.push(watcher)
    }

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

}

Dep.target = null
let stack = []

export function pushTarget(watcher) {
    Dep.target = watcher
    stack.push(watcher)
}

export function popTarget() {
    stack.pop()
    Dep.target = stack[stack.length - 1]}export default Dep
Copy the code

After the

The next chapter is about vuE2 core principles (simple)- Computed Notes