【源码导读 】在new Vue()以前,Vue是什么样子的?

前言

初步读完vue(v2.6.10)的源码,现将读后感记录以下,故事就是由此开始。vue

小提示:配合源码食用更佳美味。node

混沌初开

开始的开始,vue就是一个简单的函数。web

src/core/instance/index.js设计模式

function Vue (options) {
  this._init(options)
}
复制代码

扩展功能

设计模式之混入模式,对Vue进行扩展,很值得学习的一种方式。api

initMixin()

定义了_init方法,是入口函数,在new Vue()时第一时间执行的方法。缓存

src/core/instance/init.jsbash

Vue.prototype._init = function (options?: Object){}
复制代码

stateMixin()

数据相关的扩展。dom

src/core/instance/state.js函数

$data & $props

Vue实例观察的数据对象和当前组件接收到的props对象。 其实是代理到_data_props工具

Object.defineProperty(Vue.prototype, '$data', {
      get(){
        return this._data
      }
  })
  Object.defineProperty(Vue.prototype, '$props', {
      get(){
        return this._props
      }
  })
复制代码

$set & $delete

Vue.setVue.delete 的别名

Vue.prototype.$set = set
  Vue.prototype.$delete = del
复制代码

$watch

实现了$watch方法,观察Vue实例变化的一个表达式或计算属性函数。

Vue.prototype.$watch = function () {}
复制代码

eventsMixin()

事件相关的扩展

src/core/instance/events.js

$on

监听当前实例上的自定义事件。

Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component{}
复制代码

$once

监听一个自定义事件,可是只触发一次。一旦触发以后,监听器就会被移除。

Vue.prototype.$once = function (event: string, fn: Function): Component{}
复制代码

$off

移除自定义事件监听器。

Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component{}
复制代码

$emit

触发当前实例上的事件。

Vue.prototype.$emit = function (event: string): Component {}
复制代码

lifecycleMixin()

生命周期相关的扩展

src/core/instance/lifecycle.js

_update

私有方法,更新dom节点流程的重要函数

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
复制代码

$forceUpdate

迫使 Vue 实例从新渲染。简单粗暴

Vue.prototype.$forceUpdate = function (){ // 触发更新}
复制代码

$destroy

彻底销毁一个实例。

Vue.prototype.$destroy = function (){ // 移除事件 指令等 }
复制代码

installRenderHelpers

render相关的扩展

src/core/instance/render.js

installRenderHelpers

定义render函数有关的方法,在执行render时使用

src/core/instance/render-helpers/index.js

export function installRenderHelpers (target: any) {
  target._o = markOnce
  target._n = toNumber
  target._s = toString
  target._l = renderList
  target._t = renderSlot
  target._q = looseEqual
  target._i = looseIndexOf
  target._m = renderStatic
  target._f = resolveFilter
  target._k = checkKeyCodes
  target._b = bindObjectProps
  target._v = createTextVNode
  target._e = createEmptyVNode
  target._u = resolveScopedSlots
  target._g = bindObjectListeners
  target._d = bindDynamicKeys
  target._p = prependModifier
}
复制代码

$nextTick

将回调延迟到下次 DOM 更新循环以后执行。跟Vue.nextTick同样,可是绑定了实例的this

Vue.prototype.$nextTick = function (fn: Function) {
  return nextTick(fn, this)
}
复制代码

_render

私有方法,建立vnode流程的重要函数

Vue.prototype._render = function (): VNode{}
复制代码

核心代码

src/core/index.js

initGlobalAPI()

全局api

src/core/global-api/index.js

config

vue 全局配置 代理到vue的config,在new Vue()以前能够修改

全局配置 src/core/config.js

import config from '../config'
Object.defineProperty(Vue, 'config', {
    get() {
        return config
    }
})
复制代码

util

vue的工具函数

Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
复制代码

经常使用方法

Vue.set = set // 向响应式对象中添加一个属性
Vue.delete = del // 删除对象的属性。
Vue.nextTick = nextTick // 在下次 DOM 更新循环结束以后执行延迟回调。
复制代码

observable

让一个对象可响应。

Vue.observable = <T>(obj: T): T => {
  observe(obj)
  return obj
}
复制代码

options

默认的options

Vue.options = Object.create(null)
复制代码

组件、指令、过滤器的存放属性

组件、指令、过滤器实际上就是在options中建立了三个属性。

ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })
// Vue.options.components
// Vue.options.directives
// Vue.options.filters
复制代码

