简版v-show和v-if实现

1,977 阅读2分钟

学习过VueJS的小伙伴都知道,Vue本身有内置的指令v-if和v-show。两者本身都可以控制节点的隐藏和显示,只是各自实现的原理不一样。下面尝试实现一遍类似,简版的v-show和v-if。

首先创建一个index.html文件,里面创建一个id为app的div元素,里面存放相应的子元素。

 <div id="app">        
    <div class="box-wrapper">            
        <div class="box box1" v-if="boxShow1">BOX 1</div>            
        <div class="box box2" v-show="boxShow2">BOX 2</div>        
    </div>        
    <button @click="showBox1">BOX 1</button>        
    <button @click="showBox2">BOX 2</button> 
</div>   

给元素命令class类名,box1绑定v-if,box2绑定v-show,按钮Box1和按钮Box2分别绑定showBox1和showBox2方法控制box1和box2的显示和隐藏。css样式方面省略介绍。效果图如下。

在index.html文件的相同路径下创建一个index.js文件,并在index.html文件下引入。在index.html文件下的script写相应的逻辑代码。

<script>
   new mVue({
    el: #app,
    data() {
        return {
            boxShow1: false,
            boxShow2: false
        }
    },
    methods: {
        showBox1() {
            this.boxShow1 = !this.boxShow1
        },
        showBox2() {
            this.boxShow2 = !this.boxShow2
        }
    }
})  
<script>

到这里或多或少有点疑惑,mVue到底是什么?其实mVue的主要核心在index.js。

用过VueJS都知道,v-show在html元素上是通过控制display来显示隐藏的,而v-if在html元素上通过来占位。

class mVue{
    constructor(options) {
        this.el = document.querySelector(el)
        this.data = typeof(this.options.data) === 'function' ? options.call(this) : options.data
        this.methods = options.methods
        this.showPool = new Map()
        this.eventPool = new Map()
        this.init()
    }
}

showPool接收的是一个map的数据结构,key为节点类型的dom,value为一个对象,字段type的值为if或show,字段show的值为true或false,字段data为绑定的数据。

showPool接收的也是一个map的数据结构,key为节点类型的dom,value为绑定的方法。

为什么选择map作为数据结构?因为map的key可以是任意类型

init() {
    this.initData() // 代理数据与数据劫持
    this.initDom() // 初始化Dom
    this.initView() // 初始化视图
    this.initEvent() // 事件处理函数的绑定
}

initData() {
    for (let key in this.data) {
        Object.defineProperty(this, key, {
            get() {
                return this.data[key]
            },
            set(newValue) {
                this.data[key] = newValue
                this.domChange(key, this.showPool)
            }
        }
    }
}

initDom(el) {
    const _childNodes = el.childNodes
    if (!childNodes.length) return
    _childNodes.forEach(dom => {
        if (dom.NodeTye === 1) {
            const vIf = dom.getAttribute('v-if')
            const vShow = dom.getAttribute('v-show')
            const vEvent = dom.getAttribute('@click')
            if (vIf) {
                this.showPool.set(dom, {
                    type: 'if',
                    show: this.data[vIf],
                    data: vIf
                })
               } else if (vShow) {
                    this.showPool.set(dom, {
                    show: this.data[vShow],
                    data: vShow
                })
                if (vEvent) {
                    this.eventPool.set(dom, this.methods[vEvent])
                }
            }
            this.initDom(dom)
        })
}

initView(showPool) {
    this.domChange(null, showPool)
}

domChange(data, showPool) {
    if (!data) {
    for (let [k, v] of showPool) {
        switch (v.type)
            case: 'if':
                v.comment = document.createComment(`v-if`)
                !v.show && k.parentNode.replace(v.comment, k)
                break
            case 'show':
                !show && (k.style.display = 'none')
                break
            default:
                break
            }
        }
        return
    }
    for (let [k, v] of showPool) {
        if (v.data === data) {
            switch (v.type) {
                case 'if':
                    v.show ? k.parentNode.replaceChild(v.comment, k)
                            : v.comment.parentNode.replaceChild(k, v.comment)
                    v.show = !v.show
                    break
                case 'show':
                    v.show ? (k.style.display = 'none')
                            : (k.style.display = 'block')
                    v.show = !v.show
                    break
                default:
                    break
            }
        }
    }
}
       


initEvent(eventPool) {
    for (let [k, v] of eventPool) {
        k.addEventListener('click', v.bind(this), false)
    }
}

boxShow1为false的情况

boxShow2为false的情况

boxShow1和boxShow2都为true的情况下,元素的情况

上面就是大体简单的实现情况。

完结,撒花.jpeg