入口开始,解读Vue源码(一)-- 造物创世

Why?

网上现有的Vue源码解析文章一搜一大批,可是为何我还要去作这样的事情呢?由于以为纸上得来终觉浅,绝知此事要躬行html

而后平时的项目也主要是Vue,在使用Vue的过程当中,也对其一些约定产生了一些疑问,可能官网上只会建议你这么作,可是核心实现咱们可能并不知道。好比:vue

  • v-for key 是如何达到“就地复用”策略
  • 数组更新检测是如何完成的
  • set 为何就能动态添加根级别的响应式属性
  • 为何Vue能够跨平台支持weex,以及后来出现的mpvue
  • ...

其次,好久没有更新内容了,以前对Vue源码也是有点研究,只不过没有很体系的记录,如今抽了点时间,作了一次基础的总结吧。一方面是由于想要克服本身的惰性,另外一方面也是想从新温故一遍。node

What?

一共分红了10个基础部分,后续还会继续记录。咱们能够先看一下概览:
git

而后咱们来看一下基础的目录:github

入口开始,解读Vue源码(一)———— 造物创世api

入口开始,解读Vue源码(二)—— new Vue 的故事数组

入口开始,解读Vue源码(三)—— initMixin 上篇浏览器

入口开始,解读Vue源码(三)—— initMixin 下篇weex

入口开始,解读Vue源码(四)—— 实现一个基础的 Vue 双向绑定函数

入口开始,解读Vue源码(五)—— $mount 内部实现

入口开始,解读Vue源码(六)—— $mount 内部实现 --- compile parse函数生成AST

入口开始,解读Vue源码(七)—— $mount 内部实现 --- compile optimize标记节点

入口开始,解读Vue源码(八)—— $mount 内部实现 --- compile generate 生成render函数

入口开始,解读Vue源码(九)—— $mount 内部实现 --- render函数 --> VNode

入口开始,解读Vue源码(十)—— $mount 内部实现 --- patch

开篇:入口开始,解读Vue源码(一)-- 造物创世

世间万物的起源来自于盘古的开天辟地,Vue 项目的起源,源于一次Vue的实例化:

new Vue({
  el: ...,
  data: ...,
  ....
})

那么在此次实例化的过程当中,究竟发生了哪些行为?让咱们来一探究竟。打开Vue的源码文件,其核心代码在src/core目录下。下面咱们从入口文件index.js开始进入:(刚开始看的时候,咱们可能不太清楚每一个引用方法的具体实现,不过不要紧,咱们能够本身根据他的命名来YY一下。)

// src/core/index.js

// 这里应该是咱们 Vue 核心方法
import Vue from './instance/index'
// 根据命名,应该能够猜出这里是初始化一些全局API
import { initGlobalAPI } from './global-api/index'
// 根据命名,这里应该是获取一个Boolean类型的变量,来判断是否是ssr
import { isServerRendering } from 'core/util/env'
// 这里开始执行初始化全局变量
initGlobalAPI(Vue)
// 为Vue原型定义属性$isServer
Object.defineProperty(Vue.prototype, '$isServer', {
  get: isServerRendering
})
// 为Vue原型定义属性$ssrContext
Object.defineProperty(Vue.prototype, '$ssrContext', {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

Vue.version = '__VERSION__'

export default Vue

下面咱们来一步步验证咱们的猜想,首先找到core/instance/index文件,能够清晰的看到:

import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'

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 Class,而后又调用了一系列init、mixin这样的方法来初始化一些功能,具体的咱们后面在分析,不过经过代码咱们能够确认的是:没错!这里确实是导出了一个 Vue 功能类。

接下来,咱们接着看initGlobalAPI这个东西,其实在Vue官网上,就已经为咱们说明了Vue的全局属性:

关于全局API

那咱们来看看,是否是这么回事(内容太多,只贴一下主要的代码):

// core/global-api/index

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

  // 这些工具方法不视做全局API的一部分,除非你已经意识到某些风险,不然不要去依赖他们
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }
  // 这里定义全局属性
  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

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

  // 定义全局方法
  initUse(Vue)
  initMixin(Vue)
  initExtend(Vue)
  initAssetRegisters(Vue)
}
  • 【Vue.config】 各类全局配置项

  • 【Vue.util】 各类工具函数,还有一些兼容性的标志位(哇,不用本身判断浏览器了,Vue已经判断好了)

  • 【Vue.set/delete】 这个你文档应该见过

  • 【Vue.nextTick】

  • 【Vue.options】 这个options和咱们上面用来构造实例的options不同。这个是Vue默认提供的资源(组件指令过滤器)。

  • 【Vue.use】 经过initUse方法定义

  • 【Vue.mixin】 经过initMixin方法定义

  • 【Vue.extend】经过initExtend方法定义

接下来即是提供给ssr使用的全局变量$isServer$ssrContext。 关于他们的使用,其实ssr文档也有说明:Head 管理

到这里,咱们的入口文件差很少就了解清楚了,接下来,咱们开始去了解一下 Vue class 的具体实现,其中咱们会了解到Vue的相关生命周期的知识。

End?

文章先后也是利用碎片时间总结整理而成,有些也是翻阅了不少的资料,也有过引用巨人的段落,文章中有所标注。若是没有标注,多是本人忘记了,欢迎提醒。文章中若是有笔误或者不正确的解释,也欢迎批评指正,共同进步。

最后:

github地址

部分源码demo

相关文章
相关标签/搜索