v-show的作用是将表达式值转换为布尔值,根据该布尔值的真假来显示/隐藏切换元素,它是通过切换元素的display这个css属性值来实现的,例如:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Document</title><script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body><div id="d"><p v-show="isShow">Hello Vue!</p></div> <script>Vue.config.productionTip=false;Vue.config.devtools=false;var app = new Vue({el:'#d',data:{isShow:true}})</script>
</body>
</html>

渲染结果为:

当我们在修改isShow为false时:

页面里的Hello Vue!就隐藏部件了,我们查看DOM结构如下:

可以看到Vue是通过修改display这个CSS属性来隐藏元素的

源码分析


在解析模板将DOM转换成AST对象的时候会执行processAttrs()函数,如下:

function processAttrs (el) {                     //解析Vue的属性var list = el.attrsList; var i, l, name, rawName, value, modifiers, isProp;for (i = 0, l = list.length; i < l; i++) {             //遍历每个属性 name = rawName = list[i].name;value = list[i].value;if (dirRE.test(name)) {                                 //如果该属性以v-、@或:开头,表示这是Vue内部指令// mark element as dynamicel.hasBindings = true;// modifiersmodifiers = parseModifiers(name);if (modifiers) {name = name.replace(modifierRE, '');}if (bindRE.test(name)) { // v-bind                          //bindRD等于/^:|^v-bind:/ ,即该属性是v-bind指令时/*v-bind的分支*/} else if (onRE.test(name)) { // v-on/*v-on的分支*/} else { // normal directivesname = name.replace(dirRE, '');                         //去掉指令前缀,比如v-show执行后等于show// parse argvar argMatch = name.match(argRE);var arg = argMatch && argMatch[1];if (arg) {name = name.slice(0, -(arg.length + 1));}addDirective(el, name, rawName, value, arg, modifiers); //执行addDirective给el增加一个directives属性if ("development" !== 'production' && name === 'model') {checkForAliasModel(el, value);}}} else {/*非Vue指令的分支*/}}
}

addDirective会给AST对象上增加一个directives属性保存指令信息,如下:

function addDirective (                         //第6561行 指令相关,给el这个AST对象增加一个directives属性,值为该指令的信息
  el,name,rawName,value,arg,modifiers
) {(el.directives || (el.directives = [])).push({ name: name, rawName: rawName, value: value, arg: arg, modifiers: modifiers });el.plain = false;
}

例子里的p元素执行到这里时对应的AST对象如下:

接下来在generate生成rendre函数的时候,会执行genDirectives()函数,将AST转换成一个render函数,如下:

with(this){return _c('div',{attrs:{"id":"d"}},[_c('p',{directives:[{name:"show",rawName:"v-show",value:(isShow),expression:"isShow"}]},[_v("Hello Vue!")])])}

最后等渲染完成后会执行directives模块的create钩子函数,如下:

var directives = {                 //第6173行 directives模块 create: updateDirectives,             //创建DOM后的钩子
  update: updateDirectives,destroy: function unbindDirectives (vnode) {updateDirectives(vnode, emptyNode);}
}function updateDirectives (oldVnode, vnode) {         //第6181行   oldVnode:旧的Vnode,更新时才有 vnode:新的VNodeif (oldVnode.data.directives || vnode.data.directives) {_update(oldVnode, vnode);}
}function _update (oldVnode, vnode) {                 //第6187行 初始化/更新指令var isCreate = oldVnode === emptyNode;                                                     //是否为初始化var isDestroy = vnode === emptyNode;var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context);          var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context);                 //调用normalizeDirectives$1()函数规范化参数1,返回格式:{v-show:{name: "show", rawName: "v-show", value: true, expression: "ok", modifiers: {…}, …}}var dirsWithInsert = [];var dirsWithPostpatch = [];var key, oldDir, dir;for (key in newDirs) {                                     //遍历newDirsoldDir = oldDirs[key];                                         //oldVnode上的key指令信息dir = newDirs[key];                                            //vnode上的key指令信息if (!oldDir) {                                                 //如果oldDir不存在,即是新增指令// new directive, bindcallHook$1(dir, 'bind', vnode, oldVnode);                     //调用callHook$1()函数,参数2为bind,即执行v-show指令的bind函数if (dir.def && dir.def.inserted) {dirsWithInsert.push(dir);}} else {// existing directive, updatedir.oldValue = oldDir.value;callHook$1(dir, 'update', vnode, oldVnode);if (dir.def && dir.def.componentUpdated) {dirsWithPostpatch.push(dir);}}}/*以下略*/
}

normalizeDirectives$1会调用resolveAsset()函数从Vue.options.directives里获取v-show指令的信息如下:

function normalizeDirectives$1 (         //第6249行      规范化dirs
  dirs,vm
) {var res = Object.create(null);             //存储最后的结果if (!dirs) {                                 //如果用户没有定义指令,则直接返回空对象// $flow-disable-linereturn res}var i, dir;for (i = 0; i < dirs.length; i++) {             ///遍历dirsdir = dirs[i];if (!dir.modifiers) {                                                     //如果没有修饰符,则重置dir.modifiers为空对象// $flow-disable-linedir.modifiers = emptyModifiers;}           res[getRawDirName(dir)] = dir;                                             //将dir保存到res里面,键名为原始的指令名dir.def = resolveAsset(vm.$options, 'directives', dir.name, true);         //调用resolveAsset获取该指令的信息,是一个对象,保存到res的def属性里面
  }// $flow-disable-linereturn res
}

resolveAsset是获取资源用的,当我们定义了组件、过滤器、指令时,都通过该函数获取对应的信息,之前组件和过滤里介绍了,这里不说了