options._base

私有属性,缓存Vue

Vue.options._base = Vue
复制代码

内部组件

定义了内部组件KeepAlive

extend(Vue.options.components, builtInComponents)
复制代码

initUse(Vue)

定义了Vue.use方法, 安装 Vue.js 插件。

src/core/global-api/use.js

Vue.use = function (plugin: Function | Object) {}
复制代码

initMixin(Vue)

定义了Vue.mixin方法, 全局注册一个混入

src/core/global-api/mixin.js

Vue.mixin = function (mixin: Object) {}
复制代码

initExtend(Vue)

定义了Vue.extend方法, Vue 构造器,建立一个“子类”。

src/core/global-api/extend.js

Vue.extend = function (extendOptions: Object): Function
复制代码

initAssetRegisters(Vue)

用于实现组件、指令、过滤器方法

Vue.directive 注册或获取全局指令。

Vue.filter 注册或获取全局过滤器。

Vue.component 注册或获取全局组件。

src/core/global-api/assets.js

ASSET_TYPES.forEach(type => {
    Vue[type] = function (
        id: string,
        definition: Function | Object
    ): Function | Object | void {
        if (!definition) {
          return this.options[type + 's'][id]
        } else {
            if (type === 'component' && isPlainObject(definition)) {
                definition.name = definition.name || id
                definition = this.options._base.extend(definition)
            }
            if (type === 'directive' && typeof definition === 'function') {
                definition = { bind: definition, update: definition }
            }
            this.options[type + 's'][id] = definition
            return definition
        }
    }
})
复制代码

version

版本号

Vue.version = '__VERSION__'
复制代码

web运行时

再次之上是vue的核心代码,与平台无关。如下是web平台有关代码。 咱们一般用cli写的代码是不须要编译器的,由于vue-loader有一个编译过程,这个版本一般较小,可是不带编译器。

仅运行时的版本src/platforms/web/entry-runtime.js 实际上就是 src/platforms/web/runtime/index.js

定义配置

Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
复制代码

内部指令

平台相关 定义内部指令v-modelv-show

extend(Vue.options.directives, platformDirectives)
复制代码

内部组件

平台相关 定义内部组件TransitionTransitionGroup,上面提到过平台无关的内部组件KeepAlive

extend(Vue.options.components, platformComponents)
复制代码

patch

打补丁方法,平台不同打补丁的方法也不同,设计很巧妙。

Vue.prototype.__patch__ = inBrowser ? patch : noop
复制代码

$mount

若是 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可使用 vm.$mount()手动地挂载一个未挂载的实例。

最后执行了实际上是src/core/instance/lifecycle.js中的mountComponent方法。也就是对mountComponent的一次封装

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && inBrowser ? query(el) : undefined
  return mountComponent(this, el, hydrating)
}
复制代码

带编译的运行时

这个版本比较大,由于带有编译器。

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

$mount 二次封装

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  const options = this.$options
  
  // 其实所谓的编译就是讲模板编译成render函数
  
  // 存在render将直接使用render函数,则不须要编译
  // 不存在render则读取template template有害几种配置方法也是在此到处理

    if (template) {
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

    }

  // 实际仍是调用了$mount
  return mount.call(this, el, hydrating)
}
复制代码

compile

在 render 函数中编译模板字符串 编译器版本特有。

Vue.compile = compileToFunctions
复制代码

系列

结尾

感谢各位的阅读,错误是在所不免的,如有错误,或者有更好的理解,请在评论区留言,再次感谢。但愿你们相互学习,共同进步。