阅读 100

Vue2.5.16源码笔记

主线

Vue中数据状态改变后会采用virtual DOM的方式更新DOM,暂且不去深究virtual DOM内部具体实现,你只需要知道virtual DOM分为三个步骤:

  • createElement(): 用 JavaScript对象(虚拟树) 描述 真实DOM对象(真实树)
  • diff(oldNode, newNode) : 对比新旧两个虚拟树的区别,收集差异
  • patch() : 将差异应用到真实DOM树

有的时候 第二步 可能与 第三步 合并成一步(Vue 中的patch就是这样)。

Vue 的构造函数

使用 new 操作符来调用 Vue,那么也就是说 Vue 应该是一个构造函数。

运行 npm run dev 命令:

"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
复制代码

rollup配置文件使用了scripts/config.js文件,并设置环境变量 TARGET:web-full-dev,打开看一下关键代码

// builds 对象
const builds = {
  ...
  // Runtime+compiler development build (Browser)
  'web-full-dev': {
    entry: resolve('web/entry-runtime-with-compiler.js'),
    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: { he: './entity-decoder' },
    banner
  },
  ...
}
// 生成配置的方法
function genConfig (name) {
  const opts = builds[name]
  const config = {
    input: opts.entry,
    external: opts.external,
    ...
  }
  ...
  return config
}

if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
复制代码
  1. genConfig 函数返回一个 config 对象,从这个对象上面大概可以看出入口文件为src/platfroms/web/entry-runtime-with-compiler.js
import Vue from './runtime/index'
复制代码
  1. 继续到文件src/platfroms/web/runtime/index.js中找构造函数
import Vue from 'core/index'
复制代码
  1. 继续到文件src/core/index.js中找构造函数
import Vue from './instance/index'
复制代码
  1. 继续到文件src/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)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

export default Vue
复制代码

定义 Vue 构造函数,然后以Vue构造函数为参数,5个**Mixin(Vue)函数作用:在Vue 的原型 prototype 上挂载方法或属性,最后导出 Vue。

// initMixin(Vue)    src/core/instance/init.js *****************
Vue.prototype._init = function (options?: Object) {}

// stateMixin(Vue)    src/core/instance/state.js ****************
Vue.prototype.$data
Vue.prototype.$set = set
Vue.prototype.$delete = del
Vue.prototype.$watch = function(){}

// eventsMixin(Vue)    src/core/instance/events.js **************
Vue.prototype.$on = function (event: string, fn: Function): Component {}
Vue.prototype.$once = function (event: string, fn: Function): Component {}
Vue.prototype.$off = function (event?: string, fn?: Function): Component {}
Vue.prototype.$emit = function (event: string): Component {}

// lifecycleMixin(Vue)    src/core/instance/lifecycle.js ***************
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
Vue.prototype.$forceUpdate = function () {}
Vue.prototype.$destroy = function () {}

// renderMixin(Vue)    src/core/instance/render.js ************
Vue.prototype.$nextTick = function (fn: Function) {}
Vue.prototype._render = function (): VNode {}
复制代码

追溯路线往回走,那么下一个处理 Vue 构造函数的应该是src/core/index.js文件,

initGlobalAPI(Vue)

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.version = '__VERSION__'

export default Vue
复制代码

src/core/instance/index.js中导入已经在原型上挂载了方法和属性后的 Vue,然后导入 initGlobalAPI 和 isServerRendering,之后将Vue作为参数传给 initGlobalAPI ,最后又在 Vue.prototype 上挂载了 $isServer ,在 Vue 上挂载了 version 属性。

文章分类
阅读
文章标签