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) {
// process.env 返回一个包含用户环境信息的对象
// instanceof 用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,只能检测 new 出来的构造函数实例
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
终于找到了,最底层原来就是一个构造函数,我们在使用时用 new Vue 去实例它;其他的 **Mixin 方法都是给 Vue 的 prototype 上扩展方法,好吧,最底层的 Vue 函数已经找到了,再来看看前面说的全局 API initGlobalAPI;
// 没粘贴出所有的代码
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)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
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)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}
最后,找到了使用了如此久的 Vue 最底层的代码,虽然学习还需更进一步,但是已经开始了。。。go go go。。。
源码构建
Vue.js 的源码是通过 Rollup 构建,然而,在第一部分学习目录结构学习时有一个 scripts 文件夹,此文件夹下面存放的都是构建相关的配置文件。
构建命令
基于 NPM 托管的项目都会有一个 package.json 文件,在使用 node 的构建过程中都会调用 package.json 中的 script 配置项作为执行脚本,而 Vue.js 在构建的过程中调用的脚本文件如下3条配置:
在当前目录的命令行中运行:
npm run build
实际执行后面的 node 命令进行构建npm run build:ssr
实际执行后面的 node 命令进行服务端构建npm run build:weex
实际执行后面的 node 命令进行移动端平台的代码构建构建过程
npm run build
为例,直接看scripts/build.js
文件,从源码出发(对构建的关键代码添加了一些自己的注释):// filter builds via command line arg // process.argv 返回一个数组,获取命令行参数 // process.argv 第一个元素为 process.execPath(它返回启动node.js进程的可执行文件所在绝对路径) // process.argv 第二个元素为当前执行的 js 文件绝对路径 // process.argv 剩下的其他元素为命令行后跟的参数 if (process.argv[2]) { // 当前命令行存在参数 const filters = process.argv[2].split(',') builds = builds.filter(b => { // some 会遍历 builds 的所有数据,当表达式返回 true ,剩余的不会再执行;否则返回 false // 满足下面条件的 builds 数据才会组成新的数组 return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1) }) } else { // filter out weex builds by default // 当前命令行没有参数 // filter 返回一个新数组 builds = builds.filter((b, index) => { // config 文件中处理后的 builds 中是否是存在 weex(移动端) 路径 // 将路径中不存在 weex 的数据返回成一个新数组 return b.output.file.indexOf('weex') === -1 }) }
build(builds)
这里包含了基本构建、服务端渲染 webpack 插件及 weex 的打包配置,基于单个配置,它是遵循 Rollup 构建规则的,其中
entry
表示构建的入口文件地址,dest
表示构建后的 js 文件地址,format
表示构建的格式,而format
中的值有cjs(CommonJS 规范)
、es(ES Module 规范)
、umd(UMD 规范)
;web-runtime-cjs
配置项为例,对其中的参数进行分析,首先看entry
项,前面增加了我自己对resolve
函数的理解注释,这里看一下resolve
的代码:它首先将传入的参数以
/
进行了分隔并取出第一个值,通过所有的builds
对象值对比发现这里的值是区分web
、weex
、server
三个不同平台的,而这里得到的base
变量并不是真正的实际路径,实际路径是通过别名配置文件(alias.js
)控制,看一下别名控制的代码,scripts/alias.js
中(加上了自己的理解注释):// 这里把相应变量处理成对应的绝对地址 module.exports = { // vue: __dirname/src/platforms/web/entry-runtime-with-compiler vue: resolve('src/platforms/web/entry-runtime-with-compiler'), // 下同 vue: '' 变量值 compiler: resolve('src/compiler'), core: resolve('src/core'), shared: resolve('src/shared'), web: resolve('src/platforms/web'), weex: resolve('src/platforms/weex'), server: resolve('src/server'), entries: resolve('src/entries'), sfc: resolve('src/sfc') }
虽然在 Vue2.* 中最终渲染都是通过
render
函数,如果是写template
属性,则需要编译成render
函数,然而这个编译过程会发生在运行时,因此需要带编译器的版本,显然,这种在客户端操作对性能会有一定损耗,一般都是用 Runtime Only.在前面找到了 web 平台在构建过程中使用的文件
entry-runtime.js
和entry-runtime-with-compiler.js
,它们有一个共同的特点是:然而,我们在使用 Vue 到项目的时候需要执行代码
import Vue from 'vue'
,这个vue
其实就是从这里暴露出去的,那它底层究竟是什么?在文件
entry-runtime.js
和entry-runtime-with-compiler.js
中引入了./runtime/index
,找到文件:这里又引入了另外一个文件夹的代码并命名为 Vue,继续找
core/index
:这里又对 Vue 进行了再次引入
instance/index
,并引入了初始化的全局 APIglobal-api/index
文件,再更进一层文件夹找到文件instance/index
代码,终于找到了 Vue 的定义:终于找到了,最底层原来就是一个构造函数,我们在使用时用
new Vue
去实例它;其他的 **Mixin 方法都是给 Vue 的 prototype 上扩展方法,好吧,最底层的 Vue 函数已经找到了,再来看看前面说的全局 APIinitGlobalAPI
;其实,Vue 在整个初始化的过程中,除了给原型上扩展方法外,它还给自身扩展很多全局的静态方法和属性,就定义在
global-api/index
文件中,如下:最后,找到了使用了如此久的 Vue 最底层的代码,虽然学习还需更进一步,但是已经开始了。。。go go go。。。