阅读vue源码(1)

293 阅读2分钟

定义Vue

找到入口文件

首先在vue的源码中找到入口文件,package.json找到

"scripts": {
    ...
    "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
    ...
}

这里指出是在scripts/config.js中找到web-full-dev环境的路径

  // Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  }

可以看到入口文件是web/entry-runtime-with-compiler.js,然后找到alias.js文件

module.exports = {
  ...
  web: resolve('src/platforms/web'),
  ...
}

那么完整的路径就是src/platforms/web/entry-runtime-with-compiler.js

entry-runtime-with-compiler.js

这个文件大致做了三件事:

  • 定义mount,指向了原型上的$mount方法,会在下一个$mount中调用
  • 在Vue的原型上挂载了$mount方法,负责找到render函数,再调用上边的mount方法
  • 在Vue上挂载了compile,负责把用户编写的render或者template编译成render函数

这里只是Vue的原型上挂载了一些方法,还不是Vue构造方法的地方

import Vue from './runtime/index'

runtime/index.js

// patch就是将vnode渲染成dom,diff就在这里面
Vue.prototype.__patch__ = inBrowser ? patch : noop

// 这里就是上边的mount指向的方法
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}

从这个文件可以看到

import Vue from 'core/index'

这里的Vue又是从core文件引用的,initGlobalAPI又对Vue做了处理

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
...
initGlobalAPI(Vue)
...
export default Vue

global-api/index

export function initGlobalAPI (Vue: GlobalAPI) {
  ...
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
  // 这里的set、del和后面用到的$set$del是一样的
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 这里就是为什么调用observable方法处理数据,可以让数据响应式
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }
  ...
  // 定义plugin要用的api
  initUse(Vue)
  // merge操作
  initMixin(Vue)
  // 定义Vue.extend
  initExtend(Vue)
  // 给options添加了component、directive、filter
  initAssetRegisters(Vue)
}

use方法就是为plugin提供的api,Vue.use(plugin),就是调用的这里

export function initUse (Vue: GlobalAPI) {
  Vue.use = function (plugin: Function | Object) {
    ...
    // 这里的this指向当前的Vue实例,添加到数组的最前头
    const args = toArray(arguments, 1)
    args.unshift(this)
    // plugin可以是对象也可以是函数,在这里可以看到plugin接收到的第一个参数就是Vue的原型
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}

instance/index

这里定义了最初是的Vue构造方法,基本上就是在原型上挂载各种方法

function Vue (options) {
  ...
  this._init(options)
}
// 在原型上挂载_init,就是new Vue时调用的
initMixin(Vue)
// 拦截了$data$props,对其访问就是访问了this._data、this._props,又在原型上挂载了$watch
stateMixin(Vue)
// 在原型上挂载$on$emit等方法
eventsMixin(Vue)
// 在原型上挂载_update、$foceUpdate$destory
lifecycleMixin(Vue)
// 在原型上挂载$nextTick、_render
renderMixin(Vue)

以上就是在new Vue()之前要执行的方法 源码调试地址