使用页面:
<!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>
<script src="Observer.js"></script>
<script src="MyVue.js"></script>
</head>
<body>
<div id="mvvm-app">
<input type="text" v-model="word">
<p>{{message}}</p>
<p>{{word}}</p>
<button v-on:click="sayHi">change model</button>
</div>
<script type="text/javascript">
new MyVue({
el: '#mvvm-app',
data: {
message: "hello,MyVue",
word: "my is input"
},
methods: {
sayHi() {
//获取1-10之间的随机数
var num = Math.floor(Math.random() * 10 + 1);
this.message = num;
}
},
updated(){
console.log('视图更新')
console.log(this)
},
created(){
console.log('初始化完成')
console.log(this)
},
mounted() {
console.log('完成挂载')
}
})
</script>
</body>
</html>
Observer.js
由:
Observer 监听器
Dep 通知器
Watcher 观察器处理刷新
/**
*
* @param {} obj 类型判断
*/
let getType = (obj) => {
return Object.prototype.toString.call(obj).match(/object\s*(\w*)/)[1]
}
function Watcher(vm, exp, fn) {
this.fn = fn;
this.exp = exp;
this.vm = vm;
this.getter = this.parseGetter(exp);
this.value = this.get();
this.update = function () {
var value = this.get();
var oldVal = this.value;
if (value !== oldVal) {
this.value = value;
this.fn.call(this.vm, value, oldVal);
}
}
}
Watcher.prototype.get = function (exp) {
Dep.target = this;
var value = this.getter.call(this.vm, this.vm);
Dep.target = null;
return value;
}
Watcher.prototype.parseGetter = function (exp) {
if (/[^\w.$]/.test(exp)) return;
var exps = exp.split('.');
return function (obj) {
for (var i = 0, len = exps.length; i < len; i++) {
if (!obj) return;
obj = obj[exps[i]];
}
return obj;
}
}
function Dep() {
this.subs = [];
this.addSub = function (watcher) {
this.subs.push(watcher);
}
this.notify = function () {
this.subs.forEach(function (watcher) {
watcher.update();
});
}
}
function Observer(obj, key, value) {
var dep = new Dep();
if (Object.prototype.toString.call(value) == '[object Object]') {
Object.keys(value).forEach(function (key) {
new Observer(value, key, value[key])
})
};
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function () {
if (Dep.target) {
dep.addSub(Dep.target);
};
return value;
},
set: function (newVal) {
value = newVal;
dep.notify();
}
})
}
/**
*
* @param {} el 解析模板
*/
function Compile(el, vm) {
this.$vm = vm;
this.$el = this.isElementNode(el) ? el : document.querySelector(el);
if (this.$el) {
this.$fragment = this.node2Fragment(this.$el);
this.init();
this.$el.appendChild(this.$fragment);
}
}
/**
* 判断是否是元素
*/
Compile.prototype.isElementNode = function (el) {
return el.nodeType === 1 ? true : false;
}
Compile.prototype.node2Fragment = function ($el) {
/**
* 创建虚拟document 片段
*/
var fragment = document.createDocumentFragment(), child;
while (child = $el.firstChild) {
fragment.appendChild(child);
}
return fragment;
}
Compile.prototype.init = function () {
this.compileElement(this.$fragment);
}
Compile.prototype.compileElement = function ($el) {
/**
* 获取#demo 的所有子元素
*/
var childNodes = $el.childNodes
new Array().slice.call(childNodes).forEach((node) => {
//返回节点及其后代的文本内容
var text = node.textContent;
var reg = /\{\{(.*)\}\}/;
/**
* 判断是否为元素节点
*/
if (node.nodeType === 1) {
this.compile(node)
}
//文本节点
if (node.nodeType === 3 && reg.test(text)) {
compileUtil.text(node, this.$vm, RegExp.$1);
}
/**
* 是否存在子节点
*/
if (node.childNodes && node.childNodes.length) {
this.compileElement(node);
}
})
}
/**
* 处理元素节点解析指令
*/
Compile.prototype.compile = function (node) {
var nodeAttrs = node.attributes
new Array().slice.call(nodeAttrs).forEach((attr) => {
//获得自定义属性名
var attrName = attr.name;
/**
* 如果存在v- 表示指令
*/
if (attrName.indexOf("v-") != -1) {
var exp = attr.value;
//列:v-on:click 去除v- 得到 on:click
var dir = attrName.substring(2);
//解析事件指令
if (dir.indexOf('on') === 0) {
compileUtil.eventHandler(node, this.$vm, exp, dir);
}else{
//判断指令是否 存在
compileUtil[dir] && compileUtil[dir](node, this.$vm, exp);
}
}
})
}
var compileUtil = {
//注入文本
text: function (node, vm, exp) {
this.bind(node, vm, exp, 'text');
},
//绑定文本表单
model:function(node, vm, exp){
this.bind(node, vm, exp, 'model');
var val = this._getVMVal(vm, exp);
node.addEventListener('input', (e)=> {
this._setVMVal(vm, exp,e.target.value);
});
},
bind: function (node, vm, exp, dir) {
var updaterFn = updater[dir + 'Updater'];
updaterFn && updaterFn(node, this._getVMVal(vm, exp));
new Watcher(vm, exp, (value, oldValue) => {
vm.$options.updated.call(vm)
updaterFn && updaterFn(node, value, oldValue);
});
},
//注入事件
eventHandler: function (node, vm, exp, dir) {
var eventType = dir.split(':')[1],
fn = vm.$options.methods && vm.$options.methods[exp];
if (eventType && fn) {
node.addEventListener(eventType, fn.bind(vm), false);
}
},
_getVMVal: function (vm, exp) {
var val = vm;
exp = exp.split('.');
//匹配this.$data 里面对应的值
exp.forEach(function (k) {
val = val[k];
});
return val;
},
_setVMVal: function (vm, exp,value) {
vm[exp] = value
}
}
var updater = {
textUpdater: function (node, value) {
node.textContent = typeof value == 'undefined' ? '' : value;
},
modelUpdater: function (node, value) {
node.value = typeof value == 'undefined' ? '' : value;
},
};
MyVue.js 实例
function MyVue(options) {
if (getType(options) != 'Object') {
throw ("参数必须为对象")
}
let $this = this;
/**
* 得到需要监听的Object 对象
*/
this.$data = getType(options.data) != 'Object' ? {} : options.data;
this.$options = options;
this.proxyKeys();
/**
* 监听器,监听this.$data 的属性
*/
Object.keys(this.$data).forEach((key) =>{
Observer(this.$data,key,this.$data[key])
});
options.created.call(this)
this.$compile = new Compile(options.el || document.body, this)
/**
* 执行mounted 函数方法,并且把作用域执行MyVue 实例
*/
options.mounted.call(this)
}
/**
* proxyKeys 代理属性将this.$data 里面的对象属性都绑定到MyVue 属性上
* 列:this.$data.xxx 拥有可以this.xxx 实例直接获取对应属于他的属性
*/
MyVue.prototype.proxyKeys = function () {
/**
* 获取对象的所有可遍历属性
*/
let keys = Object.keys(this.$data);
/**
* 为MyVue 添加this.xx 实例属性
*/
keys.forEach((key) => {
Object.defineProperty(this, key, {
enumerable: true,
configurable: false,
set(newVal) {
this.$data[key] = newVal
},
get() {
return this.$data[key];
}
})
})
}