Skip to content

Instantly share code, notes, and snippets.

@ssdemajia
Created September 25, 2018 10:42
Show Gist options
  • Save ssdemajia/5c4abaa34c7d9767e47c2c45182442e4 to your computer and use it in GitHub Desktop.
Save ssdemajia/5c4abaa34c7d9767e47c2c45182442e4 to your computer and use it in GitHub Desktop.
Vue双向绑定实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
<input type="text" v-model="vueData">
{{ vueData }}
<button type="button" onclick="ssClick()">change</button>
</div>
<script src="ss.js"></script>
<script>
let vm = new Vue({
el: '#app',
data: {
vueData: 'ssss'
}
})
function ssClick() {
vm.data['vueData'] = 'shaoshuai';
}
</script>
</body>
</html>
// vm == view model
class Dep{
constructor(){
this.subscribers = [];
}
addSubscriber(sub) {
this.subscribers.push(sub);
}
notify() {
this.subscribers.forEach((sub) => {
sub.update();
});
}
}
class Watcher {
constructor(vm, node, name, nodeType) {
Dep.target = this;
this.vm = vm;
this.node = node;
this.name = name;
this.nodeType = nodeType;
this.update();
Dep.target = null;
}
update() {
this.get();
if (this.nodeType == 'text') {
this.node.data = this.value;
}
if (this.nodeType == 'input') {
this.node.value = this.value;
}
}
get() {
this.value = this.vm.data[this.name];
}
}
class Vue{
constructor(options) {
this.data = options.data;
toObserve(this.data);
let root = document.querySelector(options.el);
var newChild = nodeToFragment(root, this);
root.appendChild(newChild);
}
}
function toReactive(key, value, obj) {
let dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.addSubscriber(Dep.target);
}
return value;
},
set(newValue) {
if (newValue == value) {
return;
}
value = newValue;
dep.notify();
}
});
}
function toObserve(obj) {
Object.keys(obj).forEach((key) => {
toReactive(key, obj[key], obj);
});
}
function nodeToFragment(node, vm) {
let flag = document.createDocumentFragment();
let child = node.firstChild;
while (child) { // 遍历child
compile(child, vm);
flag.appendChild(child);
child = node.firstChild;
}
return flag;
}
function compile(node, vm) {
let reg = /\{\{(.*)\}\}/;
if (node.nodeType == Node.ELEMENT_NODE) {
let attrNode = node.attributes;
for (let i = 0; i < attrNode.length; i++) {
let attr = attrNode[i];
if (attr.nodeName == 'v-model') {
let name = attr.nodeValue;
node.addEventListener('input', (e) => {
console.log(e);
vm.data[name] = e.target.value; // 这里会通知name的subscribers进行update
console.log(vm);
});
node.value = vm.data[name]; // 通知
node.removeAttribute('v-model');
new Watcher(vm, node, name, 'input');
}
}
}
else if (node.nodeType == Node.TEXT_NODE) {
if (reg.test(node.nodeValue)) {
let name = RegExp.$1;
name = name.trim();
node.nodeValue = vm.data[name];
new Watcher(vm, node, name, 'text');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment