vue源碼系列07_watch屬性

state.js

在該方法中,保存了vue實例初始的5種屬性,我們找到watch屬性的位置,編寫其方法即可

import { observe } from './observer/index.js'
import Watcher from './watcher.js';
import Dep from './observer/dep.js';
export function initState(vm) {
    const opts = vm.$options;
    // vue的數據來源 屬性 方法 數據 計算屬性 watch
    if (opts.props) {
        initProps(vm);
    }
    if (opts.methods) {
        initMethod(vm);
    }
    if (opts.data) {
        initData(vm);
    }
    if (opts.computed) {
        initComputed(vm, opts.computed);
    }
    if (opts.watch) {
        initWatch(vm);
    }
}

function initProps() { }
function initMethod() { }

function proxy(vm, source, key) {
    Object.defineProperty(vm, key, { // 給vm添加了監聽屬性
        get() {
            return vm[source][key]
        },
        set(newValue) {
            return vm[source][key] = newValue
        }
    })
}

function initData(vm) {
    // 數據初始化工作
    let data = vm.$options.data; //用戶傳遞的data
    data = vm._data = typeof data === 'function' ? data.call(vm) : data; //如果data是一個函數,就直接執行,並把this指向vue實例,否則就獲取data對象
    // 對象劫持,用戶改變了數據 我希望可以得到通知 => 刷新頁面
    // MVVM 模式 數據變化驅動視圖變化
    // Object.defineProperty() 給屬性添加get方法和set方法

    for (let key in data) {
        proxy(vm, "_data", key)
    }


    observe(data); // 響應式原理
}
function initComputed(vm, computed) {}
function initWatch(vm) {}

$watch

在原型上添加該方法,用於創建watcher

Vue.prototype.$watch = function(key,handler) {
     let vm = this
     new Watcher(vm,key,handler,{user:true}) // user:true 代表是用戶單獨調用
 }

修改watcher

  1. 主要是判斷 exprOrFn 是否爲函數,由於我們傳入的是key並非函數,所以我們得編寫getValue方法,取出value
  2. 修改get() 方法,返回一個老值
  3. 修改run()方法, 判斷新值是否相等於老值,如果不相等,就觸發cb(用戶寫在watch裏的函數)
import { pushTarget, popTarget } from "./observer/dep";
import { util } from "./utils/index";

let id = 0
class Watcher {
    constructor(vm, exprOrFn, cb = () => { }, opts) {
        this.vm = vm
        this.exprOrFn = exprOrFn
        this.cb = cb
        this.id = id++
        this.deps = []
        this.depsId = new Set()
        if (opts && opts.lazy) {
            this.lazy = opts.lazy
        }
        this.dirty = this.lazy // 緩存屬性
        if (typeof exprOrFn === 'function') {
            this.getter = exprOrFn
        } else {
            // 現在 exprOrFn 是我們傳進來的key
            this.getter = function () {
                return util.getValue(vm, exprOrFn)
            }
        }
        this.value = this.lazy ? undefined : this.get() // 獲取老值
        if (opts && opts.user) {
            this.user = true
        }
        // 如果當前是我們的計算屬性的話 不會默認調用get方法
    }
    evalValue() {
        this.value = this.get()
        this.dirty = false; //第二次計算的時候走緩存
    }
    get() {
        pushTarget(this)
        let value = this.getter.call(this.vm)
        popTarget()
        return value // 返回老值
    }
    update() {
        if (this.lazy) {
            this.dirty = true
        } else {
            queueWatcher(this)
        }
        // this.get()
    }
    run() {
        let value = this.get(); //獲取新值
        if (this.value !== value) {
            this.cb(value, this.value)
        }
    }
    addDep(dep) {
        let id = dep.id
        // 當該watcher沒有相同的 dep
        if (!this.depsId.has(id)) {
            this.depsId.add(id)
            this.deps.push(dep)
            dep.addSub(this)
        }
    }
    depend() {
        let i = this.deps.length
        while(i--){
            this.deps[i].depend()
        }
    }
}
let has = {}
let queue = [];

function flusqueue() {
    queue.forEach(watcher => watcher.run())
    has = {};
    queue = []
}
function queueWatcher(watcher) {
    let id = watcher.id
    if (has[id] == null) {
        has[id] = true
        queue.push(watcher)
    }
    nextTick(flusqueue)
}

// 異步執行
let callbacks = [];

function flushCallbacks() {
    callbacks.forEach(cb => cb())
}
function nextTick(flusqueue) {
    callbacks.push(flusqueue)
    let asyncFn = () => {
        flushCallbacks()
    }
    if (Promise) {
        Promise.resolve().then(asyncFn)
    }
    setTimeout(asyncFn, 0)
}

export default Watcher

initWatch()

工作步驟:

  1. 獲取watch屬性
  2. 將watch的值取出
  3. 給每個值創建對應的watcher
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章