本次学习版本是 vue 2.6.11
首先下载vue源码 **https://github.com/vuejs/vue.git** 然后优先看根目录下的 package.json
scripts 字段里面有一堆配置
"scripts": {
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
"dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev",
"dev:esm": "rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm",
"dev:test": "karma start test/unit/karma.dev.config.js",
"dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:web-server-renderer",
"dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:web-compiler ",
"dev:weex": "rollup -w -c scripts/config.js --environment TARGET:weex-framework",
"dev:weex:factory": "rollup -w -c scripts/config.js --environment TARGET:weex-factory",
"dev:weex:compiler": "rollup -w -c scripts/config.js --environment TARGET:weex-compiler ",
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex",
"test": "npm run lint && flow check && npm run test:types && npm run test:cover && npm run test:e2e -- --env phantomjs && npm run test:ssr && npm run test:weex",
"test:unit": "karma start test/unit/karma.unit.config.js",
"test:cover": "karma start test/unit/karma.cover.config.js",
"test:e2e": "npm run build -- web-full-prod,web-server-basic-renderer && node test/e2e/runner.js",
"test:weex": "npm run build:weex && jasmine JASMINE_CONFIG_PATH=test/weex/jasmine.js",
"test:ssr": "npm run build:ssr && jasmine JASMINE_CONFIG_PATH=test/ssr/jasmine.js",
"test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2",
"test:types": "tsc -p ./types/test/tsconfig.json",
"lint": "eslint src scripts test",
"flow": "flow check",
"sauce": "karma start test/unit/karma.sauce.config.js",
"bench:ssr": "npm run build:ssr && node benchmarks/ssr/renderToString.js && node benchmarks/ssr/renderToStream.js",
"release": "bash scripts/release.sh",
"release:weex": "bash scripts/release-weex.sh",
"release:note": "node scripts/gen-release-note.js",
"commit": "git-cz"
},
主要分析我们日常开发熟悉的 "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev" 这一段,这里用到了 rollup (https://github.com/rollup/rollup) 构建,这里暂时不管,只知道是一个类似 webpack 的打包工具。
往后查找 scripts/config.js 以及 TARGET为 web-full-dev 的内容
大概所略为
const version = process.env.VERSION || require('../package.json').version
const banner =
'/*!\\n' +
` * Vue.js v${version}\\n` +
` * (c) 2014-${new Date().getFullYear()} Evan You\\n` +
' * Released under the MIT License.\\n' +
' */'
const builds = {
// Runtime+compiler development build (Browser)
'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
},
}
function genConfig (name) {
const opts = builds[name]
const config = {
input: opts.entry,
external: opts.external,
plugins: [
flow(),
alias(Object.assign({}, aliases, opts.alias))
].concat(opts.plugins || []),
output: {
file: opts.dest,
format: opts.format,
banner: opts.banner,
name: opts.moduleName || 'Vue'
},
onwarn: (msg, warn) => {
if (!/Circular/.test(msg)) {
warn(msg)
}
}
}
// 中间被我删掉一些
...
return config
}
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
到最下面,我们看到它调用了genConfig(process.env.TARGET) ,getConfig用于生成rollup的配置文件。builds是一个对象,获取它的process.env.TARGET值,在package.json中,我们看到dev中有TARGET:web-full-dev参数,即上面我留下的那一段配置。这样入口文件我们就找到了,也就是根目录下 src\platforms\web\entry-runtime-with-compiler.js
找到 entry-runtime-with-compiler.js 就看到
import Vue from './runtime/index'
Vue 是从 './runtime/index' 文件引入,然后在改文件进行加工 原型上挂载 $mount 以及 添加 compile 方法。
打开 './runtime/index'
第一行就是 import Vue from 'core/index' 所以该文件的vue也还是从另一个地方的 vue 引进来而进行拓展。该文件 引入 patch 以及 对vue原型关注了 $mount 所以该文件是比较重要,以后会分析
接下来找到 'core/index' 文件
第一行也是从其他文件引入 import Vue from './instance/index' 是真有点深
找到 ./instance/index 文件,终于看到了光明。
整个过程是
src/platforms/web/entry-runtime-with-compiler.js
->
src/platforms/web/runtime/index.js
->
src/core/index.js
->
src/core/instance/index.js
找到 src/core/instance/index.js 终于找到定义 Vue 对象的所在之处,它是一个构造函数
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)
}
首先判断如果不是生产环境,且不是通过 new 构造出来的实例对象话,就在控制台打印 warn ,来看下这个 warn 定义
是在 src\core\util\debug.js 文件里面,
export let generateComponentTrace = (noop: any) // work around flow check
warn = (msg, vm) => {
const trace = vm ? generateComponentTrace(vm) : ''
// config.warnHandler 默认是 null
if (config.warnHandler) {
config.warnHandler.call(null, msg, vm, trace)
} else if (hasConsole && (!config.silent)) {
console.error(`[Vue warn]: ${msg}${trace}`)
}
}
接下来调用 this._init(options) ,接着 就对 Vue.prototype 原型上绑定了一些实例,后面看到源码有总结慢慢进行补充
// _init
initMixin(Vue)
// $set、$delete、$watch
stateMixin(Vue)
// $on、$once、$off、$emit
eventsMixin(Vue)
// _update、$forceUpdate、$destroy
lifecycleMixin(Vue)
// $nextTick、_render、以及多个内部调用的方法
renderMixin(Vue)
接着我们按照查找的倒叙看回 src/core/index.js
initGlobalAPI(Vue)
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__'
这个文件也可以一目了然,首先调用了 initGlobalAPI(Vue) , initGlobalAPI 方法引自src\core\global-api\index.js
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 -> 'component','directive','filter'
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
// Vue.options.components.KeepAlive
extend(Vue.options.components, builtInComponents)
// Vue.use
initUse(Vue)
// Vue.mixin
initMixin(Vue)
// Vue.extend
initExtend(Vue)
// Vue.component、Vue.directive、Vue.filter
initAssetRegisters(Vue)
}
从上面代码可以看出,它是对Vue对象添加了一些静态方法和属性
/src/core/index.js文件中,还添加了一个Vue.prototype.$isServer
属性,用于判断是不是服务端渲染,以及 $ssrContext
(待补充) 还有 FunctionalRenderContext 用来 SSR 运行时帮助安装, 还有一个就是Vue.version。
接着就是 src/platforms/web/runtime/index.js
// install platform specific utils
Vue.config.mustUseProp = mustUseProp
Vue.config.isReservedTag = isReservedTag
Vue.config.isReservedAttr = isReservedAttr
Vue.config.getTagNamespace = getTagNamespace
Vue.config.isUnknownElement = isUnknownElement
// install platform runtime directives & components
extend(Vue.options.directives, platformDirectives)
extend(Vue.options.components, platformComponents)
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
// devtools global hook
/* istanbul ignore next */
if (inBrowser) {
setTimeout(() => {
if (config.devtools) {
if (devtools) {
devtools.emit('init', Vue)
} else if (
process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== 'test'
) {
console[console.info ? 'info' : 'log'](
'Download the Vue Devtools extension for a better development experience:\\n' +
'<https://github.com/vuejs/vue-devtools>'
)
}
}
if (process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== 'test' &&
config.productionTip !== false &&
typeof console !== 'undefined'
) {
console[console.info ? 'info' : 'log'](
`You are running Vue in development mode.\\n` +
`Make sure to turn on production mode when deploying for production.\\n` +
`See more tips at <https://vuejs.org/guide/deployment.html`>
)
}
}, 0)
}
export default Vue
我们先看看 src/platforms/ 里面有 web 和 weex 文件夹,所以源码里把两个平台不同的内容单独提取出来了,这种目录思维非常值得学习借鉴。
我们这里只谈 web ,首先,在Vue.config上添加了几个平台相关的方法,扩展了Vue.options.directives(model和show)和Vue.options.components(Transition和TransitionGroup)。在Vue.prototype上添加了__patch__
(虚拟dom相关)和$mount(挂载元素)。
最后是/src/entries/web-runtime-with-compiler.js,该文件主要干了两件事,一个是定义了一个方法Vue.prototype.$mount,另一个是将compileToFunctions挂在到Vue.compile上。
总结
entry-runtime-with-compiler.js
一个是定义了一个方法Vue.prototype.$mount,另一个是将compileToFunctions挂在到Vue.compile上
src/platforms/web/runtime/index.js
首先,在Vue.config上添加了几个平台相关的方法,扩展了Vue.options.directives(model和show)和Vue.options.components(Transition和TransitionGroup)。在Vue.prototype上添加了__patch__
(虚拟dom相关)和$mount(挂载元素)。
src/core/index.js
添加了一些静态方法和属性
Vue.use、 Vue.mixin、 Vue.extend、[ Vue.component、Vue.directive、Vue.filter ] Vue.options.components.KeepAlive
还添加了一个Vue.prototype.$isServer
属性,用于判断是不是服务端渲染,以及 $ssrContext (待补充) 还有 FunctionalRenderContext 用来 SSR 运行时帮助安装, 还有一个就是Vue.version。
src/core/instance/index.js
vue 构造函数,以及一些静态方法和实例
_init initMixin(Vue)
$set
、$delete
、$watch stateMixin(Vue)
$on
、$once
、$off
、$emit eventsMixin(Vue)
_update、$forceUpdate
、$destroy lifecycleMixin(Vue)
$nextTick
、_render、以及多个内部调用的方法 renderMixin(Vue)