一、数据绑定
AngularJS 使用双向绑定, Vue在不同组件间强制使用单向数据流。
二、VUE框架中MVVM实现原理深度解读
1、什么是MVVM?
MVVM实现了数据层和视图层的关联。
VUE主要靠的,就是MVVM, 实现了数据绑定和模板渲染。
先来个小例子来感受一下双向绑定:
初始化package.json:
npm init -y
安装:
npm install vue --save
安装的vue的版本为@2.6.7
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--双向数据绑定,靠的是表单-->
<input type="text" v-model="msg">
{{msg}}
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
// 我们的数据一般都挂载在vm上
let vm = new Vue({
el: '#app',
data: {
msg: 'hello'
}
})
</script>
</body>
</html>
compile.js:
// compile.js 负责编译模板
// 把数据渲染到页面上
class Compile {
constructor(el, vm) {
this.el = this.isElementNode(el) ? el : document.querySelector(el);
this.vm = vm;
if (this.el) {
// 如果这个元素能获取到,我们才开始编译
// 1.先把这些真实的DOM移入到内存中,fragment(文档碎片)
let fragment = this.node2fragment(this.el);
// 2.编译,提取想要的元素节点v-model 和 文本节点 {{}}
this.compile(fragment);
// 3.把编译好的fragment再塞回到页面里去
}
}
// 专门写一些辅助的方法
isElementNode(node) {
return node.nodeType === 1;
}
// 核心的方法
// 编译
compile(fragment) {
let childNodes = fragment.childNodes;
// console.log(childNodes);
// 需要递归
Array.from(childNodes).forEach(node => {
if (this.isElementNode(node)) {
// 是元素节点,还需要继续深入的检查
console.log('element', node);
// 这里需要编译元素
this.compileElement(node);
this.compile(node);
} else {
// 是文本节点
// 这里需要编译文本
console.log('text', node);
// 编译文本
this.compileText(node);
}
});
}
// 需要将el中的内容全部放到内存中
node2fragment(el) {
// 创建内存中的文档碎片
// 文档碎片, 内存中的dom节点
let fragment = document.createDocumentFragment();
let firstChild;
while (firstChild = el.firstChild) {
fragment.appendChild(firstChild);
}
// 内存中的节点
return fragment;
}
// 编译元素
compileElement(node) {
// 比如,编译带 v-model 的
// 判断元素上有没有v-model属性
// 取出当前元素节点的属性
let attrs = node.attributes;
console.log(attrs);
Array.from(attrs).forEach(attr => {
console.log(attr.name);
console.log(attr.value); // 取的是值
// 判断属性名字是不是包含 v-
let attrName = attr.name;
if (this.isDirective(attrName)) {
// 取到对应的值放到节点中
let expr = attr.value;
let [,type] = attrName.split('-');
console.log(type);
// node
// 编译
CompileUtil[type](node, this.vm, expr);
}
})
}
// 编译文本
compileText(node) {
// 比如,编译带 {{}} 的
let expr = node.textContent; // 取文本中的内容
console.log(expr);
let reg = /\{\{([^}]+)\}\}/g;
if (reg.test(expr)) {
// node, this.vm.$data
CompileUtil['text'](node, this.vm, expr);
}
}
// 判断是不是指令
isDirective(name) {
// 如果name里面包含'v-',那么返回true。
return name.includes('v-');
}
}
// 编译的方法
CompileUtil = {
getVal(vm, expr){
// 获取实例上对应的数据
expr.split('.');
expr.reduce((prev,next) => {
return prev[next];
}, vm.$data);
},
// 编译文本
text(node, vm, expr) {
let updateFn = this.updater['textUpdater'];
vm.$data[expr];
updateFn && updateFn(node);
},
// 编译 v-model
model(node, vm, expr) {
let updateFn = this.updater['modelUpdater'];
updateFn && updateFn(node,this.getVal(vm, expr));
},
// 编译 v-html
html(node, vm, expr) {
//...
},
updater: {
// 文本更新
textUpdater(node, value) {
node.textContent = value;
},
// 输入框更新
modelUpdater(node, value) {
node.value = value;
},
// html更新
htmlUpdater(node, value) {
node.innerHTML = value;
}
}
}
// 01:09:37