<div id='wu-app'>
<input type="text" v-model='text'>
<br>
<label for="">Input value:{{text}}</label>
<br>
<input type="button" v-on:click='btnClick' value='Click Me'>
</div>
<script>
window.onload = function () {
var app = new W.Wu({
el: '#wu-app',
data: {
text: 'Hello World!'
},
methods: {
btnClick(e) {
this.text = 'You clicked the button!'
}
}
})
}
</script>
export function Wu(options) {
this.$options = options;
this.$data = options.data || {};
this.$methods = options.methods || {};
this.$watched = options.watched;
// 将data和methods以及computed中的属性方法代理在自己身上
proxy(this, this.$data);
proxy(this, this.$methods);
// 初始化数据劫持
observe(this.$data);
// 模板解析
compile(options.el || document.body, this);
}
function observe(data) {
if (!data || typeof data !== "object") return;
Object.keys(data).forEach(function(key) {
let val = data[key],
dep = new Dep();
//观察子属性
observe(val);
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get: function() {
// console.log(`i get ${key}:${val}`);
//添加订阅者
Dep.target && dep.addSub(Dep.target);
return val;
},
set: function(newVal) {
// console.log(`i set ${key}:${val}-->${newVal}`);
val = newVal;
//通知所有订阅者数据变更
dep.notify();
}
});
});
}
let compileUtil = {
elementNodeType: 1,
textNodeType: 3,
isDirective(attr) {
let reg = /v-|:|@/;
return reg.test(attr);
return (
attr.indexof("v-") == 0 ||
attr.indexof(":") == 0 ||
attr.indexof("@") == 0
);
},
// 将原生节点拷贝到fragment
node2Fragment(node) {
let frag = document.createDocumentFragment();
[].slice.call(node.childNodes).forEach(child => {
frag.appendChild(child);
});
return frag;
},
// 更新回调函数
update(node, dir, newVal, oldVal) {
switch (dir) {
case "model":
node.value = typeof newVal === "undefined" ? "" : newVal;
break;
case "class":
break;
case "html":
break;
case "text":
node.textContent = typeof newVal === "undefined" ? "" : newVal;
break;
}
},
// 获取属性值(当表达式为不只是key,而是一各需要运算的语句是如何处理?)
getVMVal(vm, exp) {
let src = vm;
exp.split(".").forEach(k => {
src = src[k];
});
return src;
},
// 设置属性值
setVMVal(vm, exp, val) {
let src = vm,
keys = exp.split(".");
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
if (i < keys.length - 1) {
src = src[k];
} else {
src[k] = val;
}
}
return src;
},
// 解析节点
compileNode(node, vm) {
[].slice.call(node.childNodes).forEach(child => {
switch (child.nodeType) {
// 节点
case compileUtil.elementNodeType:
let attrs = child.attributes;
[].slice.call(attrs).forEach(attr => {
// 判断是否为内部指令
let attrName = attr.name;
if (this.isDirective(attrName)) {
let dir = attrName.split(/v-|:|@/).join(""),
exp = attr.value;
// 事件
if (dir.substring(0, 2) === "on") {
child.addEventListener(
dir.substring(2),
//注意 this 指向
this.getVMVal(vm, exp).bind(vm)
);
} else {
// 其他指令model bind text等
this.update(child, dir, this.getVMVal(vm, exp));
// 订阅者
new Watcher(vm, exp, (newVal, oldVal) => {
// 更新回调
this.update(child, dir, newVal, oldVal);
});
// model
if (dir === "model") {
let oldVal = this.getVMVal(vm, exp);
// 注册对于表单输入项的input事件
child.addEventListener("input", e => {
var newVal = e.target.value;
if (newVal !== oldVal) {
// 更改数值
this.setVMVal(vm, exp, newVal);
}
});
}
}
// 移除指令
// child.removeAttributes(attr);
}
});
if (child.childNodes && child.childNodes.length > 0) {
this.compileNode(child, vm);
}
break;
// 文本
case compileUtil.textNodeType:
var text = child.textContent,
reg = /\{\{(.*)\}\}/;
if (reg.test(text)) {
var exp = reg.exec(text)[1];
this.update(child, "text", this.getVMVal(vm, exp));
new Watcher(vm, exp, (newVal, oldVal) => {
this.update(child, "text", newVal, oldVal);
});
}
break;
}
});
}
};
// 模板解析
function compile(template, vm) {
let el =
template.nodeType == compileUtil.elementNodeType
? template
: document.querySelector(template); // 取出id为el的第一个节点作为容器
if (el) {
// 将原始节点存为fragment进行操作 减少页面渲染次数 提升效率
let fragment = compileUtil.node2Fragment(el);
compileUtil.compileNode(fragment, vm);
// 处理完后 重新添加至容器
el.appendChild(fragment);
}
}
let _uid = 0;
export function Watcher(vm, exp, cb) {
// 唯一标识
this.id = _uid++;
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.depend();
}
Watcher.prototype = {
constructor: Watcher,
depend() {
Dep.target = this;
//通过触发getter,添加自己为订阅者
var value = this.vm[this.exp];
Dep.target = null;
return value;
},
// 更新
update() {
let oldVal = this.value,
newVal = this.vm[this.exp];
if (oldVal !== newVal) {
this.value = newVal;
this.cb(newVal, oldVal);
}
}
};
export function Dep() {
// 键值对
this.subs = new Map();
}
Dep.prototype = {
constructor: Dep,
// 添加订阅者
addSub(watcher) {
// 通过订阅者id作为唯一标识 避免重复订阅
this.subs.set(watcher.id, watcher);
},
// 通知订阅者
notify() {
this.subs.forEach(watcher => {
watcher.update();
});
}
};
Dep.target = null;