toBeTheLight.github.io 荒原

Vue源码 day01 由入口来看

2017-12-14
toBeTheLight

vue源码阅读,Vue构造函数的加工

一、运行调试命令

npm run dev
  • windows10下可能会遇到Could not load xxx\xxx/的情况,可使用npm i npm update rollup-plugin-alias@1.4.0升级的方式解决。
  • 打包后sourceMap只能对应到压缩前的代码,而不能对应值合并前的各个模块,此时可去\build\config.js,在genConfig 函数的 config变量加一个属性sourceMap: true
    const config = {
      input: opts.entry,
      // xxxxx
      sourceMap: true,
      // xxxxx
    }
    

    当前项目已做以上处理。 参考

二、找主文件

根据rollup配置的entry和import的引入顺序

  1. build/config.js ->
  2. src/platforms/web/entry-runtime-with-compiler.js ->
  3. src/platforms/web/runtime/index.js ->
  4. src/core/index.js ->
  5. src/core/instance/index.js

备注

  1. 是构建文件入口。
  2. 根据运行平台(和你的构建指令也有关系)不同进行不同配置的入口。
  3. 就要找到了,此文件内对Vue做了平台相关的配置。
  4. 找到了,在Vue上添加了version,添加了options{components, directive, filter})
  5. 我们先看这个,然后按照5、4、3、2、1的顺序看源代码中都对Vue做了哪些处理。

三、src/core/instance/index.js

注意到有一个构造函数和五个函数的调用。

// 构造函数
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')
  }
  // new 调用Vue构造函数后进行_init
  this._init(options)
}
// 5个函数的调用
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

可以看到的是构造函数中使用了this instanceof Vue做了this的判断,为非的情况下,给出警告。后面调用this._init(options)根据传入配置进行实例的构造。

5个函数的调用,从函数的命名上我们也能看出来分别是初始化相关、数据状态相关、事件相关、声明周期相关、渲染相关。 我们先一个个的看这5个函数

initMixin

 Vue.prototype._init = function (options?: Object) {}

内部对Vue的原型上添加了_init方法,就是上面构造函数最后调用的方法。

stateMixin

Object.defineProperty(Vue.prototype, '$data', dataDef)
Object.defineProperty(Vue.prototype, '$props', propsDef)
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch = function (){}

内部对Vue的原型上添加了四个属性$data$props$set$delete$watch,其中$data$props因为有只读方面的限制,所以使用Object.defineProperty的方式定义,其中各自的xxxDef对setget做了处理。

eventsMixin

Vue.prototype.$on = function () {}
Vue.prototype.$once = function () {}
Vue.prototype.$off = function () {}
Vue.prototype.$emit = function () {}

Vue原型上添加了四个事件相关的方法。

lifecycleMixin

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}

Vue原型上添加了_update$forceUpdate$destory方法,方法内部针对同名周期做了组件的更新和状态的记录。

renderMixin

// 渲染相关的功能函数
installRenderHelpers(Vue.prototype)
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}

此时的Vue

1. initMixin(Vue) --> src/core/instance/init.js
  *  Vue.prototype._init (day02)
2. stateMixin(Vue) --> src/core/instance/state.js
  * Vue.prototype.$data
  * Vue.prototype.$set = set
  * Vue.prototype.$delete = del
  * Vue.prototype.$watch
3. eventsMixin(Vue) --> src/core/instance/events.js
  * Vue.prototype.$on
  * Vue.prototype.$once
  * Vue.prototype.$off
  * Vue.prototype.$emit
4. lifecycleMixin(Vue) --> src/core/instance/lifecycle.js
  * Vue.prototype._update
  * Vue.prototype.$forceUpdate
  * Vue.prototype.$destroy
5. renderMixin(Vue)
  * Vue.prototype.$nextTick
  * Vue.prototype._render
  * Vue.prototype['一些辅助渲染相关的函数']

我们看下一个引用文件

四、src/core/index.js

initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    return this.$vnode && this.$vnode.ssrContext
  }
})
Vue.version = '__VERSION__'

先看initGlobalAPI(Vue)

Object.defineProperty(Vue, 'config', configDef)
Vue.util = {
  warn,
  extend,
  mergeOptions,
  defineReactive
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
/* 
 * options配置
 * {
 *   components,
 *   directives,
 *   filters
 * }
 */
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
  Vue.options[type + 's'] = Object.create(null)
})
Vue.options._base = Vue
/* 
 * builtInComponents 为 KeepAlive
 * 即为Vue配置全局keepAlive内置组件,应该还会在某个地方配Transtion
 * 其他内置的指令、过滤器以后应该也会配进options里
 */
extend(Vue.options.components, builtInComponents)
// Vue.use = function () {} 使用插件的方法
initUse(Vue)
// Vue.mixin = function () {}
initMixin(Vue)
/* 
 * Vue.cid = 0
 * Vue.extend = function () {} 应该是继承相关的
 */
initExtend(Vue)
/*
 * Vue.component = function () {}
 * Vue.directive = function () {}
 * Vue.filter = function () {}
 */
initAssetRegisters(Vue)

然后

Vue.version = '__VERSION__'
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

我们按照import顺序继续看

五、src/platforms/web/runtime/index.js

此文件做了这些事情

// 安装平台指定utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement

// 安装平台相关指令和组件
/*
 * platformDirectives中是model,show, 内置的指令,那if等是在哪里加上的呢?
 * 
 * platformComponents 中是Transition,TransitionGroup,前面提到的就是在这加* 上的
 */
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)

// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
Vue.prototype.$mount = function () {}

然后看最后一个文件

六、src/platforms/web/entry-runtime-with-compiler.js

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function () {
  // xxx 针对template或el的情况做一些处理
  return mount.call(this, el, hydrating)
}
// 在$mount 中有调用compileToFunctions,函数作用是template 编译为render函数
Vue.compile = compileToFunctions

结语

此章完,主要看了Vue的初始化的过程。

参考。


Content