美文网首页vue源码学习
vue源码学习 - 入口篇之globalAPI

vue源码学习 - 入口篇之globalAPI

作者: halapro_liu | 来源:发表于2018-07-24 00:26 被阅读211次

    vue源码学习 - 入口篇

    // 这里是vue对象的实例化部分
    import Vue from './instance/index'
    // 这是初始化Vue的全局api
    import { initGlobalAPI } from './global-api/index'
    // 判断是否是服务端渲染
    import { isServerRendering } from 'core/util/env'
    import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
    
    // 调用初始化全局api
    initGlobalAPI(Vue)
    
    // 在Vue对象上定义$isServer参数,判断是否是服务端渲染
    Object.defineProperty(Vue.prototype, '$isServer', {
      get: isServerRendering
    })
    
    Object.defineProperty(Vue.prototype, '$ssrContext', {
      get () {
        /* istanbul ignore next */
        return this.$vnode && this.$vnode.ssrContext
      }
    })
    
    // expose FunctionalRenderContext for ssr runtime helper installation
    Object.defineProperty(Vue, 'FunctionalRenderContext', {
      value: FunctionalRenderContext
    })
    
    // Vue的版本号
    Vue.version = '__VERSION__'
    
    export default Vue
    
    

    从上述代码中看到,initGlobalAPI(Vue)这句代码就是我们的核心入口了。

      // 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)
    
    

    代码中首先先定义了一些基本的配置信息,同时不允许后续的修改配置的操作。

    // 暴露一些方法到全局的util下
    Vue.util = {
        warn,
        extend,
        mergeOptions,
        defineReactive
    }
    
    // 暴露set方法到全局
    Vue.set = set
    // 暴露set方法到全局
    Vue.delete = del
    // 暴露set方法到全局
    Vue.nextTick = nextTick
    Vue.options = Object.create(null)
    // 定义主要自定义的组件,包括components,directives,filters
    ASSET_TYPES.forEach(type => {
        Vue.options[type + 's'] = Object.create(null)
    })
    

    上述代码中,extend主要是一个浅拷贝的方法,set方法则是vue的一个关键方法,主要作用就是为对象属性添加动态监控。set方法的核心就是调用了defineReactive方法,具体的实现后续再进行展开。delete方法主要是用来删除响应的监听属性。

    nextTick模块实现

    const callbacks = []
    let pending = false
    let useMacroTask = false
    export function nextTick (cb?: Function, ctx?: Object) {
      let _resolve
      callbacks.push(() => {
        if (cb) {
          try {
            cb.call(ctx)
          } catch (e) {
            handleError(e, ctx, 'nextTick')
          }
        } else if (_resolve) {
          _resolve(ctx)
        }
      })
      if (!pending) {
        pending = true
        if (useMacroTask) {
          macroTimerFunc()
        } else {
          microTimerFunc()
        }
      }
      // $flow-disable-line
      if (!cb && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
          _resolve = resolve
        })
      }
    }
    

    nextTick方法通过callbacks模拟一个事件队列,将新传入的回调和上下文push进callbacks中。接着通过pending和useMacroTask判断使用macroTimerFunc或者
    microTimerFunc,默认使用的microTimerFunc。

    microTimerFunc.jpg
    if (typeof Promise !== 'undefined' && isNative(Promise)) {
      const p = Promise.resolve()
      microTimerFunc = () => {
        p.then(flushCallbacks)
        if (isIOS) setTimeout(noop)
      }
    } else {
      // fallback to macro
      microTimerFunc = macroTimerFunc
    }
    

    从上述代码可知,microTimerFunc默认是使用Promise,若是Promise不存在则使用macroTimerFunc。

    在ios下,会出现microTask队列无法被释放,直到浏览器要做其他动作的时候才会释放,因此添加一个空的定时器,强制清空microTask队列。

    macroTimerFunc.jpg
    if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
      macroTimerFunc = () => {
        setImmediate(flushCallbacks)
      }
    } else if (typeof MessageChannel !== 'undefined' && (
      isNative(MessageChannel) ||
      MessageChannel.toString() === '[object MessageChannelConstructor]'
    )) {
      const channel = new MessageChannel()
      const port = channel.port2
      channel.port1.onmessage = flushCallbacks
      macroTimerFunc = () => {
        port.postMessage(1)
      }
    } else {
      macroTimerFunc = () => {
        setTimeout(flushCallbacks, 0)
      }
    }
    

    从上述代码可知,macroTimerFunc默认使用setImmediate构造,若是不存在则依次使用MessageChannel——>setTimeout.

    相信大家对MessageChannel有些陌生,第一次见到也有些摸不着头脑,于是查阅了MDN文档,以下为对应的描述:

    The Channel Messaging API allows two separate scripts running in different browsing contexts attached to the same document (e.g., two IFrames, or the main document and an IFrame, two documents via a SharedWorker, or two workers) to communicate directly, passing messages between one another through two-way channels (or pipes) with a port at each end.

    MessageChannel允许我们通过该接口提供的两个端口,在两个不同的脚本文件中共享相同的文档信息(包括两个IFrames,或者一个文档和IFrame,或者通过两个SharedWorker线程,或者是两个Web Worker线程中来进行直接通话。

    回到globalApi中:

      // 这句代码主要是对Vue对象做一个拷贝,主要用在weex中。
      Vue.options._base = Vue
      // 创建一个keep-alive组件的拷贝
      extend(Vue.options.components, builtInComponents)
      // 在Vue上挂载use方法,加载自定义插件
      initUse(Vue)
      // 在Vue上挂载mixin方法,加载mixin混合插件
      initMixin(Vue)
      // 在Vue上挂载extend方法
      initExtend(Vue)
      // 初始化components,directives,filters组件
      initAssetRegisters(Vue)
    

    相关文章

      网友评论

        本文标题:vue源码学习 - 入口篇之globalAPI

        本文链接:https://www.haomeiwen.com/subject/zekjmftx.html