装作无所畏惧的样子,之后会越来越勇敢
src文件夹下:
在项目根目录下package.json文件中script配置如下:
{
"script": {
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex"
}
}
后两条是在第一条命令的基础上,添加了一些环境参数。最终都会执行scripts/build.js文件。我们一起看一下scripts/build:
let builds = require('./config').getAllBuilds()
// filter builds via command line arg
if (process.argv[2]) {
const filters = process.argv[2].split(',')
builds = builds.filter(b => {
return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1)
})
} else {
// filter out weex builds by default
builds = builds.filter(b => {
return b.output.file.indexOf('weex') === -1
})
}
build(builds)
先从配置文件读取配置,在通过命令行参数对配置构建做过滤,这样就可以构建出不同用途的vue.js了。接下来一起看看scripts/config.js
const builds = {
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
'web-runtime-cjs': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.js'),
format: 'cjs',
banner
},
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.js'),
format: 'cjs',
alias: { he: './entity-decoder' },
banner
}
...
}
对于单个的配置,它是遵循Rollup的构建规则的。其中
以web-runtime-cjs的构建为例,它的入口是 resolve做了什么:
const aliases = require('./alias')
const resolve = p => {
const base = p.split('/')[0]
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
resolve将传入的目录按照'/'分隔,取得第一个,在这个例子中取得'web',他的真实路径借助别名配置,从下面的alias文件可以看出,web对应的真实路径是path.resolve(__dirname, '../', 'src/platforms/web');
const path = require('path')
const resolve = p => path.resolve(__dirname, '../', p)
module.exports = {
vue: resolve('src/platforms/web/entry-runtime-with-compiler'),
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'),
sfc: resolve('src/sfc')
}
也就是说 resolve('web/entry-runtime.js') == path.resolve(../src/platforms/web, p.slice(base.length + 1)) == path.resolve(../src/platforms/web, 'entry-runtime.js') 最终定位到src/platforms/web/entry-runtime.js作为入口文件。
根据如下配置文件,最后生成文件vue.runtime.common.js
dest: resolve('dist/vue.runtime.common.js')
以上我们知道了不同的作用和功能的vue.js他们对应的入口以及最终编译生成的JS文件。为了分析vue的编译过程,我们会着重分析runtime + compiler的vue.js
我们从入口文件开始吧,入口文件:src/platforms/web/entry-runtime-with-compiler.js
/* @flow */
import config from 'core/config'
import { warn, cached } from 'core/util/index'
import { mark, measure } from 'core/util/perf'
import Vue from './runtime/index'
import { query } from './util/index'
import { compileToFunctions } from './compiler/index'
import { shouldDecodeNewlines, shouldDecodeNewlinesForHref } from './util/compat'
...
export default Vue
这个文件import vue 之后又export,import Vue from './runtime/index';继续追踪runtime/index.js文件
/* @flow */
import Vue from 'core/index'
import config from 'core/config'
import { extend, noop } from 'shared/util'
import { mountComponent } from 'core/instance/lifecycle'
import { devtools, inBrowser } from 'core/util/index'
...
export default Vue
根据import Vue from 'core/index',追踪到文件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'
initGlobalAPI(Vue)
...
export default Vue
这个文件有两处重要的:import Vue from './instance/index' 和initGlobalAPI(Vue) 。我们先看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的定义,实际是一个用Function实现的类。我们通过new Vue可以实例化它。
根据import { initGlobalAPI } from './global-api/index'
/* @flow */
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/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)
// 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
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
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扩展了一些全局方法。如Vue.nextTick ,Vue.set...
到现在vue的初始化过程基本即完成了。实际vue的本质是Function实现的Class,然后它的原型prototype以及它本身都扩展了一系列的方法和属性。具体的我们后面继续看。
愿我们有能力不向生活缴械投降---Lin