汪图南
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • RAG

    • RAG
  • 快速入门
  • 高级技巧
前端面试之道
  • 打包工具

    • Webpack
    • Rollup
  • TypeScript

    • TypeScript基础
    • TypeScript类型挑战
  • CSS预编译器

    • SASS
  • 自动化测试

    • Vue应用测试
  • Vue2.0源码分析
  • Vue3.0源码分析
  • 数据结构和算法(基础)
  • LeetCode(刷题)
  • JavaScript书籍

    • 你不知道的JavaScript(上)
    • 你不知道的JavaScript(中下)
    • JavaScript数据结构和算法
    • JavaScript设计模式与开发实践
    • 深入理解ES6
  • Git书籍

    • 精通Git
Github
  • 介绍

    • 介绍和参考
  • 源码目录设计和架构设计

    • 设计
  • Rollup构建版本

    • Rollup基础知识
    • Vue中的Rollup构建
  • 从入口到构造函数整体流程

    • 整体流程
    • initGlobalAPI流程
    • initMixin流程
    • stateMixin流程
    • eventsMixin流程
    • lifecycleMixin流程
    • renderMixin流程
  • 响应式原理

    • 介绍
    • 前置核心概念
    • props处理
    • methods处理
    • data处理
    • computed处理
    • watch处理
    • 深入响应式原理
    • 依赖收集
    • 派发更新
    • nextTick实现原理
    • 变化侦测注意事项
    • 变化侦测API实现
  • 虚拟DOM和VNode

    • 虚拟DOM
    • VNode介绍
    • Diff算法
  • 组件化

    • 介绍
    • $mount方法
    • render和renderProxy
    • createElement
    • createComponent
    • 合并策略
    • update和patch
    • 组件生命周期
    • 组件注册
  • 编译原理

    • 介绍
    • compileToFunctions
    • parse模板解析
    • optimize优化
    • codegen代码生成
  • 扩展

    • 扩展
    • directive指令
    • filter过滤器
    • event事件处理
    • v-model
    • 插槽
    • Keep-Alive
    • Transition
    • Transition-Group
    • Vue.use插件机制
  • Vue-Router

    • 介绍
    • 路由安装
    • matcher介绍
    • 路由切换
    • 内置组件
    • 路由hooks钩子函数
  • Vuex

    • 介绍
    • Vuex安装
    • Vuex初始化
    • Vuex辅助API
    • Store实例API

initMixin流程

在上一节我们讲到了initGlobalAPI的整体流程,这一节,我们来介绍initMixin的整体流程。首先,我们把目光回到src/core/index.js文件中:

import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
initGlobalAPI(Vue)

export default Vue

我们发现,它从别的模块中引入了大Vue,那么接下来我们的首要任务就是揭开Vue构造函数的神秘面纱。

在看src/core/instance/index.js代码之前,我们发现instance目录结构如下:

|-- instance
|   |-- render-helpers      # render渲染相关的工具函数目录
|   |-- events.js           # 事件处理相关
|   |-- init.js             # _init等方法相关
|   |-- inject.js           # inject和provide相关
|   |-- lifecycle.js        # 生命周期相关
|   |-- proxy.js            # 代理相关
|   |-- render.js           # 渲染相关
|   |-- state.js            # 数据状态相关
|   |-- index.js            # 入口文件

可以看到,目录结构文件有很多,而且包含的面也非常杂,但我们现在只需要对我们最关心的几个部分做介绍:

  • events.js:处理事件相关,例如:$on,$off,$emit以及$once等方法的实现。
  • init.js:此部分代码逻辑包含了Vue从创建实例到实例挂载阶段的所有主要逻辑。
  • lifecycle.js:生命周期相关,例如:$destroy、$activated和$deactivated。
  • state.js:数据状态相关,例如:data、props以及computed等。
  • render.js:渲染相关,其中最值得关注的是Vue.prototype._render渲染函数的定义。

在介绍了instance目录结构的及其各自的作用以后,我们再来看入口文件,其实入口文件这里才是Vue构造函数庐山真面目:

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构造函数其实就是一个普通的函数,我们只能通过new操作符进行访问,既new Vue()的形式,Vue函数内部也使用了instanceof操作符来判断实例的父类是否为Vue构造函数,不是的话则在开发环境下输出警告信息。
  • 除了声明Vue构造函数,这部分的代码也调用了几种mixin方法,其中每种mixin方法各司其职,处理不同的内容。

从以上代码中,我们能得到src/core/instance/index.js文件非常直观的代码逻辑流程图:

instance流程

接下来我们的首要任务是弄清楚_init()函数的代码逻辑以及initMixin的整体流程。我们从上面的代码发现,在构造函数内部会调用this._init()方法,如下:

// 实例化时,会调用this._init()方法。
new Vue({
  data: {
    msg: 'Hello, Vue.js'
  }
})

然后,我们在init.js中来看initMixin()方法是如何被定义的:

export function initMixin (Vue) {
  Vue.prototype._init = function (options) {
    // 省略代码
  }
}

我们可以发现,initMixin()方法的主要作用就是在Vue.prototype上定义一个_init()实例方法,接下来我们来看一下_init()函数的具体实现逻辑:

Vue.prototype._init = function (options) {
    const vm = this
    // 1. 合并配置
    if (options && options._isComponent) {
      initInternalComponent(vm, options)
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

    // 2.render代理
    if (process.env.NODE_ENV !== 'production') {
      initProxy(vm)
    } else {
      vm._renderProxy = vm
    }

    // 3.初始化生命周期、初始化事件中心、初始化inject,
    //   初始化state、初始化provide、调用生命周期
    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, 'beforeCreate')
    initInjections(vm)
    initState(vm)
    initProvide(vm)
    callHook(vm, 'created')

    // 4.挂载
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }

因为我们是要分析initMixin整体流程,对于其中某些方法的具体实现逻辑会在后续进行详细的说明,因此我们可以从以上代码得到initMixin的整体流程图。

最后更新时间: 2025/5/6 15:36
贡献者: wangtunan
Prev
initGlobalAPI流程
Next
stateMixin流程