回到_update函数,最后调用callHook$1()函数,参数2为bind,该函数如下:

function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) {         //第6276行 执行指令的某个回调函数 dir:指令信息,var fn = dir.def && dir.def[hook];                                     //尝试获取钩子函数if (fn) {try {fn(vnode.elm, dir, vnode, oldVnode, isDestroy);                         //执行钩子函数,参数依次为绑定的元素、dir对象、新的VNode,老的VNode} catch (e) {handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook"));}}
}

v-show指令的信息如下:

var show = {                 //第8082行 v-show指令的信息bind: function bind (el, ref, vnode) {         //初次绑定时执行var value = ref.value;vnode = locateNode(vnode);var transition$$1 = vnode.data && vnode.data.transition;         //尝试获取transition,如果v-show绑定的标签外层套了一个transition则会把信息保存到该对象里 这是transition的组件分支,可先忽略 var originalDisplay = el.__vOriginalDisplay =                      //保存最初的display属性el.style.display === 'none' ? '' : el.style.display;if (value && transition$$1) {                                     //如果transition$$1存在的话vnode.data.show = true;enter(vnode, function () {el.style.display = originalDisplay;});} else {el.style.display = value ? originalDisplay : 'none';             //否则直接根据value的值是否可以转换为1来设置el.style.display属性
    }},update: function update (el, ref, vnode) {/*更新时的逻辑*/},unbind: function unbind (el,binding,vnode,oldVnode,isDestroy) {/*卸载时的逻辑*/}
}

v-show的流程就是这样的,注意,v-show不支持<template>元素,也不支持v-else。

转载于:https://www.cnblogs.com/greatdesert/p/11157771.html

Vue.js 源码分析(二十三) 指令篇 v-show指令详解相关推荐

  1. 【投屏】Scrcpy源码分析二(Client篇-连接阶段)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  2. Vue.js 源码分析(九) 基础篇 生命周期详解

    先来看看官网的介绍: 主要有八个生命周期,分别是: beforeCreate.created.beforeMount.mounted.beforeupdate.updated   .beforeDes ...

  3. Vue.js 源码分析(五) 基础篇 方法 methods属性详解

    methods中定义了Vue实例的方法,官网是这样介绍的: 例如:: <!DOCTYPE html> <html lang="en"> <head&g ...

  4. 【转】ABP源码分析二十三:Authorization

    Permission:用于定义一个Permission,一个permission可以包含多个子Permission. PermissionDictionary:继承自Dictionary<str ...

  5. 【Vue.js源码解析 二】-- 虚拟 DOM

    前言 笔记来源:拉勾教育 大前端高薪训练营 阅读建议:建议通过左侧导航栏进行阅读 虚拟 DOM 基本介绍 什么是虚拟 DOM 虚拟 DOM(Virtual DOM) 是使用 JavaScript 对象 ...

  6. Vue.js 源码分析—— Slots 是如何实现的

    今天主要分析 Vue.js 中常用的 Slots 功能是如何设计和实现的.本文将分为普通插槽.作用域插槽以及 Vue.js 2.6.x 版本的 v-slot 语法三部分进行讨论. 本文属于进阶内容,如 ...

  7. Spring5源码分析系列(九)Spring事务原理详解

    终于等到了B站的薪资沟通电话,美滋滋,本节开始进入Spring数据访问篇,讲解spring事务,文章参考自Tom老师视频. 事务基本概念 事务(Transaction)是访问并可能更新数据库中各种数据 ...

  8. 【投屏】Scrcpy源码分析三(Client篇-投屏阶段)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

  9. 【投屏】Scrcpy源码分析一(编译篇)

    Scrcpy源码分析系列 [投屏]Scrcpy源码分析一(编译篇) [投屏]Scrcpy源码分析二(Client篇-连接阶段) [投屏]Scrcpy源码分析三(Client篇-投屏阶段) [投屏]Sc ...

最新文章

  1. Python中dict用法详解
  2. Mysql中文乱码问题解决
  3. Rinne Loves Xor
  4. redis-java客户端jedis测试
  5. linux添加windows网络打印机,Linux Mint如何添加windows分享的网络打印机?
  6. 程序逻辑上多一些提示
  7. 20 年后,我们怎么看电影?
  8. Java中的堆分配参数总结《对Java的分析总结》(二)
  9. pythonsupermro_Python高级编程之继承问题详解(super与mro)
  10. gc --JNI调用引发的长gc问题
  11. enter 默认搜索
  12. gen阻抗 pcie_COM载板设计之一: PCB的设计
  13. ddrescue重建损坏磁盘
  14. Vue3:集成wangEditor富文本编辑器
  15. 联想笔记本linux驱动,联想为Linux驱动的计算机提供自动固件更新
  16. PowerPCB转Protel 99的详细教程
  17. 含有一般疑问句的歌_一般疑问句,特殊疑问句和否定句
  18. pinbox: 我用过最好的收藏工具
  19. 分布式系统中的幂等性(客户端与服务端的交易一致性,避免多次扣款)
  20. mysqlbinlog 加-v -vv 的区别

热门文章

  1. 1. golang 语言环境安装
  2. 《Raspberry Pi用户指南》——导读
  3. Linux 按键定时器去抖
  4. Linux下基于官方源代码RPM包构建自定义MySQL RPM包
  5. pjsip学习笔记二
  6. 用一句JQuery代码实现表格的简单筛选
  7. ASP.NET2.0的multiview和wizard控件
  8. Android投票列表设计,AndroidCustomView一个简单的投票排名对比图
  9. Python的if __name__ == ‘__main__‘:的作用
  10. 脑细胞膜等效神经网路12分类实例