day108-ts实现mvvm-笔记(1)

前言

Vue3.0的源码要用typescript来编写,同时个人随着近期typescript的练习渐渐变多,比如上周就用typescript写了,国际化的文本提取解析和替换(其中涉及到babel),也感觉到静态语言的强大。所以就慢慢的上手typescript咯。


代码-依赖收集-dep

之前依赖收集这部分还是有点迷糊的,所以这次着重先去尝试着完全理解依赖收集这部分,尤其是Dep.target = null的上下文发生的事情。还是比较习惯先上代码来说话。

dep.js

关于 Observer, Dep, Watcher相互间的联系总结出Dep的几句话:

  1. Dep实质是个数组,放置监听这个Observer的Watcher,当Observer对应的值变化时,就通知Dep中的所有Watcher执行callback。
  2. 闭包中的Dep保存这个键的Watcher
  3. Watcher该放进哪些Dep
    规定一个全局变量,初始化null,依赖收集阶段,让全局变量指向Watcher

    主要还是Dep和Watcher是怎样的关系,这点容易迷糊。

代码

 // util一些检验方法
import { remove } from './util'
import { Watcher } from './watcher'

let uid = 0
export default class Dep {
// Dep.target 即全局变量
public static target: Watcher | null = null

public id: number
public subs: Watcher[]

constructor() {
this.id = uid++
this.subs = []
}

public addSub (sub: Watcher) {
this.subs.push(sub)
}
public removeSub (sub: Watcher) {
remove(this.subs, sub)
}

// Dep.target, ?????
public depend () {
if (Dep.target) {
this.addSub(Dep.target)
}
}

public notify() {
// 防止数组变化,先复制一份????
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
// Watcher上定义的update方法
subs[i].update()
}
}
}
// 注意: 这里vuejs源码中还有加了一段。(vue/src/core/observer/dep.js, 318f29f on 10 Mar 2018)
// the current target watcher being evaluated.
// this is globally unique because there could be only one
// watcher being evaluated at any time.

// Dep.target = null
// const targetStack = []

// export function pushTarget(_target: ?Watcher) {
// if (Dep.target) targetStack.push(Dep.target)
// Dep.target = _target
// }

// export function popTarget () {
// Dep.target = targetStack.pop()
// }

watcher.js

import Dep from './dep'

let uid = 0

export class Watcher {
public id: number
public value: any
public target: any
public getter: (target: any) => any
public callback: (newVal: any, oldVal: any) => void

constructor(target: any, expression: string, callback: (newVal: any, oldVal: any) => void) {
this.id = uid++
this.target = target
this.getter = parsePath(expression)
this.callback = callback
this.value = this.get()
}
/**
* 依赖收集阶段
* Evaluate the getter, and re-collect dependencies.
*/
public get() {
// 进入依赖收集阶段
Dep.target = this

let value: any
const obj = this.target

try {
// 调用getter,对应键的dep中放入了这个watcher
value = this.getter(obj)
} finally {
// 退出依赖收集阶段
Dep.target = null
}
return value
}

public update() {
this.run()
}

public run() {
this.getAndInvoke(this.callback)
}

public getAndInvoke(cb: (newVal: any, oldVal: any) => void) {
// 监视目标为对象或数组的??
if (value !== this.value || isObject(value)) {
const oldVal = this.value
this.value = value
// 回调
cb.call(this.target, value, oldVal)
}
}

// 简单的解析一个路径
const bailRE = /[^\w.$]/
functiono parsePath(path: string): any {
if (this.bailRE.test(path)) {
return
}
const segments = path.split(".")
return (obj: any) => {
for (const segment of segments) {
if (!obj) {
return
}
obj = obj[segment]
}
return obj
}
}
}
文章作者: lmislm
文章链接: http://lmislm.com/2019/04/30/2019-04-30/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LMISLMのBlog