VUE MVVM实现
详细代码请参考: https://github.com/osorso/VUE_MVVM
理解MVVM
mvvm - Model View ViewModel 数据 视图 视图模型
其中Model ---> data, View ---> template, ViewModel ---> new Vue({...})
view通过绑定事件的方式将model联系在一起, model可以通过数据绑定的形式影响view, 而这两种联系则是通过viewModel实现的
实现原理
创建html
vue响应式实现
{{ person.name }} ----- {{ person.age }}
{{ person.fav }}
- 123
- 123
- 123
{{ msg }}
创建一个VUE实例类
class Vue{
constructor(options) {
this.$el = options.el
this.$data = options.data
this.$options = options
if (this.$el) {
new Observer(this.$data)
new Compile(this.$el, this)
}
}
}
通过创建vue实例,将data, el存在时, 创建数据观察者Observe以及指令解析器Compile
实现compile(指令解析器)
class Compile{
constructor(el, vm) {
this.el = this.isElementNode(el) ? el : document.querySelector(el)
this.vm = vm
const fragment = this.nodeFragment(this.el)
this.compile(fragment)
this.el.appendChild(fragment) // 追加子元素到根元素
}
}
fragment此方法为获取文档碎片对象,将其放入内存中,减少回流重绘
nodeFragment(el) {
// 创建文档碎片
const f = document.createDocumentFragment()
let firstChild;
while (firstChild = el.firstChild) {
f.appendChild(firstChild)
}
return f
}
compile(编译模版)
compile(fragment) {
// 获取子节点
const childNodes = fragment.childNodes;
[...childNodes].forEach(child => {
if (this.isElementNode(child)) {
this.compileElement(child)
} else {
this.compileText(child)
}
if (child.childNodes && child.childNodes.length) {
this.compile(child)
}
})
}
compileElement(node) {
const attributes = node.attributes
;[...attributes].forEach(attr => {
const { name, value } = attr
if (this.isDirective(name)) { // 获取到指令v-html v-text等
const [,dirctive] = name.split('-') // text, html, model on:click等
const [dirName, eventName] = dirctive.split(':') // text html model on
// 更新数据, 数据驱动视图
compileUtil[dirName](node, value, this.vm, eventName)
// 删除有指令的标签上的属性
node.removeAttribute('v-' + dirctive)
} else if(this.isEventName(name)) {
let [,eventName] = name.split('@')
compileUtil['on'](node, value, this.vm, eventName)
}
})
}
compileText(node) {
const content = node.textContent
if (/\{\{(.+?)\}\}/.test(content)) {
compileUtil['text'](node, content, this.vm)
}
}
isEventName(attrName) {
return attrName.startsWith('@')
}
nodeFragment(el) {
// 创建文档碎片
const f = document.createDocumentFragment()
let firstChild;
while (firstChild = el.firstChild) {
f.appendChild(firstChild)
}
return f
}
isDirective(attrName) {
return attrName.startsWith('v-')
}
isElementNode(node) {
return node.nodeType === 1
}
compile中解析模板的方法
const compileUtil = {
getVal(expr, vm) { // 获取div v-text="person.name">