vue2.7.16源码 - 构造函数增强,增加静态属性

87 阅读4分钟

src/core/index.ts - 往vue的构造函数挂载静态属性

src/core/global-api/index.ts - initGlobalAPI函数执行,注入静态方法和属性

import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'

import {
  warn,
  extend,
  nextTick,
  mergeOptions,
  defineReactive
} from '../util/index'
import type { GlobalAPI } from 'types/global-api'

export function initGlobalAPI(Vue: GlobalAPI) {
  // 拦截config的set属性  不需要修改
  const configDef: Record<string, any> = {}
  configDef.get = () => config
  if (__DEV__) {
    configDef.set = () => {
      warn(
        'Do not replace the Vue.config object, set individual fields instead.'
      )
    }
  }
  Object.defineProperty(Vue, 'config', configDef)

// 插入utils属性
// 新增几个方法
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

// 新增set delete属性
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  // 2.6 新增监控的api 数据发生改变后触发回调 
  // 可以独立于vue文件的属性 
  Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }

  // 新增options属性
  Vue.options = Object.create(null)
  
  // ASSET_TYPES = ['component', 'directive', 'filter']
  // 在options上新增对应的属性名,用于收集全局的组件 自定义指令 过滤器
  ASSET_TYPES.forEach(type => {
    Vue.options[type + 's'] = Object.create(null)
  })

  // weex使用
  Vue.options._base = Vue

  // 扩展components属性 预置keepAlive 全局组件
  extend(Vue.options.components, builtInComponents)

  // 新增Vue.use 方法
  initUse(Vue)
  // 新增Vue.min方法
  initMixin(Vue)
  //  Vue.extend 
  // 模板转组件使用
  initExtend(Vue)
  // 在vue上新增对应的创建方法
  // Vue.components  Vue.directives Vue.filters
  initAssetRegisters(Vue)
}

src/core/util/next-tick.ts - nextTick具体实现

  • 先判断promise
  • 然后判断mutationobserver
  • 然后判断setImmediate
  • 最后使用 settimeout
/* globals MutationObserver */

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'

export let isUsingMicroTask = false

const callbacks: Array<Function> = []
let pending = false

function flushCallbacks() {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}

//这里我们使用微任务异步延迟包装器。
//在2.5版本中,我们使用(宏)任务(与微任务结合)。
//但是,在重新粉刷之前改变状态会有一些微妙的问题
//另外,在事件处理程序中使用(宏)任务会导致一些奇怪的行为无法绕过的
//我们现在到处都在使用微任务。
//这种权衡的一个主要缺点是在某些情况下
//微任务的优先级太高,并且介于两者之间
//顺序事件
//甚至在同一个事件的冒泡之间(#6566)。
let timerFunc
// nextTick行为利用微任务队列,它可以被访问
//通过本地Promise。then或MutationObserver。
// MutationObserver有更广泛的支持,但是它存在严重的bug
//当在触摸事件处理程序中触发时,UIWebView在iOS >= 9.3.3。它
//触发几次后完全停止工作…如果是本地的
// Promise可用,我们将使用它:
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
 //在有问题的UIWebViews中,Promise。然后不会完全破裂,但是
//它可能会被卡在一个奇怪的状态,回调被推入
//微任务队列,但队列不被刷新,直到浏览器
//需要做一些其他的工作,例如处理一个计时器。因此我们可以
//通过添加空计时器“强制”刷新微任务队列。
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
} else if (
  !isIE &&
  typeof MutationObserver !== 'undefined' &&
  (isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]')
) {
//在原生Promise不可用的地方使用MutationObserver;
//例如PhantomJS, iOS7, Android 4.4
// (#6466 MutationObserver在IE11中不可靠)
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  const textNode = document.createTextNode(String(counter))
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
 //回退到seimmediate。
//技术上它利用(宏)任务队列,
//但它仍然是比setTimeout更好的选择。
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
} else {
  // Fallback to setTimeout.
  timerFunc = () => {
    setTimeout(flushCallbacks, 0)
  }
}

export function nextTick(): Promise<void>
export function nextTick<T>(this: T, cb: (this: T, ...args: any[]) => any): void
export function nextTick<T>(cb: (this: T, ...args: any[]) => any, ctx: T): void
/**
 * @internal
 */
