1. vue 目录结构 (src)

|-- dist  打包后的vue版本
|-- flow  类型检测,3.0换了typeScript    // 使用flow定义和检测类型,增强代码健壮性
|-- script  构建不同版本vue的相关配置
|-- src  源码
    |-- compiler  编译器    // 将template编译成render函数
    |-- core  不区分平台的核心代码
        |-- components  通用的抽象组件
        |-- global-api  全局API
        |-- instance  实例的构造函数和原型方法
        |-- observer  数据响应式   // vue检测数据变化并更新视图的实现
        |-- util  常用的工具方法
        |-- vdom  虚拟dom相关  // 将render函数转为vnode从而patch为真实dom以及diff算法的代码实现
    |-- platforms  不同平台不同实现
    |-- server  服务端渲染
    |-- sfc  .vue单文件组件解析
    |-- shared  全局通用工具方法
|-- test 测试

和运行时版本的关键所在,完整版就自带这个编译器,而运行时版本就没有,
(面试题) 请问 runtime compiler 和 runtime-only 这两个版本的区别?
文件大小的区别,带编译的版本要比不带的版本文件要大6kb左右
编译时机不同:编译运行时版本会在运行时进行编译,性能会有一定损耗;运行时版本借助于 vue-loader 做的离线编译,运行速度更快。

2. vue入口 首先是构造函数 core/instance/index.js

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):定义_init方法。
stateMixin(Vue):定义数据相关的方法$set,$delete,$watch方法。
eventsMixin(Vue):定义事件相关的方法$on,$once,$off,$emit。
lifecycleMixin(Vue):定义_update,及生命周期相关的$forceUpdate和$destroy。
renderMixin(Vue):定义$nextTick,_render将render函数转为vnode。

这些初始化后还会定义一些全局的API,initGlobalAPI(),里面包括

 initUse(Vue)   // Vue.use()
 initMixin(Vue)  // Vue.mixins()
 initExtend(Vue)  // Vue.extends()
 initAssetRegisters(Vue)  // Vue.component,Vue.directive,Vue.filter方法

this._init(options) 是 core/instance/init.js中的挂载到vue的proptype原型链上的。
此处的options是传进来的Vue实例,eg:

new Vue(
  {
    el: '#app',
    data: {
    message: 'hello world'
  },
    components: {}
  }
)
Vue.prototype._init = function (options?: Object) {
// 如果options里没有传components就开始解析构造器,传了就初始化这个组件
if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }}
    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')

所以在beforeCreate生命周期前vue 初始化了生命周期、事件处理机制
在created之前vue 初始化事件通信、options相关的
vue/src/core/instance/index.js stateMixin(Vue)
state.js

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  if (!isPlainObject(data)) {
    data = {}
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  while (i--) {
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)
}

这是初始化data函数的方法,其中将props和methods中的对象和函数与data中的比较,避免重复。这里只在data里传了一个message字段,走到until/lang.js     isReserved()

export function isReserved (str: string): boolean {
  const c = (str + '').charCodeAt(0)
  return c === 0x24 || c === 0x5F
}

0x24和0x5F是$和_对应的ASCII码值,isReserved函数是判断data里的字段是否带有$和_,为true会走到 proxy(vm, `_data`, key)这里,这里使用了代理模式

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

这里是Object.defineProperty的实现,set和get, 然后实现observe(data, true /* asRootData */)
../observer/index.js

xport function observe (value: any, asRootData: ?boolean): Observer | void {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}
 posted on 2020-12-10 13:11  Yseraaa  阅读(483)  评论(0编辑  收藏  举报