看 Vue 源码步骤

1,635 阅读3分钟

前言

本文主要记一下看Vue源码的步骤,以 Vue 2.6 为例,研究一下 Vue 到底对 data 做了什么

第一步

首先进入 Vue 官网
点击 Github

第二步

点击 dev 输入自己想要看的分支版本,切换到这个分支

截屏2021-12-19 下午11.50.22.png

选择 src 这个目录

截屏2021-12-19 下午11.52.45.png

进入后 选择 core

截屏2021-12-19 下午11.53.11.png

想研究哪个就去看哪个目录

这里去看一下 Vue 的实例

截屏2021-12-19 下午11.55.59.png

第三步

点开 index.js

import { initMixin } from './init'  // 4. initMixin 从这里来的,于是去找当前目录的 init
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

function Vue (options) {  // 1. 构造了一个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)  // 2. 初始化了 options,那这个 _init, 做了啥?
}

initMixin(Vue)  // 3. 看名字 init,利用排除法 感觉是它做的,然后去看 initMixin 哪来的( cmd/ctrl + f 搜索一下)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

第四步

来到当前目录的 init.js

链接

截屏2021-12-20 上午12.09.12.png 在 Vue 原型上加了 init属性,接收一个 options,那它到底做了啥? 于是就往下面找

截屏2021-12-20 上午12.12.09.png 感觉是这个 initState(vm) 因为找不到 data,退而求其次看它,其他的看的不像
于是去看initState(vm)它是从哪里来的

截屏2021-12-20 上午12.15.55.png 去找当前目录下的 state 目录

第五步

全局查找 initState

截屏2021-12-20 上午12.19.24.png

if (opts.data) { 
  initData(vm)  // 如果你传了data 它就会初始化 data  
} else { 
  observe(vm._data = {}, true /* asRootData */)
}

全局查找 initData

function initData (vm: Component) {
  let data = vm.$options.data  // 拿到 options 里的 data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      proxy(vm, `_data`, key)   // 对 vm 进行代理,用 _data 进行存储那个 key ,这段代码在源码147行
    }
  }
  // observe data
  observe(data, true /* asRootData */)  // 监听原始对象
}

去看看代理咋写的

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)  // 最核心的一行,它会对vm 进行代理
}

以上代码使用了 get set 然后把它传给 Object.defineProperty,这个 key 有 get set 属性
通过代理可以访问到原始的data
vm 当了 _data 这个代理之后,调用 observe(data)

第六步

全局查找 observe 看它从哪里来的

截屏2021-12-20 上午12.42.14.png

进入 ../observer/index.js 文件夹瞧瞧

找observe 的函数

截屏2021-12-20 上午12.47.21.png 以上代码的 value 表示, 它会接收一个值(这个值就是那个 data)
在 124 行,来看看这个观察者做了啥

截屏2021-12-20 上午12.50.19.png 如果是个数组就观察,如果不是就 walk
来看看这个 walk 做了啥

截屏2021-12-20 上午12.52.01.png 它对这个对象的每一个属性,都会 defineReactive
在看看这个 defineReactive 是啥

截屏2021-12-20 上午12.54.08.png 这个函数会得到一个对象和对应的 key,这里的 val 是最开始的那个值 ,这个 key 做啥?

截屏2021-12-20 上午12.55.22.png 它对原来的属性进行了一个覆盖,对它进行 get 操作它就会先去读它的 val(161行)

截屏2021-12-20 上午12.58.19.png 当我对右边这个 data 进行写的时候,会去尝试读(174行),写的时候又回到 val (188行)了

总结

不要随便的去看源码,得知道原理才可以去看源码,连原理都不知道怎么通过源码学习呢?基础知识都不牢固,学***,所以通过源码学习得要有个前提,那就是你是高手, 我也看不懂= =,小菜鸟一个,本文只是简单记录一哈,看源码学习不可取,先把基础打牢,有些东西不需要看源码也能知道大概的是怎么写的。\

Object.defineProperty 不了解的可以看看这篇文章