src/platforms/web 这个是
vue
针对web
平台
拉取Vue源码
git clone https://github.com/vuejs/vue.git
版本为 Vue 2.6.10
在根目录下执行:
// 安装依赖
npm i
安装rollup:
因为在 package.json 文件里面发现有这个 rollup
npm i -g rollup
修改dev
脚本,添加sourcemap
,在 package.json
的 scripts
节点下
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-full-dev",
引用网络上的一张图描述
sourcemap
: source-map 的基本原理是,在编译处理的过程中,在生成产物代码的同时生成产物代码中被转换的部分与源代码中相应部分的映射关系表。
也就是说,在本地调试的时候,保证运行的代码和源码里面,位置是对应的。
我们就可以通过 Chrome 控制台中的"Enable Javascript source map"来实现调试时的显示与定位源代码功能
目录结构:
dist
发布目录
examples
范例
packages
Vue核心之外的一些独立库(比如:ssr-serve 服务端渲染,template 模板, weex等)
scripts
构建脚本
src
源码目录
types
类型申明
源码目录结构: (src下的目录)
compiler
编译器相关
platforms
平台相关(web
平台【这次主要看这个】,weex
平台,ssr
平台)
core
核心代码库
-- components
通用组件(但是只有 keep-alive
组件)
-- global-api
全局Api
-- instance
构造函数
-- observer
响应式相关
-- vdom
虚拟dom
入口文件 entry-runtime-with-compiler.js
package.json
通过运行脚本
scripts\config.js
找到这个文件
// 运行脚本
// 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
},
通过上面脚本 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)
}
}
通过上面脚本 aliases
这个地方找到所有的前缀映射
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')
}
那么完整入口文件 entry-runtime-with-compiler.js
位置就在 src/platforms/web/entry-runtime-with-compiler.js
测试文件 01.html
<!DOCTYPE html>
<html lang="en">
<script src="../../dist/vue.min.js"></script>
<body>
<div id='app'>{{ num }}</div>
</body>
<script>
// 1.render 和 template 和 el 优先级?
// 2.el 和 $mount
new Vue({
el: '#app',
// template: '<div>6666</div>',
// render: h => h('div', '777'),
data() {
return {
num: 5
}
}
})// .$mount('#app')
</script>
</html>
通过 src\platforms\web\entry-runtime-with-compiler.js
入口文件
粘贴的源码里面,我添加了注释,并删除了无用代码
/* @flow */
// 入口文件。覆盖 $mount ,执行模板解析和编译工作
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'
const idToTemplate = cached(id => {
const el = query(id)
return el && el.innerHTML
})
// 保存一份原来的 $mount 函数
const mount = Vue.prototype.$mount
// 覆盖(重写) Vue 上的 $mount 函数
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
// 解析option
const options = this.$options
// resolve template/el and convert to render function
if (!options.render) {
let template = options.template
// 模板解析
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
}
} else if (template.nodeType) {
template = template.innerHTML
} else {
if (process.env.NODE_ENV !== 'production') {
warn('invalid template option:' + template, this)
}
return this
}
} else if (el) {
template = getOuterHTML(el)
}
// r如果模板存在,执行编译
if (template) {
// 编译得到渲染函数
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
}
}
// 执行挂载
// 父级的 mount 函数
return mount.call(this, el, hydrating)
}
/**
* Get outerHTML of elements, taking care
* of SVG elements in IE as well.
*/
function getOuterHTML (el: Element): string {
if (el.outerHTML) {
return el.outerHTML
} else {
const container = document.createElement('div')
container.appendChild(el.cloneNode(true))
return container.innerHTML
}
}
Vue.compile = compileToFunctions
export default Vue
发现在最开始的位置const mount = Vue.prototype.$mount
,做了一次保存一份Vue
原有 $mount
函数,然后紧接着又重写了 $mount
上的函数。
为什么重写了 $mount
函数?
发现重写的$mount
函数里面,做了一个事情,就是用 render
还是用 template
或者 el
进行渲染。
但是最后还是使用原本Vue的
$mount
函数进行挂载(因为之前保存了一份Vue
原有$mount
函数)
// 执行挂载
// 父级的 mount 函数
return mount.call(this, el, hydrating)
发现这段源码里面
// r如果模板存在,执行编译
if (template) {
// 编译得到渲染函数
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
}
可以看出Vue
最终渲染的属性始终是 render
(位置:options.render = render
)。虽然平常使用 template
方式,但是最终都会赋值给 render
进行渲染。
结论:
- render 和 template 和 el 优先级?
通过源码的顺序来看,如果render
存在那么就直接执行render
,如果没有就执行template
,如果没有就执行el
(是通过getOuterHTML
函数它的获取HTML)
所有的,最终都是渲染
HTML
render
==> template
==> el
- el 和 $mount 作用
都是指定挂载带一个节点上
在看看 src\platforms\web\runtime\index.js
src\platforms\web\runtime\index.js这个文件是
web
平台的起始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'
import {
query,
mustUseProp,
isReservedTag,
isReservedAttr,
getTagNamespace,
isUnknownElement
} from 'web/util/index'
import { patch } from './patch'
import platformDirectives from './directives/index'
import platformComponents from './components/index'
// 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)
// 指定补丁方法:传入虚拟donm转换为真是dom
// install platform patch function
Vue.prototype.__patch__ = inBrowser ? patch : noop
// 实现$mount
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
// 初始化,将首次渲染结果替换el
return mountComponent(this, el, hydrating)
}
export default Vue
虚拟dom后面在分析
看到这个代码的最下面,发现是做初始化,将首次渲染结果替换为 el
节点
也重新定义了 $mount
函数
上面的分析是 Vue 关于
web
平台相关的src\platforms\web\runtime\index.js
网友评论