HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>简单实现 v-if 和 v-show</title>
<style>
.box-warp {
width: 800px;
height: 200px;
display: flex;
justify-content: flex-start;
align-items: center;
}
.box-warp div {
text-align: center;
line-height: 200px;
font-size: 32px;
font-weight: bold;
color: white;
flex: 1;
}
.btn-warp {
display: flex;
justify-content: flex-start;
align-items: center;
}
.box1 {
background-color: aquamarine;
}
.box2 {
background-color: rosybrown;
}
.box3 {
background-color: burlywood;
}
.box4 {
background-color: orange;
}
</style>
</head>
<body>
<div id="app">
<div class="box-warp">
<div class="box1" v-if="box1">Box1</div>
<div class="box2" v-show="box2">Box2</div>
<div class="box3" v-if="box3">Box3</div>
<div class="box4" v-show="box4">Box4</div>
</div>
<br>
<div class="btn-warp">
<button @click="changeBox1">changeBox1</button>
<button @click="changeBox2">changeBox1</button>
<button @click="changeBox3">changeBox1</button>
<button @click="changeBox4">changeBox1</button>
</div>
</div>
<script src="./index.js"></script>
<script>
var vm = new Mvvm({
el: '#app',
data: {
box1: false,
box2: false,
box3: false,
box4: false,
},
methods: {
changeBox1() {
this.box1 = !this.box1;
},
changeBox2() {
this.box2 = !this.box2;
},
changeBox3() {
this.box3 = !this.box3;
},
changeBox4() {
this.box4 = !this.box4;
}
}
});
</script>
</body>
</html>
JavaScript
class Mvvm {
constructor(options) {
this.el = document.querySelector(options.el);
this.data = options.data;
this.methods = options.methods;
this.dataPool = new Map();
this.init();
console.log(this);
}
init() {
this.observer();
this.compile(this.el);
}
observer() {
const data = this.data;
for (const key in data) {
if (Object.hasOwnProperty.call(data, key)) {
Object.defineProperty(this, key, {
get() {
return data[key];
},
set(newValue) {
data[key] = newValue;
for (const [dom, data] of this.dataPool) {
if (key === data.data) {
data.show = newValue,
this.handleDom(dom, this.dataPool)
}
}
}
})
}
}
}
compile(el) {
const childNodes = el.childNodes;
if (!childNodes.length) {
return;
}
for (const children of childNodes) {
if (children.nodeType === 1) {
const vIf = children.getAttribute('v-if');
const vShow = children.getAttribute('v-show');
const vClick = children.getAttribute('@click');
if(vIf) {
this.dataPool.set(children, {
type: 'if',
show: this.data[vIf],
data: vIf
})
this.initDom(children, this.dataPool);
}
if(vShow) {
this.dataPool.set(children, {
type: 'show',
show: this.data[vShow],
data: vShow
})
this.initDom(children, this.dataPool);
}
if(vClick) {
this.initEvent(vClick, children);
}
}
children.childNodes.length && this.compile(children);
}
}
initDom() {
this.handleDom(...arguments);
}
handleDom(dom, dataPool) {
const data = dataPool.get(dom);
const type = data.type;
switch (type) {
case 'if':
if (!data.comment) {
data.comment = document.createComment('v-if');
}
data.show ? data.comment.parentNode.replaceChild(dom, data.comment) : dom.parentNode.replaceChild(data.comment, dom);
break;
case 'show':
data.show ? dom.style.display = 'block' : dom.style.display = 'none';
break;
default:
break;
}
}
initEvent(method, dom) {
dom.addEventListener('click', this.methods[method].bind(this), false);
}
}