JSFiddle - React, Tailwind, and code Playground
HTML
<div id="app">
<h1>{{title}}</h1>
<input v-model="name">
<h2>{{name}}</h2>
<h3>count: <span>{{count}}</span></h3>
<button v-on:click="clickMe">clickMe</button>
</div>
CSS
#app {
text-align: center;
}
JavaScript
class Dep {
constructor() {
this.subs = [];
}
addSub(watcher) {
this.subs.push(watcher);
}
notify() {
this.subs.forEach(watcher => {
watcher.update();
});
}
}
Dep.target = null;
class Observer {
constructor(data) {
this.data = data;
this.walk();
}
walk() {
Object.keys(this.data).forEach(key => {
this.defineReactive(this.data, key, this.data[key]);
});
}
defineReactive(data, key, value) {
const dep = new Dep();
if ( value && typeof value === 'object' ) {
new Observer(value);
}
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get() {
if (Dep.target) {
dep.addSub(Dep.target);
}
return value;
},
set(newVal) {
if (newVal === value) {
return false;
}
value = newVal;
dep.notify();
}
});
}
}
class Watcher {
constructor(vm, exp, cb) {
this.cb = cb;
this.vm = vm;
this.exp = exp;
// 将自己添加到订阅器的操作
this.value = this.getValue();
}
update() {
const value = this.vm.data[this.exp];
const oldValue = this.value;
if (value !== oldValue) {
this.value = value;
this.cb.call(this.vm, value, oldValue);
}
}
getValue() {
Dep.target = this;
const value = this.vm.data[this.exp];
Dep.target = null;
return value;
}
}
class Compile {
constructor(el, vm) {
this.vm = vm;
this.el = document.querySelector(el);
this.fragment = null;
this.init();
}
init() {
if (this.el) {
this.fragment = this.nodeToFragment();
this.compileElement(this.fragment);
this.el.appendChild(this.fragment);
}
}
nodeToFragment() {
const fragment = document.createDocumentFragment();
let child = this.el.firstChild;
while (child) {
fragment.appendChild(child);
child = this.el.firstChild
}
return fragment;
}
compileElement(fragment) {
const childNodes = fragment.childNodes;
[].slice.call(childNodes).forEach((node) => {
const reg = /\{\{(.*)\}\}/;
const text = node.textContent;
if...