直接上代码,如下:
<!DOCTYPE html>
<html>
<head>
<title>vue-test</title>
</head>
<body>
<div id="app">
<span>title:</span><span v-text="text"></span><br>
<span>Price:¥ </span><span v-text="price"></span><br>
<span>Total:¥ </span><span v-text="total"></span><br>
<button v-click="addPrice">
加1
</button>
<button v-click="decPrice">
减1
</button>
</div>
</body>
<script type="text/javascript">
class Vue {
constructor(options) {
this.$options = options;
this.$el = document.querySelector(options.el);
this._data = typeof(options.data) == 'function'? options.data(): options.data; // data可为function也可为object
this._methods = options.methods;
this._bindClick();
Object.keys(this._data).forEach(key => this._proxy(key))
observer(this._data)
watch(this._render.bind(this), this._update.bind(this))
}
_proxy(key) {
console.log('_proxy', key)
const self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
console.log('_proxy get')
return self._data[key]
},
set: function proxySetter (val) {
console.log('_proxy set')
self._data[key] = val
}
})
}
_bindClick() {
console.log("_bindClick");
var _this=this;
let textDOMs=_this.$el.querySelectorAll('[v-click]');
for (let i = 0; i < textDOMs.length; i++) {
if (textDOMs[i].hasAttribute('v-click')) {
textDOMs[i].onclick = (function () {
let attrVal = textDOMs[i].getAttribute('v-click');
return _this._methods[attrVal].bind(_this._data);
})();
}
}
}
_update(target) {
console.log("_update");
this._render.call(this,target)
}
_render(target) {
console.log("_render", target);
let textDOMs=this.$el.querySelectorAll('[v-text]'), bindText;
for(let i=0; i<textDOMs.length; i++){
bindText=textDOMs[i].getAttribute('v-text');
if(target == undefined){
let data = this._data[bindText];
if(data.toString()){
textDOMs[i].innerHTML=data.toString();
}
}
else if(target == bindText){
let data = this._data[bindText];
if(data.toString()){
textDOMs[i].innerHTML=data.toString();
}
}
}
}
}
function observer(value){
console.log('observer')
Object.keys(value).forEach((key) => defineReactive(value, key, value[key]))
}
function defineReactive(obj, key, val) {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: ()=>{
console.log('get,'+key+':', val)
return val
},
set: newVal => {
if(newVal === val)
return
console.log('set,'+key+'由:', val+',变更为:'+newVal)
val = newVal
dep.notify(key)
}
})
}
function watch(exp, cb){
console.log('watch')
Dep.target = new Watcher(cb);
exp()
}
class Watcher {
constructor(cb) {
this.cb = cb
}
}
class Dep {
notify(target) {
console.log('notify')
Dep.target.cb(target)
}
}
// Dep.target=null;
var demo = new Vue({
el: '#app',
data() {
return {
text: "hello world",
price: 0,
total: 0,
};
},
methods: {
addPrice() {
console.warn('addPrice')
this.price = this.price + 1;
this.total = this.price * 3.14159265768;
},
decPrice() {
console.warn('decPrice')
this.price = this.price -1;
this.total = this.price * 3.14159265768;
}
}
})
setTimeout(function(){
console.warn('setTimeout')
demo.text = "hello new world"
}, 2000)
</script>
</html>
输出结果如下:
官方原理图: