项目中 vue 比较多, 大概知道实现, 最近翻了一下双向绑定的代码, 这里写一下阅读后的理解.
项目目录
拉到 vue 的代码之后, 首先来看一下项目目录, 因为本文讲的是双向绑定, 所以这里主要看双向绑定这块的代码.
入口
从入口开始: src/core/index.JS
index.JS 比较简单, 第一句就引用了 Vue 进来, 看下 Vue 是啥
import Vue from './instance/index'
Vue 构造函数
来到 src/core/instance/index.JS
一进来, 嗯, 没错, 定义了 Vue 构造函数, 然后调用了好几个方法, 这里我们看第一个 initMixin
- import { initMixin } from './init'
- function Vue (options) {
- if (process.env.NODE_ENV !== 'production' &&
- !(this instanceof Vue)
- ) {
- warn('Vue is a constructor and should be called with the `new` keyword')
- }
- this._init(options)
- }
- initMixin(Vue)
- ...
- export default Vue
初始化
来到 src/core/instance/init.JS
这个给 Vue 构造函数定义了_init 方法, 每次 new Vue 初始化实例时都会调用该方法.
然后看到_init 中间的代码, 调用了好多初始化的函数, 这里我们只关注 data 的走向, 所以这里看一下 initState
- export function initMixin (Vue: Class<Component>) {
- Vue.prototype._init = function (options?: Object) {
- const vm: Component = this
- ...
- initState(vm)
- ...
- }
- }
来到 src/core/instance/state.JS
initState 调用了 initData,initData 调用了 observe, 然后我们再往下找 observe
- import { observe } from '../observer/index'
- export function initState (vm: Component) {
- vm._watchers = []
- const opts = vm.$options
- ...
- if (opts.data) {
- initData(vm)
- } else {
- observe(vm._data = {}, true /* asRootData */)
- }
- ...
- }
- function initData (vm: Component) {
- let data = vm.$options.data
- ...
- observe(data, true /* asRootData */)
- }
Observer(观察者)
来到 src/core/observer/index.JS
这里, 实例化 Observer 对象
首先, new Observer 实例化一个对象, 参数为 data
- export function observe (value: any, asRootData: ?boolean): Observer | void {
- let ob: Observer | void
- ob = new Observer(value)
- return ob
- }
然后我们来看下 Observer 构造函数里面写了什么, 这里给每个对象加了 value 和实例化了一个 Dep, 然后 data 为数组的话则递归, 否则执行 walk.
walk 这里是对对象遍历执行 defineReactive
- export class Observer {
- value: any;
- dep: Dep;
- vmCount: number; // number of vms that has this object as root $data
- constructor (value: any) {
- this.value = value
- this.dep = new Dep()
- if (Array.isArray(value)) {
- ...
- this.observeArray(value)
- } else {
- this.walk(value)
- }
- }
- walk (obj: Object) {
- const keys = Object.keys(obj)
- for (let i = 0; i <keys.length; i++) {
- defineReactive(obj, keys[i])
- }
- }
- observeArray (items: Array<any>) {
- for (let i = 0, l = items.length; i < l; i++) {
- observe(items[i])
- }
- }
- }
然后, 我们来看 defineReactive 做了什么, 嗯, 这里就是 Observer 的核心.
用 Object.defineProperty 对对象进行配置, 重写 get&set
get: 对原来 get 执行, 然后执行 dep.depend 添加一个订阅者
set: 对原来 set 执行, 然后执行 dep.notify 通知订阅者
Dep 是干啥的呢? Dep 其实是一个订阅者的管理中心, 管理着所有的订阅者
- import Dep from './dep'
- export function defineReactive (
- obj: Object,
- key: string,
- val: any,
- customSetter?: ?Function,
- shallow?: boolean
- ) {
- const dep = new Dep()
- const property = Object.getOwnPropertyDescriptor(obj, key)
- const getter = property && property.get
- const setter = property && property.set
- let childOb = !shallow && observe(val)
- Object.defineProperty(obj, key, {
- enumerable: true,
- configurable: true,
- get: function reactiveGetter () {
- const value = getter ? getter.call(obj) : val
- if (Dep.target) {
- dep.depend()
- }
- return value
- },
- set: function reactiveSetter (newVal) {
- const value = getter ? getter.call(obj) : val
- if (setter) {
- setter.call(obj, newVal)
- } else {
- val = newVal
- }
- dep.notify()
- }
- })
- }
Dep(订阅者管理中心)
那么, 到这里了, 疑问的是什么时候会触发 Observer 的 get 方法来添加一个订阅者呢?
这里的条件是有 Dep.target 的时候, 那么我们找一下代码中哪里会对 Dep.target 赋值, 找到了 Dep 定义的地方
来到 src/core/observer/dep.JS
pushTarget 就对 Dep.target 赋值了, 然后来看一下到底是哪里调用了 pushTarget
- export default class Dep {
- ...
- }
- Dep.target = null
- const targetStack = []
- export function pushTarget (_target: ?Watcher) {
- if (Dep.target) targetStack.push(Dep.target)
- Dep.target = _target
- }
然后, 找到了 Watcher 调用了 pushTarget, 那么我们来看一下 Watcher 的实现
来到 src/core/observer/watcher.JS
这里可以看到每次 new Watcher 时, 就会调用 get 方法
这里执行两步操作
第一: 调用 pushTarget
第二: 调用 getter 方法触发 Observer 的 get 方法将自己加入订阅者
- export default class Watcher {
- vm: Component;
- constructor (
- vm: Component
- ) {
- this.value = this.lazy
- ? undefined
- : this.get()
- }
- get () {
- pushTarget(this)
- let value
- const vm = this.vm
- try {
- value = this.getter.call(vm, vm)
- }
- return value
- }
- }
接着, Dep.target 有了之后, 接下来就要看一下 dep.depend() 这个方法, 所以还是要到 Dep 来看下这里的实现. 来到 src/core/observer/dep.JS
这里调用了 Dep.target.addDep 的方法, 参数是 Dep 的实例对象, 那么我们看下 addDep
- export default class Dep {
- addSub (sub: Watcher) {
- this.subs.push(sub)
- }
- depend () {
- if (Dep.target) {
- Dep.target.addDep(this)
- }
- }
- }
Watcher(观察者)
又来到 src/core/observer/watcher.JS
到这, addDep 其实又调用时 Dep 实例的 addSub 方法, 参数也是把 Watcher 实例传递过去
然后, 我们看上面的 addSub, 这里就是把 Watcher 实例 push 到 dep 的 subs 数组中保存起来
到这里, 就完成了把 Watcher 加入到 Dep 这里订阅器管理中心这里, 后面的管理就由 Dep 来统一管理
- export default class Watcher {
- addDep (dep: Dep) {
- const id = dep.id
- if (!this.newDepIds.has(id)) {
- this.newDepIds.add(id)
- this.newDeps.push(dep)
- if (!this.depIds.has(id)) {
- dep.addSub(this)
- }
- }
- }
- }
走完了添加订阅器, 接着再来看下 Observer 的 set 方法, 这里调用了 dep.notify, 我们来看一下这个方法
来到 src/core/observer/dep.JS
这里, 就是对 subs 中的所有 Watcher, 调用其 update 方法来更新数据
- export default class Dep {
- notify () {
- const subs = this.subs.slice()
- for (let i = 0, l = subs.length; i < l; i++) {
- subs[i].update()
- }
- }
- }
这里, 我们就来看看 Watcher 是怎么更新的 又来到 src/core/observer/watcher.JS
update 调用的是 run 方法, run 方法这里先用 get 拿到新的值, 然后把新 & 旧值做为参数给 cb 调用
- export default class Watcher {
- update () {
- this.run()
- }
- run () {
- if (this.active) {
- const value = this.get()
- const oldValue = this.value
- this.value = value
- this.cb.call(this.vm, value, oldValue)
- }
- }
- }
这里的 cb 其实是实例化的时候传进来的, 这里我们看一下什么时候会实例化 Watcher
回到一开始的 initState:src/core/instance/state.JS
initState 的最后还调用了 initWatch, 然后再 createWatcher, 最后 $watch 的时候就实例化了 Watcher 对象, 这里就把 cb 传到了 Watcher 实例中, 当监听的数据改变的时候就会触发 cb 函数
- import Watcher from '../observer/watcher'
- export function initState (vm: Component) {
- ...
- if (opts.watch && opts.watch !== nativeWatch) {
- initWatch(vm, opts.watch)
- }
- }
- function initWatch (vm: Component, watch: Object) {
- for (const key in watch) {
- const handler = watch[key]
- createWatcher(vm, key, handler)
- }
- }
- function createWatcher (
- vm: Component,
- expOrFn: string | Function,
- handler: any,
- options?: Object
- ) {
- return vm.$watch(expOrFn, handler, options)
- }
- Vue.prototype.$watch = function (
- expOrFn: string | Function,
- cb: any,
- options?: Object
- ): Function {
- const vm: Component = this
- const watcher = new Watcher(vm, expOrFn, cb, options)
- }
写在最后
这里的思路, 其实就是翻着源码走的, 写的时候都是按自己的理解思路来的, 存在问题的话欢迎指出~
来源: https://juejin.im/post/5bd5d1c86fb9a05d1117cc55