Vue源码阅读(21):v-show 指令的源码解析

 我的开源库:

v-show 和 v-if 虽然在页面上的效果差不多,但是他们底层的实现却完全不一样。v-if 是在模板编译阶段实现功能的,通过 v-if 指令可以控制目标节点是否存在于真实的DOM中,v-if 的源码解析可以看我之前写的这篇文章。而 v-show 的本质是 Vue 中内置的一个自定义指令,借助于 v-show 我们可以控制目标节点 style 中的 display 属性值,关于自定义指令的源码解析可以看我的这篇文章。接下来我们开始 v-show 的源码解析。

v-show 内置自定义指令定义在 src/platforms/web/runtime/directives/show.js,我们直接看源码,内容并不多,详细的解析都在注释中,看注释即可理解。

/* @flow */

import { enter, leave } from '../modules/transition'

export default {
  // v-show 第一次被绑定到元素上时调用(oldVNode 没有使用 v-show 指令,而 vnode 使用了 v-show 指令)
  bind (el: any, { value }: VNodeDirective, vnode: VNodeWithData) {
    // value 参数是 v-show 后面表达式当前的值
    vnode = locateNode(vnode)
    const transition = vnode.data && vnode.data.transition
    // 获取在 el 元素上的原始 style.display 属性值,这主要是为了应对用户在元素上设置了 display 属性,
    // 并且设置的属性值不是 'none' 的情况,例如设置了 block,当 v-show 为 true 的话,需要将 el.style.display 设置为 block。
    //
    // 需要特别注意的是,如果 el 元素上的原始 style.display 是 'none' 的话,则视为 '' 空字符串
    // 这是因为当 v-show 为 true 的话,vue 会将 el.style.display 设置为 originalDisplay,如果不将 'none' 视为 '' 的话,
    // 会出现 v-show 为 true,而 el.style.display 的值为 'none' 的情况,这是完全本末倒置的。
    const originalDisplay = el.__vOriginalDisplay =
      el.style.display === 'none' ? '' : el.style.display
    // 如果绑定了 v-show 指令的元素外层套了一层 transition 的话,则进入该分支
    // 该情况先不讨论,等后续分析 transition 源码的时候再回来说这部分内容
    if (value && transition) {
      vnode.data.show = true
      enter(vnode, () => {
        el.style.display = originalDisplay
      })
    } else {
      // 如果 el 外层没有嵌套 transition 的话,则进入该分支
      //
      // 如果 v-show 后面的表达式值为 true 的话,则 el.style.display = originalDisplay;
      // 如果 v-show 后面的表达式值为 false 的话,则 el.style.display = 'none';
      el.style.display = value ? originalDisplay : 'none'
    }
  },
  // 组件重新渲染时,如果进行对比的 vnode 和 oldVNode 都使用了 v-show 指令的话,触发执行 update
  update (el: any, { value, oldValue }: VNodeDirective, vnode: VNodeWithData) {
    // 如果 v-show 后面表达式的新值和旧值一样的话,则不需要进行任何处理,直接 return 即可
    if (value === oldValue) return
    vnode = locateNode(vnode)
    const transition = vnode.data && vnode.data.transition
    // 该分支先不讨论,等后续分析 transition 源码的时候再回来说这部分内容
    if (transition) {
      vnode.data.show = true
      if (value) {
        enter(vnode, () => {
          el.style.display = el.__vOriginalDisplay
        })
      } else {
        leave(vnode, () => {
          el.style.display = 'none'
        })
      }
    } else {
      // 如果 el 外层没有嵌套 transition 的话,则进入该分支
      // 这部分的逻辑和上面 bind 中的是一样的:
      // 如果 v-show 后面的表达式值为 true 的话,则 el.style.display = el.__vOriginalDisplay;
      // 如果 v-show 后面的表达式值为 false 的话,则 el.style.display = 'none';
      el.style.display = value ? el.__vOriginalDisplay : 'none'
    }
  },
  // v-show 指令和元素解绑时调用(oldVNode 使用了 v-show 指令,而 vnode 没有使用 v-show 指令)
  unbind (
    el: any,
    binding: VNodeDirective,
    vnode: VNodeWithData,
    oldVnode: VNodeWithData,
    isDestroy: boolean
  ) {
    if (!isDestroy) {
      // 如果当前的元素没有被销毁的话,则将 el.style.display 设置为 style.display 初始值。
      el.style.display = el.__vOriginalDisplay
    }
    // 如果当前元素已经被销毁了的话,就不用做任何操作
  }
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值