vue源码分析(四)解密new Vue()之前做了哪些不为人知工

2020-03-19  本文已影响0人  vue爱好者

上一篇我们说过了vue实例化之前会调用 initGlobalAPI(Vue) 方法,那我们先看看一下定义initGlobalAPI 方法的路径 “src/core/global-api/index.js”。

打开文件可以看到上面导入了很多的对象,但基本都是定义函数的,没有涉及到执行,我们可以先不看,最主要的我们来看一下这个文件。

"import builtInComponents from '../components/index'"

代码如下:

import KeepAlive from './keep-alive'

export default {
  KeepAlive
}

然后我们再打开 “src/core/components/keep-alive.js” 代码如下:

/* @flow */

import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'

type VNodeCache = { [key: string]: ?VNode };

......为了不增大篇幅,省略部分代码(大家可自行查看)

export default {
  name: 'keep-alive',
  abstract: true,

  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },

  created () {
    this.cache = Object.create(null)
    this.keys = []
  },

  destroyed () {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },

  mounted () {
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },

  render () {
    const slot = this.$slots.default
    const vnode: VNode = getFirstComponentChild(slot)
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      const name: ?string = getComponentName(componentOptions)
      const { include, exclude } = this
      if (
        // not included
        (include && (!name || !matches(include, name))) ||
        // excluded
        (exclude && name && matches(exclude, name))
      ) {
        return vnode
      }

      const { cache, keys } = this
      const key: ?string = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        remove(keys, key)
        keys.push(key)
      } else {
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}

通过以上代码我们看到导出了一个对象 “keep-alive”,对于这个对象大家应该比较熟悉了,这是vue的一个内置组件,主要用来缓存路由的。这个我们后期再分享具体的代码,我们还是接着看 "initGlobalAPI(vue)"的具体代码:

export function initGlobalAPI (Vue: GlobalAPI) {
  // config
  const configDef = {}
  configDef.get = () => config
  if (process.env.NODE_ENV !== 'production') {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)

  // exposed util methods.
  // NOTE: these are not considered part of the public API - avoid relying on
  // them unless you are aware of the risk.
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 explicit observable API
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // this is used to identify the "base" constructor to extend all plain-object
  // components with in Weex's multi-instance scenarios.
  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)

  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}

可以看到对 “config”属性进行了一个拦截。

“Object.defineProperty(Vue, 'config', configDef)”

然后给vue注册了 “set”、“delete”、“nextTick”三个属性,和我们在第三讲说的在vue原型对象上面挂载了“ set”、“delete”是一样的,都是指向同一个函数,正如官方说的 “这是全局 Vue.set 的别名”。

Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick

紧接着就是给vue添加了一个观察者,然后给 Vue.options对象添加了三个属性 directives、filters、components。

注释:ASSET_TYPES = ['component','directive','filter']

然后就是让“components”属性继承了 builtInComponents。

接下来可以清楚的看到调用了如下4个函数:

initUse(Vue) // 在vue实例上挂载use方法(此方法用来加载插件)

initMixin(Vue) // 在vue实例上挂载mixin方法

initExtend(Vue) // 在vue实例上挂载extend方法

initAssetRegisters(Vue) // 在vue实例上挂载directives、filters、components

到这一步vue的准备工作基本 做完了,接下来我们看看new Vue()之后做了哪些不为人知的工作。

上一篇 下一篇

猜你喜欢

热点阅读