vue中v-if和v-show使用区别源码分析

目录
  • 一、v-if
    • 1、render
    • 2、vNode
    • 3、patch
    • 小结
  • 二、v-show
    • 1、render
    • 2、vNode
    • 3、patch
      • (1)normalizeDirectives
      • (2)callHook
  • 总结

高频面试题:vue中的v-showv-if的区别?

一、v-if

例子:

new Vue({
  el: "#app",
  data() {
    return {
      isShow: false,
    };
  },
  methods: {
    changeStatus() {
      this.isShow = !this.isShow;
    }
  },
  template: `<div><button @click="changeStatus">切换</button><div v-if="isShow">显示</div></div>`
});

1、render

`with(this){
    return _c('div',[_c('button',{on:{"click":changeStatus}},[_v("切换")]),(isShow)?_c('div',[_v("显示")]):_e()])
}`

可以看出,这里通过isShow为三目运算符的判断条件,起始条件下其值为false

2、vNode

获取到的vNodev-if条件为false的情况下,获取到的是空的注释节点用来占位,包含属性isComment: truetext: ""

3、patch

当前例子中,v-iffalsepatch的过程中执行到:

else if (isTrue(vnode.isComment)) {
  vnode.elm = nodeOps.createComment(vnode.text);
  insert(parentElm, vnode.elm, refElm);
}

通过nodeOps中的方法创建注释空节点,并插入到父元素中,最终执行结果为:

小结

v-if的情况下,如果起始为false,只会生成空的注释节点用来占位,在需要考虑白屏场景下,使用v-if比较合适。

二、v-show

例子:

new Vue({
  el: "#app",
  data() {
    return {
      isShow: false,
    };
  },
  methods: {
    changeStatus() {
      this.isShow = !this.isShow;
    }
  },
  template: `<div><button @click="changeStatus">切换</button><div v-show="isShow">显示</div></div>`
});

1、render

`with(this){
    return _c('div',[_c('button',{on:{"click":changeStatus}},[_v("切换")]),_c('div',{directives:[{name:"show",rawName:"v-show",value:(isShow),expression:"isShow"}]},[_v("显示")])])
}`

可以看出,这里与v-if不同的是,里面有directives属性。

2、vNode

v-if不同的是,这里包含用于描述vNode属性的data

data: {
    directives: {
        expression: "isShow",
        name: "show",
        rawName: "v-show",
        value: false,
    }
}

3、patch

在当前例子中v-show控制的节点会执行到createElm方法中的以下逻辑:

  {
    createChildren(vnode, children, insertedVnodeQueue);
    if (isDef(data)) {
      invokeCreateHooks(vnode, insertedVnodeQueue);
    }
    insert(parentElm, vnode.elm, refElm);
  }

当执行完createChildren(vnode, children, insertedVnodeQueue)vnodeelm中包含outerHTML: "<div>显示</div>"

data存在,会执行到invokeCreateHooks

function invokeCreateHooks (vnode, insertedVnodeQueue) {
    for (let i = 0; i < cbs.create.length; ++i) {
      cbs.create[i](emptyNode, vnode)
    }
    i = vnode.data.hook // Reuse variable
    if (isDef(i)) {
      if (isDef(i.create)) i.create(emptyNode, vnode)
      if (isDef(i.insert)) insertedVnodeQueue.push(vnode)
    }
}

这里对data中的directives进行处理的方法是cbs.create中的updateDirectives

function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
  if (oldVnode.data.directives || vnode.data.directives) {
    _update(oldVnode, vnode)
  }
}
function _update (oldVnode, vnode) {
  const isCreate = oldVnode === emptyNode
  const isDestroy = vnode === emptyNode
  const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context)
  const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)
  const dirsWithInsert = []
  const dirsWithPostpatch = []
  let key, oldDir, dir
  for (key in newDirs) {
    oldDir = oldDirs[key]
    dir = newDirs[key]
    if (!oldDir) {
      // new directive, bind
      callHook(dir, 'bind', vnode, oldVnode)
      if (dir.def && dir.def.inserted) {
        dirsWithInsert.push(dir)
      }
    } else {
      // existing directive, update
      dir.oldValue = oldDir.value
      dir.oldArg = oldDir.arg
      callHook(dir, 'update', vnode, oldVnode)
      if (dir.def && dir.def.componentUpdated) {
        dirsWithPostpatch.push(dir)
      }
    }
  }
  // ...
}

