侧边栏壁纸
博主头像
woku博主等级

成功的路上并不拥挤

  • 累计撰写 50 篇文章
  • 累计创建 13 个标签
  • 累计收到 3 条评论
vue

v-show/v-if认识与实现

woku
2022-03-07 / 0 评论 / 1 点赞 / 97 阅读 / 5,010 字

v-show与v-if的共同点

  • vue内置指令

  • 都是条件渲染,作用效果都是相同的,都是控制页面上的元素是否显示与隐藏

    当值为true时, v-show,v-if控制的元素显示

    当值为false时,v-show,v-if控制的元素隐藏

  • 用法相同

 v-if="指令表达式"、v-show="指令表达式”

<Model v-show="isShow" />
<Model v-if="isShow" />

v-show与v-if的区别

指令控制手段编译手段
v-show给元素添加css--display:none,dom存在基于css样式切换
v-if整个元素的添加与移除,真dom添加,假dom移除(惰性渲染)组件的重建与卸载(值由false变为true时,触发组件的beforeCreated,created,beforeMount,mounted钩子,由true变为false,触发组件的beforeDestorydestoryed方法)

性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;

基本使用

   <script>
        const rootComp = {
            data() {
                return {
                    isShowBoxA: false,
                    isShowBoxB: false
                }
            },
            template: `
               <div>
                  <p v-if="isShowBoxA">盒子A</p>
                  <p v-show="isShowBoxB">盒子B</p>
               </div>
               <button @click="boxAHandle">操作盒子A</button>
               <button @click="boxBHandle">操作盒子B</button>
            `,
            methods: {
                boxAHandle() {
                    this.isShowBoxA = !this.isShowBoxA
                },
                boxBHandle() {
                    this.isShowBoxB = !this.isShowBoxB
                }
            }
        }
        const app = Vue.createApp(rootComp)
        const vm = app.mount('#app')
    </script>

和v-if一组的还有v-else-if、v-else
用法和if、else-if、else一样
v-if、v-else-if、v-else 在结构上必须紧挨在一起

<div v-if="">---</div>
<div v-else-if="">---</div>
<div v-else="">---</div>

不能下面这样用:

<div v-if="">---</div>
<di>---</div>
<div v-else-if="">---</div>
<div v-else="">---</div>

简单实现v-show/v-if

index.html

...
 <script src="./module/index.js"></script>
...
<body>
    <div id="app"></div>
    <script>
        const vm = new Vue({
            el: '#app',
            data() {
                return {
                    isShowBoxA: false,
                    isShowBoxB: false
                }
            },
            template: `
               <div>
                  <p v-if="isShowBoxA">盒子A</p>
                  <p v-show="isShowBoxB">盒子B</p>
               </div>
               <button @click="boxAHandle">A</button>
               <button @click="boxBHandle">B</button>
            
            `,
            beforeCreated() {
                console.log('beforeCreated')
            },
            created() {
                console.log('created')
            },
            beforeMount() {
                console.log('beforeMount')
            },
            mounted() {
                this.isShowBoxA = true
                console.log('mounted')
            },
            methods: {
                boxAHandle() {
                    this.isShowBoxA = !this.isShowBoxA
                },
                boxBHandle() {
                    this.isShowBoxB = !this.isShowBoxB
                }
            }
        })
    </script>
</body>

module/index.js

