state.js
给state添加 initComputed 方法
工作流程:
- 计算属性是有缓存的,我们要创建两个变量存数据
- 遍历computed的值,创建带{dirty:true}的watcher,并保存到watchers对象中
- 当用户取值的时候,获取当前watcher运行evalValue()进行运算
- 编写watcher用dirty保存值是否改变,判断其是否需要重新计算
- 取出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
- 编写 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