Vue源码学习1——Vue构造函数

这是我第一次正式阅读大型框架源码,刚开始的时候完全不知道该如何入手。Vue源码clone下来之后这么多文件夹,Vue的这么多方法和概念都在哪,完全没有头绪。现在也只是很粗略的了解一下,个人认为这篇只是能做到大家阅读Vue的参考导航,可以较快的找到需要看的文件或方法。很多细节依然没有理解到位,但是可以慢慢来,先分享一波~

源码文件目录结构

- benchmarks 暂时不知道是什么

- dist 存放打包后的文件夹

- examples 示例,这个地方可以自己写一些简单例子,然后通过调试看整个代码运行的过程来了解源码是怎么写的

- flow 静态类型检查,比如 (n:number)即n需要是number类型

- packages 查资料说是vue还可以分别生成其他的npm包

- scripts 打包相关的配置文件夹

- src 我们研究的主要文件夹,下面会详细再说明

- test 测试文件夹

- types 暂时不知道是什么

/src

接下来重点说src这个文件夹,这里面需要重点看core这个文件夹,这里面才是我们真正需要研究的地方如下图:

- components 组件,现在里面只有KeepAlive一个

- global-api 全局api,可以给Vue添加全局方法,比如里面我们常使用的Vue.use()

-instance 核心文件夹,里面是实例相关的一些方法,例如初始化实例、实例事件绑定、渲染、状态、生命周期等

- observe 双向数据绑定相关文件(暂时不太清楚)

- util 工具方法,看到里面有props、nextTick之类的方法(暂时不太清楚)

- vdom 虚拟dom

大体文件结构说了一下,但是很多还不是很清晰。对于我这样的小白来说,我的建议是可以从 npm run dev 开始一步步开始看,采取的方法是“倒序”

package.json

打开这个文件找到’dev’命令,如下

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

‘rollup’ 是Vue使用的打包工具,从上面可以看出执行这个命令是到 ‘scripts/config.js’ 那就打开这个文件

scripts/config.js

if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}

genConfig 方法是设置一些配置,和webpack里的设置差不多,然后找到 web-full-dev

  '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
  }

可以看到入口文件是’web/entry-runtime-with-compiler.js’

src/platforms/web/entry-runtime-with-compiler.js

在这个文件里可以看到

import Vue from './runtime/index'

该方法中有一个$mount需要注意,这个就是渲染的入口,接下来会说这个方法

src/platforms/web/runtime/index.js

`import Vue from 'core/index'`

src/core/index.js

import Vue from './instance/index' ## src/core/instance/index.js ## 终于进入到最重要的方法,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

src/core/instance/init.js

第二个参数就是我们自己设置的option,比如data、el;第三个参数如下:

接下来将会一个个初始化方法说明,初次之外_init方法还有一些变量的初始化,比如_uid、_isVue、_name、_renderProxy的初始化

src/core/instance/state.js

src/core/instance/events.js

src/core/instance/lifecycle.js

src/core/instance/render.js

src/core/instance/inject.js

在_init方法中调用了initProvide、initInjections两个方法,这两个方法在实际应用中不是很多,查看Vue API说provide 和 inject 主要为高阶插件/组件库提供用例。并不推荐直接用于应用程序代码中。所以这里不做说明,有需要的可以到这个文件查看相关方法

vue的渲染过程

其中可以看到,通过el或者template的方式都需要调用compileToFunctions将字符串转换成方法,而render是不需要,这里可以看出render的性能应该会好一些,但是el和template我们使用较易理解。但是不管是哪一种最后都是生成render方法,然后再绑定到实例对象上。另外方法中的mount是从runtime/index.js中创建的。

然后调用前一步调用的_render方法是在render.js中的_render方法中try……catch地方调用了第二步中生成的render方法。通过_render方法生成vnode,传入_update方法

以上就是一个大体渲染的过程。

总结

本文只是做了Vue构造函数整体的一个流程展示,哪些参数是在哪个文件中挂载上去的以及vue渲染的一个简单流程。但其实每个环节都可以拓展出很多知识,比如响应式的数据绑定、虚拟DOM、diff算法、patch、生命周期等等,这些可以在之后再一个个点进行了解。下图是没有参数的vue实例的参数