const Vue = (function () {
    function vue(options) {
        // 生命周期函数,特定的时期执行
        // 使用bind来绑定this指向
        var recycle = {
            beforeCreated: options.beforeCreated.bind(this),
            created: options.created.bind(this),
            beforeMount: options.beforeMount.bind(this),
            mounted: options.mounted.bind(this)
        }
        recycle.beforeCreated()
        this.$el = document.querySelector(options.el)
        this.$data = options.data()
        _init(this, options.methods, options.template, recycle)
    }

    function _init(vm, methods, template, recycle) {
        // 模拟获取dom节点
        var container = document.createElement('div')
        container.innerHTML = template
        // showPool: dom节点和数据的一个绑定关系map
        // eventPool: 事件函数和dom节点的一个绑定关系map
        var showPool = new Map()
        var eventPool = new Map()
        // 数据劫持  vm.a -> vm.$data.a
        initData(vm, showPool)
        recycle.created()
        initPool(container, methods, showPool, eventPool)
        // 根据eventPool循环绑定事件处理函数
        bindEvent(vm, eventPool)
        // render v-if 替换  v-show  style属性变化
        render(vm, showPool, container, recycle)
    }

    function initData(vm, showPool) {
        var _data = vm.$data
        for (var key in _data) {
            (function (key) {
                Object.defineProperty(vm, key, {
                    get() {
                        return _data[key]
                    },
                    set(newVal) {
                        _data[key] = newVal
                        update(vm, key, showPool)
                    }
                })
            })(key)
        }
    }

    function initPool(container, methods, showPool, eventPool) {
        var _allNodeEl = container.getElementsByTagName('*')
        var dom
        for (var i = 0; i < _allNodeEl.length; i++) {
            dom = _allNodeEl[i]
            var vIfAttr = dom.getAttribute('v-if')
            var vShowAttr = dom.getAttribute('v-show')
            var vEvent = dom.getAttribute('@click')
            if (vIfAttr) {
                showPool.set(dom, {
                    type: 'if',
                    prop: vIfAttr
                })
                dom.removeAttribute('v-if')
            } else if (vShowAttr) {
                showPool.set(dom, {
                    type: 'show',
                    prop: vShowAttr
                })
                dom.removeAttribute('v-show')
            }
            if (vEvent) {
                eventPool.set(dom, methods[vEvent])
                dom.removeAttribute('@click')
            }
        }
        console.log(container.innerHTML)
    }

    function bindEvent(vm, eventPool) {
        for (var [dom, handler] of eventPool) {
            vm[handler.name] = handler
            dom.addEventListener('click', vm[handler.name].bind(vm), false)
        }
    }

    function render(vm, showPool, container, recycle) {
        var _data = vm.$data
        var _el = vm.$el
        for (var [dom, info] of showPool) {
            switch (info.type) {
                case 'if':
                    info.comment = document.createComment(['v-if']) 
                    !_data[info.prop] && (dom.parentNode.replaceChild(info.comment, dom))
                    break;
                case 'show':
                    !_data[info.prop] && (dom.style.display = 'none')
                    break;
                default:
                    break;

            }
        }
        recycle.beforeMount()
        _el.appendChild(container)
        recycle.mounted()

    }

    function update(vm, key, showPool) {
        var _data = vm.$data
        for ([dom, info] of showPool) {
            if (info.prop === key) {
                switch (info.type) {
                    case 'if':
                        !_data[key] ? (dom.parentNode.replaceChild(info.comment, dom)) :
                        (info.comment.parentNode.replaceChild(dom, info.comment))
                        break;
                    case 'show':
                        !_data[key] ? (dom.style.display = 'none') : ( dom.removeAttribute('style'))
                        break;
                    default:
                        break;
                }
            }
        }
    }
    return vue
})()

window.Vue = Vue

主要使用的:

  • 数据劫持(Object.defineProperty)
  • showPool和eventPool两个map结构来存储dom节点和数据之间的关系
  • dom事件绑定
  • v-if的真假,用注释节点和当前节点进行替换(注释节点存储在showPool中对应的dom键下)
  • v-show的真假,通过控制当前dom节点的css style属性
  • render页面
  • 数据改变时,update方法重新将对应的dom节点渲染

v-show与v-if的使用场景

需要经常切换,使用v-show更好,如果使用v-if则需要频繁的进行渲染和卸载

如果在运行时条件很少改变,则使用 v-if 较好

1

评论区