源码地址
源码阅读路径
- scripts/build.js
- script/config.js
- script/alias.js
- src/platforms/web/entry-runtime-with-compiler.js
- src/platforms/web/runtime/index.js
- src/core/index.js
- src/core/instance/index.js
源码入口
- 找到package.json文件下的script中的build.js
"build": "node scripts/build.js",
- script/build.js
获取打包的配置文件
let builds = require('./config').getAllBuilds()
- script/config.js
解析入口文件地址【src/platforms/web/entry-runtime-with-compiler.js】此地址为源码的入口地址
const aliases = require('./alias')
const resolve = p => {
const base = p.split('/')[0]
if (aliases[base]) {
// 选web-full-esm配置后,路径被解析成src/platforms/web/entry-runtime-with-compiler.js
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
找到es6的配置项
'web-full-esm': {
// 入口
entry: resolve('web/entry-runtime-with-compiler.js'),
// 出口
dest: resolve('dist/vue.esm.js'),
format: 'es',
alias: { he: './entity-decoder' },
banner
},
- script/alias.js
web: resolve('src/platforms/web'),
入口文件解析
src/platforms/web/entry-runtime-with-compiler.js
此文件主要是对vue原型上$mount方法进行封装,将模版编译成render函数
import Vue from './runtime/index'
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el) // 获取dom元素
/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
process.env.NODE_ENV !== 'production' && warn(
`Do not mount Vue to <html> or <body> - mount to normal elements instead.`
)
return this
}
const options = this.$options // 获取用户选项
// resolve template/el and convert to render function
if (!options.render) { // 先看有没有render方法
let template = options.template // 没有render 找template方法
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') { // #template
template = idToTemplate(template)
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} else if (template.nodeType) { // dom 元素取模板中的内容
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) { // 获取外部模板
template = getOuterHTML(el)
}
if (template) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
// 带compiler的包 内部会对模板进行编译成render函数的操作
const { render, staticRenderFns } = compileToFunctions(template, {
outputSourceRange: process.env.NODE_ENV !== 'production',
shouldDecodeNewlines,
shouldDecodeNewlinesForHref,
delimiters: options.delimiters,// {{}} <%%>
comments: options.comments //
}, this)
options.render = render
options.staticRenderFns = staticRenderFns
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
}
// mount方法
return mount.call(this, el, hydrating)
}
- 接下来到了src/platforms/web/runtime/index.js
做了一个组件挂载的操作
import Vue from 'core/index'
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
// 组件的挂载操作
return mountComponent(this, el, hydrating)
}
- 接下来到了src/core/index.js
初始化全局api
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
//此方法非常重要,下节再说
initGlobalAPI(Vue)
- 接下来到了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'
// Vue的构造函数
function Vue (options) {
// 此方法定义在./init中
this._init(options)
}
// 给Vue扩展原型方法
initMixin(Vue) // Vue.prototype._init
stateMixin(Vue) // $data $props $set $delete $watch
eventsMixin(Vue) // $on $once $emit
lifecycleMixin(Vue)// Vue.prototype._update
renderMixin(Vue) // Vue.prototype._render
export default Vue