实现v-model v-bind v-click
github: https://github.com/myh6666/vvvue/blob/master/src/vue/vue.js
export default class Vue {
constructor(option) {
if (!option.el) return;
this.$el = document.querySelector(option.el);
this.data = option.data || {};
this.$methods = option.methods || {};
this.$binding = {};
this.init()
}
init() {
this.obs();
this.complie(this.$el);
Object.keys(this.$binding)
.forEach(key => this.$binding[key]
.forEach(b => b.update()));
}
obs() {
let that = this;
this.$data = new Proxy(this.data, { // 使用proxy在值发生变化时触发监听器
set(target, key, value) {
let val = Reflect.set(target, key, value);
that.$binding[key].forEach(x => x.update());
return val;
}
});
}
complie(node) {
let nodes = Array.from(node.children);
nodes.forEach(n => {
if (n.children.length) this.complie(n);
const vModel = n.getAttribute('v-model');
if (vModel) {
this.pushWatcher(new Watcher(n, 'value', this.$data, vModel));
n.addEventListener('input', () => this.$data[vModel] = n.value);
}
const vBind = n.getAttribute('v-bind');
if (vBind) {
this.pushWatcher(new Watcher(n, 'innerHTML', this.$data, vBind));
}
const vClick = n.getAttribute('v-click');
if (vClick) {
n.addEventListener('click', () => this.$methods[vClick] && this.$methods[vClick].call(this.$data))
}
});
}
pushWatcher(watcher) {
if (!this.$binding[watcher.key]) this.$binding[watcher.key] = [];
this.$binding[watcher.key].push(watcher)
}
}
class Watcher { // 注册监听器
constructor(node, attr, data, key) {
this.node = node;
this.attr = attr;
this.data = data;
this.key = key;
}
update() {
this.node[this.attr] = this.data[this.key];
}
}
<div id="app">
<input type="text" v-model="ipt">
<p v-bind="ipt"></p>
<hr>
<input type="text" v-model="ipt2">
<p v-bind="ipt2"></p>
<hr>
<button v-click="switch">click</button>
</div>
import Vue from './vue/vue';
import './css/index.css';
new Vue({
el: '#app',
data: {
ipt: '',
ipt2: '234'
},
methods: {
switch() {
this.ipt = [this.ipt2, this.ipt2 = this.ipt][0]
}
}
});