vue源码系列08_computed属性

vue源码系列07_computed属性

state.js

给state添加 initComputed 方法
工作流程:

  1. 计算属性是有缓存的,我们要创建两个变量存数据
  2. 遍历computed的值,创建带{dirty:true}的watcher,并保存到watchers对象中
  3. 当用户取值的时候,获取当前watcher运行evalValue()进行运算
  4. 编写watcher用dirty保存值是否改变,判断其是否需要重新计算
  5. 取出watcher中的dep,触发depend()添加渲染的watcher
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 createComputedGetter(vm, key) {
    let watcher = vm._watcherComputed[key];
    return function () {
        if (watcher) {
            if (watcher.dirty) {
                // 页面取值的时候dirty如果是true 就会调用get方法 计算
                watcher.evalValue()
            }
            if (Dep.target) {
                watcher.depend()
            }
            return watcher.value
        }
    }
}
function initComputed(vm, computed) {
    // 计算属性是有缓存的
    let watchers = vm._watcherComputed = Object.create(null);
    for (let key in computed) {
        let userDef = computed[key]
        watchers[key] = new Watcher(vm, userDef, () => { }, { lazy: true }) // 存watcher
        // 当用户取值的时候,我们将key定义到我们的vm上
        Object.defineProperty(vm, key, {
            get: createComputedGetter(vm, key)
        })
    }
}

function createWatch(vm, key, handler) {

    return vm.$watch(key, handler)
}

function initWatch(vm) {
    let watch = vm.$options.watch
    for (let key in watch) {
        let handler = watch[key]
        createWatch(vm, key, handler)
    }
}


watcher

  1. 编写 evalValue()
evalValue() {
    this.value = this.get()
    this.dirty = false; //第二次计算的时候走缓存
}
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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章