Skip to content

Latest commit

 

History

History
245 lines (201 loc) · 7.49 KB

vue源码解析.md

File metadata and controls

245 lines (201 loc) · 7.49 KB
title date tags
vue源码解析
2019-06-22 09:56:20 -0700

首先是入口文件: src/platforms/web/entry-runtime-with-compiler.js

做了哪些事情呢?

  1. src/platforms/web/runtime/index.js里导入Vue
  2. 获取Vue原型上的$mount方法,先存下来 const mount = Vue.prototype.$mount
  3. 重写Vue.prototype.$mount
    1. 获取el
    2. 判断this.$options里是否有render函数,如果有直接进入第5步;没有则进入第三步
    3. 获取templatetemplate可能来自this.$options,或者getOuterHTML(el)
    4. template通过compileToFunctions方法编译为渲染函数,得到render, staticRenderFns,并将他们挂在this.$options
    5. 返回mount.call(this, el, hydrating),这里mount就是事先存下的
  4. Vue上挂载compile方法 Vue.compile = compileToFunctions
  5. 导出Vue export default Vue

通常我们写代码时,会在main.js里,这样写:

new Vue({
  // 这里是我们写的配置,对应的是this.$options
}).$mount('#app')

这里的$mount就是上面重写后的Vue.prototype.$mount,所以执行$mount时实际执行的是mount.call(this, el, hydrating),也就是重写前的Vue.prototype.$mount。重写前的Vue.prototype.$mount来自src/platforms/web/runtime/index.js。我们去看一下这个文件里做了哪些事情。

src/platforms/web/runtime/index.js

做了哪些事情呢?

  1. src/core/index里导入Vue
  2. 安装平台指定的utilsVue.config
    Vue.config.mustUseProp = mustUseProp
    Vue.config.isReservedTag = isReservedTag
    Vue.config.isReservedAttr = isReservedAttr
    Vue.config.getTagNamespace = getTagNamespace
    Vue.config.isUnknownElement = isUnknownElement
  3. 安装平台指定的directivescomponents
     extend(Vue.options.directives, platformDirectives)
     extend(Vue.options.components, platformComponents)
    platformDirectives包括v-model, v-show platformComponents包括transitiontransition-group
  4. 安装平台指定的patch函数
    Vue.prototype.__patch__ = inBrowser ? patch : noop
  5. 安装$mount方法到原型
     Vue.prototype.$mount = function (
       el?: string | Element,
       hydrating?: boolean
     ): Component {
       el = el && inBrowser ? query(el) : undefined
       return mountComponent(this, el, hydrating)
     }
  6. 导出Vue export default Vue

可以看到这个文件里定义了我们执行的$mount,而返回的是mountComponent(this, el, hydrating),这个mountComponent方法来自src/core/instance/lifecycle

src/core/instance/lifecycle

mountComponent函数做了哪些事情呢?

  1. 设置vm.$el: vm.$el = el
  2. 执行beforeMount钩子: callHook(vm, 'beforeMount')
  3. 实例化一个watcher
     new Watcher(vm, updateComponent, noop, {
       before () {
         if (vm._isMounted && !vm._isDestroyed) {
           callHook(vm, 'beforeUpdate')
         }
       }
     }, true
  4. 执行mounted钩子
     if (vm.$vnode == null) {
       vm._isMounted = true
       callHook(vm, 'mounted')
     }
  5. 返回return vm

可以看到这里就完成了整个组件的挂载,并且在这里调用了new Watcher,如果有更新,就执行beforeUpdate钩子。

这个new Watcher我们稍后再说。

总结:上面这些是顺着.$mount('#app')讲的,说明了执行.$mount('#app')过程中做了什么事情。那么new Vue({// 这里是我们写的配置,对应的是this.$options})这个实例化Vue的过程做了什么呢?请接着阅读下文

Vue的实例化过程

第一部分入口文件第二部分分析可知,最终我们在main.js里使用的Vue来自src/core/index.js,继续追踪src/core/index.js,发现Vue来自src/core/instance/index.js

src/core/instance/index.js

这个文件,定义了Vue函数,然后下面执行的一系列函数,就是在Vue的原型上添加属性。

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)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue

这一系列函数就是关键了。

initMixin

来自src/core/instance/init.js,它在Vue原型上设置了_init方法(只是定义,没有执行),来看一下这个_init方法:

  1. 设置vm等于this
  2. 设置vm.$options
  3. 设置vm._renderProxy
  4. 设置vm._self = vm
  5. 初始化生命周期之类的:
     initLifecycle(vm)
     initEvents(vm)
     initRender(vm)
     callHook(vm, 'beforeCreate')
     initInjections(vm) // resolve injections before data/props
     initState(vm)
     initProvide(vm) // resolve provide after data/props
     callHook(vm, 'created')
6. 如果有el,则执行$mount。

### `initLifecycle`
```javascript
vm.$parent = parent
vm.$root = parent ? parent.$root : vm

vm.$children = []
vm.$refs = {}

vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false

initEvents

  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }

initRender

  vm._vnode = null // the root of the child tree
  vm._staticTrees = null
  vm.$slots
  vm.$scopedSlots
  vm._c
  vm.$createElement
  defineReactive(vm, '$attrs',...
  defineReactive(vm, '$listeners',...

initInjections

解析inject,关闭响应式

initState

设置vm._watchers = []

  1. initProps 定义vm._props 校验props 关闭响应式,然后defineReactive(props, key, value,... proxy(vm, _props, key)
  2. initMethods: vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
  3. initData 定义vm._data proxy(vm, '_data', key) observe(data, true)
  4. initComputed 定义vm._computedWatchers 对每个key new一个watcher
  5. initWatch createWatcher

initProvide

设置vm._provided为vm.$options.provide

stateMixin

来自src/core/instance/state.js

  1. 定义Vue.prototype.$data,其get方法是返回this._data
  2. 定义Vue.prototype.$props,其get方法是返回this._props
  3. 定义Vue.prototype.$set = set, 这个set来自src/core/observer/index
  4. 定义Vue.prototype.$delete = del, 这个del来自src/core/observer/index
  5. 定义Vue.prototype.$watch

eventsMixin

来自src/core/instance/event.js

  1. 定义Vue.prototype.$onVue.prototype.$onceVue.prototype.$offVue.prototype.$emit

lifecycleMixin

来自src/core/instance/lifecycle.js

  1. 定义Vue.prototype._updateVue.prototype.$forceUpdateVue.prototype.$destroy

renderMixin

来自src/core/instance/render.js

  1. 执行installRenderHelpers,略
  2. 定义Vue.prototype.$nextTick
  3. 定义Vue.prototype._render

new Watcher

还记得我们上面分析.$mount执行过程时,留着一个new Watcher没说呢。Watcher是在src/core/observer/watcher.js里定义的。下面就开始分析: