(一)小菜鸡的Vue源码阅读——new Vue()干了件啥

574 阅读2分钟

本菜鸡阅读源码看的是Vue2.6,问我为啥不看3?那我至少得先看懂2吧,我觉得2还是很经典的,虽然现在Vue3的组合式API尤大很推崇,但是我还是习惯在Vue3里写声明式的,真的是个人习惯吧(我一定慢慢改,主要是我现在没项目做啊!),性能上小项目应该是没多大影响。阅读源码一个是为了以后面试可以吹逼,还有就是向优秀代码学习,理解他的组织架构,最后可以给自己使用的时候排坑。

0.import Vue

说到new Vue() 首先你得有Vue,先上demo

import Vue form 'Vue'
new Vue()

这里分为两个过程,一个是构造函数引入(或者说是类的引入),第二个才是真正的创建了一个vue的实例。光引入Vue这个构造函数,其实这里已经做了大量工作了,主要是给Vue加一些静态属性和原型链上的属性,如果用ES6的class表示大概是下面这个样子

export class Vue {
    
    constructor(options){
        this._init(options)
    }
    
    // 静态属性和方法
    static key
    static foo1(){}
    ...
    
    // 原型属性和方法
    public key2
    public foo2(){}
    
    ...
    ...
    _init(){}
}

对于ES5来说基本上是这样的

// 原型属性
Vue.prototype.key = 
// 静态属性
Vue.key2 =
// 构造函数
function Vue(options){
    this._init(options)
}

1.Vue类的创建

1.1 Vue的入口

platforms/web/entry-runtime.js

import Vue from './runtime/index'
export default Vue

1.2 运行时文件

platform/web/runtime/index.js

Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement

// 注册内置指令 v-model v-show
extend(Vue.options.directives, platformDirectives)
// 驻车内置组件 Transition,TransitionGroup
extend(Vue.options.components, platformComponents)

Vue.prototype.__patch__ = inBrowser ? patch : noop

Vue.prototype.$mount = function(){}

1.3 内核文件

core/index.js

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'

// Vue的静态属性和方法,是在类上的而不是在实例上的
initGlobalAPI(Vue)

// ssr相关,可先忽略
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__'

core/global-api/index.jsinitGlobalAPI挂载里一些Vue类上的静态属性和方法

export function initGlobalAPI (Vue: GlobalAPI) {

  Object.defineProperty(Vue, 'config', configDef)
  
  Vue.util = {
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick
  
  Vue.observable = <>(obj: T): T => {
    observe(obj)
    return obj
  }
  // ...
  //前面挂载里了一些Vue的静态方法
  
  // keep-alive组件
  extend(Vue.options.components, builtInComponents)
  // Vue.use
  initUse(Vue)
  // Vue.mixin
  initMixin(Vue)
  // Vue.extend
  initExtend(Vue)
  // Vue.components, Vue.directive, Vue.filter
  initAssetRegisters(Vue)
}

1.4 Vue构造函数定义(实例文件)

core/instance/index.js

function Vue(options){
    // 只能new不能直接执行
    // ...
    this._init(options)
}

// 给Vue的原型链上加点东西 Vue.prototype

/** 
 * 实例创建api(内部创建的时候使用)
 * _init
*/
initMixin(Vue)
/**
 * 数据相关的api
 * $data
 * $props
 * $set
 * $delete
 * $watch
 */
stateMixin(Vue)
/** 
 * 事件监听相关的api
 * $on
 * $once
 * $off
 * $emit
*/
eventsMixin(Vue)
/** 
 * 在Vue原型链上增加属性
 * _update
 * $forceUpdate
 * $destroy
*/
lifecycleMixin(Vue)
/**
 * 执行 installRenderHelpers,挂载一些渲染相关的工具函数
 * 渲染相关的api
 * $nextTick
 * _render
 */
renderMixin(Vue)

总结一下,在Vue类创建前已经有的属性

// -------静态属性和方法------------------
// 默认的options属性,和出入的参数合并
Vue.options = {
  components: {
    KeepAlive: {}
    Transition: {}
    TransitionGroup: {}
  },
  directives: {
    model: {inserted: ƒ, componentUpdated: ƒ}
    show: {bind: ƒ, update: ƒ, unbind: ƒ}
  },
  filters: {}
  _base
}

Vue.verison
Vue.FunctionalRenderContext

Vue.config.mustUseProp
Vue.config.isReservedTag 
Vue.config.isReservedAttr
Vue.config.getTagNamespace
Vue.config.isUnknownElement

Vue.use
Vue.mixin
Vue.extend
Vue.components
Vue.directive
Vue.filter

// -------实例属性和方法------------------
Vue.prototype.$isServer
Vue.prototype.$ssrContext

Vue.prototype.__patch__ 
Vue.prototype.$mount

// 数据相关
Vue.prototype.$data
Vue.prototype.props
Vue.prototype.$set
Vue.prototype.$delete
Vue.prototype.$watch

// 事件相关
Vue.prototype.$on
Vue.prototype.$once
Vue.prototype.$once
Vue.prototype.$emit

// 渲染相关
Vue.prototype._update
Vue.prototype.$forceUpdate
Vue.prototype.$destory

// 生命周期相关
// 挂载了一堆渲染工具函数
installRenderHelpers()
Vue.prototype.$nextTick
Vue.prototype._render


2.Vue实例的创建_init

 // vue 真正的构造函数
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this

    // 全局vue实例 🆔
    vm._uid = uid++
    
    // ...

    // a flag to avoid this being observed
    vm._isVue = true
    // merge options
    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else {
      // 自己开发的组件都会走到这一步,合并Vue之前已经生成好的options属性,再挂载到$options上
      vm.$options = mergeOptions(
        // 该函数会沿着继承链往上找到有options属性的地方
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

    // 开发环境下做了render函数时候的代理,优化性能应该是,生产环境的时候就是他自己
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }
    
    // expose real self
    vm._self = 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')

    //...

    // 挂载到真实的Dom  
    // 但是有的时候一般用链式调用 new Vue({}).$mount("#app")
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

接下来可能为陆续阅读实例初始化时候各个属性的具体操作,望大佬多多指正,未完待续。。。