这里主要做了两件事,通过normalizeDirectives获取到关于v-show的操作,通过callHook$1(dir, 'bind', vnode, oldVnode)的方式进行属性的绑定

(1)normalizeDirectives

function normalizeDirectives$1 (
  dirs,
  vm
) {
  var res = Object.create(null);
  if (!dirs) {
    // $flow-disable-line
    return res
  }
  var i, dir;
  for (i = 0; i < dirs.length; i++) {
    dir = dirs[i];
    if (!dir.modifiers) {
      // $flow-disable-line
      dir.modifiers = emptyModifiers;
    }
    res[getRawDirName(dir)] = dir;
    dir.def = resolveAsset(vm.$options, 'directives', dir.name, true);
  }
  // $flow-disable-line
  return res
}
/**
 * Resolve an asset.
 * This function is used because child instances need access
 * to assets defined in its ancestor chain.
 */
function resolveAsset (
  options,
  type,
  id,
  warnMissing
) {
  /* istanbul ignore if */
  if (typeof id !== 'string') {
    return
  }
  var assets = options[type];
  // check local registration variations first
  if (hasOwn(assets, id)) { return assets[id] }
  var camelizedId = camelize(id);
  if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }
  var PascalCaseId = capitalize(camelizedId);
  if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] }
  // fallback to prototype chain
  var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];
  if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {
    warn(
      'Failed to resolve ' + type.slice(0, -1) + ': ' + id,
      options
    );
  }
  return res
}

这里通过dir.def = resolveAsset(vm.$options, 'directives', dir.name, true)的方式去解析directives中存在的操作方法,resolveAsset方法中typedirectives,即从Vueoptions中获得directives的值为一个原型上存在modelshow方法的对象。

那么这里有个疑问,这个directives是什么时候挂载上去的呢?
答案:在源码文件platform/web/runtime/index.js有代码extend(Vue.options.directives, platformDirectives),将modelshow进行原型挂载。

通过 var res = assets[id] || assets[camelizedId] || assets[PascalCaseId]我们获得了show方法:

export default {
  bind (el: any, { value }: VNodeDirective, vnode: VNodeWithData) {
    vnode = locateNode(vnode)
    const transition = vnode.data && vnode.data.transition
    const originalDisplay = el.__vOriginalDisplay =
      el.style.display === 'none' ? '' : el.style.display
    if (value && transition) {
      vnode.data.show = true
      enter(vnode, () => {
        el.style.display = originalDisplay
      })
    } else {
      el.style.display = value ? originalDisplay : 'none'
    }
  },
  // 这里还有unbind和update方法
}

这里定义了节点样式属性display绑定bind、解绑unbind和更新update的方法。

(2)callHook

当获取到可执行的showbind方法后再看callHook(dir, 'bind', vnode, oldVnode)

function callHook (dir, hook, vnode, oldVnode, isDestroy) {
  const fn = dir.def && dir.def[hook]
  if (fn) {
    try {
      fn(vnode.elm, dir, vnode, oldVnode, isDestroy)
    } catch (e) {
      handleError(e, vnode.context, `directive ${dir.name} ${hook} hook`)
    }
  }
}

这里的fn就是show中的bind方法,最终执行到逻辑el.style.display = value ? originalDisplay : 'none',在当前例子中v-show控制的节点elm就有了属性outerHTML: "<div style=\"display: none;\">显示</div>"

总结

v-show点击切换成true时将会通过diff算法进行本地复用策略的优化,执行到v-show节点控制的节点渲染时节点key相同,采取原地复用的原则只对其属性display进行修改比从占位空注释节点变为真实节点更优,如果在transition这种频繁切换的场景中,进行v-show控制展示隐藏更合理。

v-ifv-show的使用需要根据场景,一般来说,v-if 有更高的切换开销,更多的使用在需要考虑白屏时间或者切换次数很少的场景;

