=> mvvm(第三版)
class Vue {
constructor(options) {
// 挂载点的dom
this.$el = document.querySelector(options.el)
// 数据
this.$data = options.data
// 数据劫持
observe(this.$data)
//模板编译
compileTemplate(this.$el, this)
}
}
// 观察仓库
class Dep {
constructor() {
// 观察者队列
this.subscribes = []
}
// 添加观察者
addSub(watcher) {
this.subscribes.push(watcher)
}
// 通知更新
notify() {
this.subscribes.forEach(watcher => watcher.update())
}
}
// 观察者
class Watcher {
constructor(vm, key, node) {
// 仓库对象的静态属性来保存当前watcher实例对象
Dep.target = this;
this.$vm = vm;
this.$key = key;
this.$node = node;
// 触发数据劫持中的get方法,此时Dep.target的值为当有对象
this.getValue();
// 设置为null,为下次new Watcher准备
Dep.target = null;
}
getValue() {
// 触发get
this.$value = this.$vm.$data[this.$key]
}
update() {
// 获取一下最新数据
this.getValue();
if (this.$node.nodeType === 1) {
if (this.$node.nodeName == 'INPUT') {
this.$node.value = this.$value
} else {
this.$node.innerHTML = this.$value
}
} else if (this.$node.nodeType === 3) {
this.$node.textContent = this.$value
}
}
}
// 模板编译
function compileTemplate(el, vm) {
// dom操作在内存中完成
// 文档碎片 --> 在内存在存储,不会在界面中渲染,通过appendChild渲染到视图中
let fragment = document.createDocumentFragment()
let childNode;
while (childNode = el.firstChild) {
// 渲染模板
compileRender(childNode, vm)
// 把得到的dom对象放到fragment中,此时dom会删除
fragment.appendChild(childNode)
}
// 处理完成后,放到视图中
el.appendChild(fragment)
}
// 编译视图显示
function compileRender(node, vm) {
// 判断当前节点的类型 1元素,3文本
if (node.nodeType === 1) {
// 得到元素所有的属性集合
// [...node.attributes].forEach(attrObj => {
// console.log(attrObj.name, attrObj.value);
[...node.attributes].forEach(({ name, value }) => {
// 只关心v-开头属性
if (/^v-/.test(name)) {
if (name === 'v-model') { // 针对于input输入框
node.value = vm.$data[value]
// 观察者
new Watcher(vm, value, node)
// 绑定事件
node.addEventListener('input', function () {
vm.$data[value] = this.value.trim()
});
} else {
// 观察者
new Watcher(vm, value, node)
node.innerHTML = vm.$data[value]
}
}
});
// 问一下有没有子元素了
node.childNodes.forEach(child => compileRender(child, vm))
} else if (node.nodeType === 3) { // 文本节点
// 内容
let cnt = node.textContent
// 匹配只有{{}}才进行处理
let preg = /\{\{\s*(\w+)\s*\}\}/
// 替换
cnt = cnt.replace(preg, (a0, a1) => {
// 观察者
new Watcher(vm, a1, node)
return vm.$data[a1]
})
node.textContent = cnt
}
}
// ------------------- 数据劫持
// 监听数据源
function observe(target) {
// 只劫持json对象
if (Object.prototype.toString.call(target) != '[object Object]') return;
// 遍历
for (let key in target) {
defineRactive(target, key, target[key])
}
}
// 实现劫持
function defineRactive(target, key, value) {
observe(value)
let dep = new Dep()
Object.defineProperty(target, key, {
// 获取器
get() {
if (Dep.target) {
// 添加了观察者到通知队列中
dep.addSub(Dep.target)
}
return value
},
// 修改器
set(newV) {
if (newV != value) {
value = newV
dep.notify()
}
}
});
}
调用
<script src="./js/myvue.js"></script>
<div id="app">
<h3>{{ title }}</h3>
<input type="text" v-model='title'>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
title: '你好MVVM'
}
})
</script>