备注:案例中使用的vue版本全部为2.6.10
git clone https://github.com/vuejs/vue.git
npm install
进入package.json,在script中增加"dev:read"命令,具体如下
"scripts": {
// 方便源码阅读
"dev:read": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
...
}
在根目录下创建index.html文件,
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible"
content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<h1>Hello {
{
name}}</h1>
</div>
<script src="./dist/vue.js"></script>
<script>
let vm = new Vue({
data() {
return {
name: "Connie!"
}
},
})
vm.$mount('#app')
</script>
</body>
</html>
动态监控生成vue.js 文件
npm run dev:read
打开index.html,可以在浏览器中看到vue的源码,并且可以修改源码内容,设置断点调试。
// 其中入口文件为scripts/config.js, 命令为web-full-dev
"dev:read": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
// 入口为web/entry-runtime-with-compiler.js,生成文件dist/vue.js,环境为development
// 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
},
文件路径: vue/src/platforms/web/entry-runtime-with-compiler.js
可以在此文件中终于找到我们想要的$mount函数,此函数返回的是一个vm组件,index.html 这样调用了
vm.$mount('#app')
现在继续往下看,我们来看这个函数中的几句核心代码
// 1 获取element元素
el = el && query(el)
// 2 生成template,此处通过如下代码获取template
template = getOuterHTML(el)
// 3 通过compileToFunctions生成render函数,此案例中render函数长这样:)
/**
function anonymous(
) {
with (this) {
return _c(
'div',
{ attrs: { "id": "app" } },
[_c(
'h1',
[_v("Hello " + _s(name))]
)]
)
}
}
*/
// 4 将render函数保存到vm.$options.render,以便在后续代码中使用
options.render = render
// 5 调用vue本身的mout函数,返回一个Component
return mount.call(this, el, hydrating)
文件路径:vue/src/platforms/web/runtime/index.js
// 1 获取element 元素
el = el && inBrowser ? query(el) : undefined
// 2 挂载组件
mountComponent(this, el, hydrating)
文件路径:vue/src/core/instance/lifecycle.js
// 1. 保存Element
vm.$el = el
// 2. 调用beforeMount生命周期函数
callHook(vm, 'beforeMount')
// 3. 设置updateComponent函数
/********重点核心代码********/
updateComponent = () => {
// vm._render()返回虚拟DOM VNode,下面会重点分析_render函数
vm._update(vm._render(), hydrating)
}
// 4. 设置观察者(vue1和vue2的重要区别)
new Watcher(vm, updateComponent, noop, {
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}, true /* isRenderWatcher */)
// 5. 调用mounted生命周期函数
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
// 6. 返回vm组件实例
return vm
文件路径:vue/src/core/instance/render.js
/*******重点核心代码*********/
// 调用render函数生成虚拟DOM
// vm._renderProxy是vm本身,
// vm.$createElement 是h参数,用来新建虚拟dom
/* render是new Vue中的render函数
new Vue({
render: h=>h(APP)
})
*/
// 组件的本质,div元素的本质,其实就是虚拟DOM
vnode = render.call(vm._renderProxy, vm.$createElement)
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
文件路径:vue/src/core/vdom/create-element.js
// 参数校验及调整
if (Array.isArray(data) || isPrimitive(data)) {
normalizationType = children
children = data
data = undefined
}
if (isTrue(alwaysNormalize)) {
normalizationType = ALWAYS_NORMALIZE
}
// 调用真正新建组件函数
_createElement(context, tag, data, children, normalizationType)
后续有待更新,争取每天更新一小部分:)…