v-if/v-show 简单实现

361 阅读1分钟

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(); // 用于收集依赖,不采用在 get 上进行闭包依赖绑定了
        this.init();
        console.log(this);
    }

    init() {
        this.observer();
        this.compile(this.el);
    }

    // observer
    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
    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
    initEvent(method, dom) {
        dom.addEventListener('click', this.methods[method].bind(this), false);
    }
}