export function nextTick(cb?: (...args: any[]) => any, ctx?: object) {
  let _resolve
  callbacks.push(() => {
    if (cb) {
      try {
        cb.call(ctx)
      } catch (e: any) {
        handleError(e, ctx, 'nextTick')
      }
    } else if (_resolve) {
      _resolve(ctx)
    }
  })
  if (!pending) {
    pending = true
    timerFunc()
  }
  // $flow-disable-line
  if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

src/core/components/keep-alive.ts - keepAlive实现

  • 缓存组件的vnode
import { isRegExp, isArray, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'
import type VNode from 'core/vdom/vnode'
import type { VNodeComponentOptions } from 'types/vnode'
import type { Component } from 'types/component'
import { getComponentName } from '../vdom/create-component'

type CacheEntry = {
  name?: string
  tag?: string
  componentInstance?: Component
}

// 缓存池(键为组件标识,值为组件实例的 vnode)
type CacheEntryMap = Record<string, CacheEntry | null>

// 获取组件配置名
function _getComponentName(opts?: VNodeComponentOptions): string | null {
  return opts && (getComponentName(opts.Ctor.options as any) || opts.tag)
}

// 判断是否匹配
function matches(
  pattern: string | RegExp | Array<string>,
  name: string
): boolean {
  if (isArray(pattern)) {
    return pattern.indexOf(name) > -1
  } else if (typeof pattern === 'string') {
    return pattern.split(',').indexOf(name) > -1
  } else if (isRegExp(pattern)) {
    return pattern.test(name)
  }
  /* istanbul ignore next */
  return false
}

// 删除缓存
function pruneCache(
  keepAliveInstance: {
    cache: CacheEntryMap
    keys: string[]
    _vnode: VNode
    $vnode: VNode
  },
  filter: Function
) {
  const { cache, keys, _vnode, $vnode } = keepAliveInstance
  for (const key in cache) {
    const entry = cache[key]
    if (entry) {
      const name = entry.name
      if (name && !filter(name)) {
        pruneCacheEntry(cache, key, keys, _vnode)
      }
    }
  }
  $vnode.componentOptions!.children = undefined
}

// 当缓存数超过 max 时,删除最久未被访问的组件
function pruneCacheEntry(
  cache: CacheEntryMap,
  key: string,
  keys: Array<string>,
  current?: VNode
) {
  const entry = cache[key]
  if (entry && (!current || entry.tag !== current.tag)) {
    // 调用组件的destroy
    entry.componentInstance.$destroy()
  }
  cache[key] = null
  remove(keys, key)
}

const patternTypes: Array<Function> = [String, RegExp, Array]

// TODO defineComponent
export default {
  name: 'keep-alive',
  abstract: true,
  props: {
    include: patternTypes,
    exclude: patternTypes,
    max: [String, Number]
  },
  methods: {
    cacheVNode() {
    // vnodeToCache 需要缓存的子组件 VNode
    // keyToCache 生成唯一标识缓存子组件的键
      const { cache, keys, vnodeToCache, keyToCache } = this
      if (vnodeToCache) {
        const { tag, componentInstance, componentOptions } = vnodeToCache
        cache[keyToCache] = {
          name: _getComponentName(componentOptions),
          tag,
          componentInstance
        }
        keys.push(keyToCache)
        // 超过max删掉缓存池的内容
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
        this.vnodeToCache = null
      }
    }
  },
  created() {
    this.cache = Object.create(null)
    this.keys = []
  },
 // 组件销毁时清空缓存
  destroyed() {
    for (const key in this.cache) {
      pruneCacheEntry(this.cache, key, this.keys)
    }
  },
  // 初始化时更新一次缓存
  mounted() {
    this.cacheVNode()
    // 每次props变化更新缓存池
    this.$watch('include', val => {
      pruneCache(this, name => matches(val, name))
    })
    this.$watch('exclude', val => {
      pruneCache(this, name => !matches(val, name))
    })
  },
  // 在每次更新时更新缓存
  updated() {
    this.cacheVNode()
  },
  render() {
    // 获取插槽中的子组件 VNode
    const slot = this.$slots.default
    const vnode = getFirstComponentChild(slot)
     // 提取子组件的组件选项(用于匹配 include/exclude)
    const componentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      const name = _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 =
        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
        // 更新key
        remove(keys, key)
        keys.push(key)
      } else {
        // 延迟设置缓存直到更新
        this.vnodeToCache = vnode
        this.keyToCache = key
      }

      // 标记为 keep-alive 组件
      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}