什么叫数据驱动式
上一篇文章大体了解了Vue项目的起手式,本文深入一下挂载实例的本质。在这之前先了解一下vue的一个开发理念-数据驱动式
传统的jq开发方式是操作DOM,进行视图层的更新,现代主流的web开发通过修改数据的形式来进行视图层的更新,也就是说数据和视图层有了双向的或者单向的关联关系,DOM变成了数据的一种映射关系。
简单的示例
<div id="app>
{{ message }}
</div>
var app = new Vue({
el: '#app',
data: {
message: 'hello Vue'
}
})
上面的div的内容将会渲染成hello Vue
,而且修改div内容里面vue实例里面message也会变化,这种双向绑定后续文章再讨论😊,本文主要分析上面div的内容怎么变成了hello Vue
。
那么new Vue干了什么呢
在上一篇文章中我们看了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'
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
当我们通过new
关键字创建一个vue实例时,会调用原型上的_init()
,这部分代码也在上一篇文章中提到了,这个方法的最后调用了vue原型上的$mount()
,这个方法在src/platforms/web/entry-runtime-with-compiler.js
中,(platforms的其他平台也有,先以这个版本为例)
const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && query(el)
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
if (!options.render) {
let template = options.template
if (template) {
if (typeof template === 'string') {
if (template.charAt(0) === '#') {
template = idToTemplate(template)
if (process.env.NODE_ENV !== 'production' && !template) {
warn(
`Template element not found or is empty: ${options.template}`,
this
)
}
}
} 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)
}
if (template) {
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile')
}
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
if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
mark('compile end')
measure(`vue ${this._name} compile`, 'compile', 'compile end')
}
}
}
return mount.call(this, el, hydrating)
}
mount方法干了啥
上述代码的第一句const mount = Vue.prototype.$mount
,使用一个局部变量mount
用来缓存原型上的$mount
,接下来又重新定义了该方法。
原型上的$mount方法
在src/platforms/web/runtime/index.js
// public mount method
Vue.prototype.$mount = function (
el?: string | Element,
hydrating?: boolean
): Component {
el = el && inBrowser ? query(el) : undefined
return mountComponent(this, el, hydrating)
}
重新定义的方法中首先对挂载点el
的类型做了限制-不能是document.body document.documentElement
,也就是不能是body html
这样的根节点。
其次,如果没有定义render方法的话,会把el或者template字符串转换成render方法,也就是说,Vue组件最终都会需要render方法。