而 v-show 有更高的初始渲染开销但切换开销较小,因此,如果在transition控制的动画或者需要非常频繁地切换场景,则使用 v-show 较好。

以上就是vue中v-if和v-show使用区别源码分析的详细内容,更多关于vue v-if v-show区别的资料请关注我们其它相关文章!

(0)

相关推荐

  • 在vue中使用el-tab-pane v-show/v-if无效的解决

    我就废话不多说了,大家还是直接看代码吧~ 解决方法如下: <template> <el-tabs v-model="settype" @tab-click="tabClick" ref="tabs"> <el-tab-pane label="广告位设置" name="bannerset">广告位设置</el-tab-pane> <el-tab-pane

  • Vue中v-if、v-if-else、v-else-if与v-show的基本使用

    目录 一.Vue的条件渲染 1.1.v-if 1.2.v-if-else 1.3.v-else-if  1.4.template元素  1.5.v-show 1.6.v-show和v-if的区别 总结 一.Vue的条件渲染 1.1.v-if if 顾名思义,就是如果满足一个条件后,执行下面的步骤,和我们学的基础语法是一样的,只是这里在Vue中稍微换了一下而已 v-if=“条件” if后面的内容只有在条件为true时,才会被渲染出来 <div class="info" v-if=&

  • Vue.js中v-show和v-if指令的用法介绍

    目录 一.v-show指令 二.v-if指令 三.v-show和v-if的区别 四.v-else指令 五.v-else-if 一.v-show指令 v-show指令可以用来动态的控制DOM元素的显示或隐藏.v-show后面跟的是判断条件,语法如下: v-show="判断变量" 例如: v-show="true",表示显示DOM元素. v-show="false", 表示隐藏DOM元素. 看下面的示例: <!DOCTYPE html>

  • 关于vuejs中v-if和v-show的区别及v-show不起作用问题

    1.官网概念描述 v-if 是'真正的'条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建. v-if 也是惰性的,如果在初始渲染时条件为假,那么什么都不做- - 直到条件第一次为真的时候才会开始渲染条件块,相比之下,v-show就简单得多- - 不管初始条件是什么,元素总会被渲染,并且只是简单的基于css进行切换. 一般来说,v-if 有更高的切换开销,而 v-show 有更高的出事渲染开销.因此,如果需要非常频繁的切换,那么使用v-show好一点;如果在运行时条

  • Vue的v-if和v-show的区别图文介绍

    目录 一.v-if和v-show区别 二.生命周期 三.性能的差异 一.v-if和v-show区别 ① v-show严格意义来说其实是条件隐藏,直接在页面初始化的时候将DOM(对象模型)元素也初始化,因为它就是将它所在的元素添加一个display属性为none,如果条件符合就显示.a. 文本框中无内容,默认为false,所以属性显示 b. 文本框中输入d,false变为ture,属性消除 ② v-if严格意义来说就是条件判断,符合就加载DOM(对象模型)元素,不符合就不显示. a. 文本框中无内

  • vue学习笔记之v-if和v-show的区别

    v-if vs v-show v-if 是"真正的"条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建. v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做--直到条件第一次变为真时,才会开始渲染条件块. 相比之下, v-show 就简单得多--不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换. 一般来说, v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销.因此,如果需要非常频繁地切换,则使用 v-sh

  • 详解Vue中Computed与watch的用法与区别

    目录 computed computed只接收一个getter函数 computed同时接收getter函数对象和setter函数对象 调试 Computed watchEffect 立即执行 监听基本数据类型 停止watchEffect 清理watchEffect watchPostEffect 和 watchSyncEffect watchEffect不能监听对象 watch 监听单个数据 监听多个数据(传入数组) 官方文档总结 computed watchEffect watch comp

  • Vue 中 template 有且只能一个 root的原因解析(源码分析)

    引言 今年, 疫情 并没有影响到各种面经的正常出现,可谓是络绎不绝(学不动...).然后,在前段时间也看到一个这样的关于 Vue 的问题, 为什么每个组件 template 中有且只能一个 root? 可能,大家在平常开发中,用的较多就是 template 写 html 的形式.当然,不排除用 JSX 和 render() 函数的.但是,究其本质,它们最终都会转化成 render() 函数.然后,再由 render() 函数转为 Vritual DOM (以下统称 VNode ).而 rende

  • 详谈vue中router-link和传统a链接的区别

    Vue-router是伴随着Vue框架出现的路由系统,它也是公认的一种优秀的路由解决方案.在使用Vue-router时候,我们常常会使用其自带的路径跳转组件Link,通过实现跳转,这和传统的何其相似!但它们到底有什么具体的区别呢? 官方中给出的解释是这样的: <router-link> 比起写死的 <a href="..." rel="external nofollow" rel="external nofollow" >

  • Vue中this.$router和this.$route的区别及push()方法

    官房文档里是这样说明的: 通过注入路由器,我们可以在任何组件内通过 this.$router 访问路由器,也可以通过 this.$route 访问当前路由 可以理解为: this.$router 相当于一个全局的路由器对象,包含了很多属性和对象(比如 history 对象),任何页面都可以调用其 push(), replace(), go() 等方法. this.$route 表示当前路由对象,每一个路由都会有一个 route 对象,是一个局部的对象,可以获取对应的 name, path, pa

  • vue中传参params和data的区别

    目录 1.使用data传参 2.使用params传参 3.总而言之 1.使用data传参 前端请求方式为post import request from '@/utils/request' // 新增banner export function saveBanner(data){ return request({ url:'/system/banner/saveBanner', method:'post', data:data }) } 后端接口接收 /** * 保存导航图 * * @param

  • Java编程中ArrayList源码分析

    之前看过一句话,说的特别好.有人问阅读源码有什么用?学习别人实现某个功能的设计思路,提高自己的编程水平. 是的,大家都实现一个功能,不同的人有不同的设计思路,有的人用一万行代码,有的人用五千行.有的人代码运行需要的几十秒,有的人只需要的几秒..下面进入正题了. 本文的主要内容: · 详细注释了ArrayList的实现,基于JDK 1.8 . ·迭代器SubList部分未详细解释,会放到其他源码解读里面.此处重点关注ArrayList本身实现. ·没有采用标准的注释,并适当调整了代码的缩进以方便介

  • JDK1.8中的ConcurrentHashMap源码分析

    一.容器初始化 1.源码分析 在jdk8的ConcurrentHashMap中一共有5个构造方法,这四个构造方法中都没有对内部的数组做初始化, 只是对一些变量的初始值做了处理 jdk8的ConcurrentHashMap的数组初始化是在第一次添加元素时完成 // 没有维护任何变量的操作,如果调用该方法,数组长度默认是16 public ConcurrentHashMap() { } // 传递进来一个初始容量,ConcurrentHashMap会基于这个值计算一个比这个值大的2的幂次方数作为初始

  • RxJava中map和flatMap的用法区别源码解析

    目录 前言: 作用 使用方法: map flatMap 源码分析 map flatMap 结语 前言: RxJava中提供了大量的操作符,这大大提高了了我们的开发效率.其中最基本的两个变换操作符就是map和flatMap.而其他变换操作符的原理基本与map类似. map和flatMap都是接受一个函数作为参数(Func1)并返回一个被观察者Observable Func1的< I,O >I,O模版分别为输入和输出值的类型,实现Func1的call方法对I类型进行处理后返回O类型数据,只是fla

  • 深入理解Python虚拟机中整型(int)的实现原理及源码剖析

    目录 数据结构 深入分析 PyLongObject 字段的语意 小整数池 整数的加法实现 总结 数据结构 在 cpython 内部的 int 类型的实现数据结构如下所示: typedef struct _longobject PyLongObject; struct _longobject { PyObject_VAR_HEAD digit ob_digit[1]; }; #define PyObject_VAR_HEAD PyVarObject ob_base; typedef struct

  • vue 虚拟dom的patch源码分析

    本文介绍了vue 虚拟dom的patch源码分析,分享给大家,具体如下: 源码目录:src/core/vdom/patch.js function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartIdx = 0 let newStartIdx = 0 let oldEndIdx = oldCh.length - 1 let oldStartVnode = oldCh[0]

随机推荐