Vue2里的v-if怎么用?和v-show有啥区别?还有哪些细节要注意?
在Vue2开发里,v-if是高频使用的指令之一,不管是做权限控制、条件渲染页面模块,还是处理交互逻辑,都离不开它,但新手常纠结“v-if咋用才对?和v-show选哪个?嵌套、和v-for一起用有啥坑?”这些问题,今天咱就把Vue2中v-if的关键知识点拆成一个个问题,用大白话讲透!
v-if最基础的用法是啥?
v-if是Vue的条件渲染指令,核心逻辑是“条件满足就把元素(或组件)渲染到DOM里,不满足就销毁对应的DOM”。
举个最直观的例子:做用户登录状态判断,假设data里有个isLogin
属性,值为true
表示已登录,false
是未登录,代码可以这么写:
<div v-if="isLogin">欢迎回来,{{ username }}</div> <button v-else>点击登录</button>
这里注意两点:
- 第一,v-if后面跟JS表达式,像
isLogin
这种布尔值可以用,也能写复杂判断(比如v-if="user.role === 'admin'"
判断用户角色); - 第二,能配合
v-else-if
、v-else
使用,但得相邻且结构连续,<div v-if="type === 'A'">显示A类型内容</div> <div v-else-if="type === 'B'">显示B类型内容</div> <div v-else>默认内容</div>
要是想同时控制多个元素的显示隐藏,不用给每个元素都加v-if,用<template>
当“容器”更方便,比如表单里的手机号、验证码输入框,只有用户选了“手机登录”才显示:
<template v-if="loginType === 'phone'"> <input type="text" placeholder="手机号"> <input type="text" placeholder="验证码"> </template>
这里<template>
不会被渲染成真实DOM节点,只负责分组逻辑,很适合批量控制元素~
v-if和v-show核心区别是啥?该咋选?
很多人刚学Vue时,总搞不清这俩指令,其实核心差异在DOM的渲染机制和性能开销上。
先看渲染机制:
v-if是“动态增删DOM”——条件不满足时,对应的DOM会被完全销毁;条件满足时,重新创建DOM。
v-show是“切换CSS的display属性”——不管条件是否满足,DOM一开始就存在,只是用display: none
把它藏起来。
再聊性能开销:
初始渲染时,v-show因为DOM已经存在,只是隐藏,所以比v-if(要判断条件、创建/销毁DOM)更快;但切换的时候,v-show只是改CSS,开销很小;v-if每次切换都要销毁/重建DOM,开销更大。
所以场景选择很明确:
- 如果模块切换特别频繁(比如tab栏切换、弹窗频繁显示隐藏),选v-show,减少DOM操作开销;
- 如果模块默认不显示,且切换次数少(比如权限控制的管理员菜单、首屏隐藏的广告位),选v-if,因为初始渲染时不加载多余DOM,节省首屏性能;
- 还有个细节:v-show不能用在
<template>
上,也不支持v-else
系列,这点和v-if不一样~
v-if在上用要注意啥?
前面提过用<template>
分组控制多个元素,这里得强调几个细节:
- 第一,
<template>
只是逻辑容器,最终不会出现在渲染后的HTML里,比如上面手机登录的例子,页面源码里不会有<template>
标签,只有里面的input
框; - 第二,
<template>
上的v-if不能和v-for同时用(官方明确不建议,后面讲v-for和v-if冲突时会细说); - 第三,嵌套
<template>
要小心作用域,比如外层<template v-if="A">
里套内层<template v-if="B">
,内层的条件B
是基于外层A
为true
时才会判断的,相当于逻辑上的“且”关系(A && B
);
举个实际场景:电商页面里,只有用户登录(isLogin
为true
)且是VIP用户(isVIP
为true
),才显示专属优惠模块:
<template v-if="isLogin"> <template v-if="isVIP"> <div class="vip-discount">VIP专属9折!</div> </template> </template>
这种嵌套能让逻辑更分层,但别嵌套太深,否则代码可读性变差,后期维护麻烦~
v-if的作用域和数据响应式咋配合?
新手容易疑惑:v-if里的变量是哪里来的?数据变化时v-if咋自动更新?
v-if的表达式访问的是当前组件实例的数据,包括data
、computed
、methods
,比如用computed
处理复杂条件:
<script> export default { data() { return { user: { age: 20, level: 3 } } }, computed: { canAccessAdvanced() { return this.user.age > 18 && this.user.level >= 2; } } } </script> <template> <div v-if="canAccessAdvanced">高级功能入口</div> </template>
Vue的响应式系统会监听v-if依赖的数据,比如user.age
或者user.level
变化时,canAccessAdvanced
会重新计算,v-if也会自动判断是否重新渲染DOM。
但有个易错点:如果给v-if的变量赋值时,不小心写成赋值语句(比如v-if="isShow = true"
),这会把isShow
改成true
,还会导致条件永远为true
(因为赋值表达式返回的是赋值后的值),所以一定要注意是判断(、)还是赋值()!
v-if和v-for一起用为啥是大坑?咋避?
官方文档明确说“避免同时使用v-for和v-if”,因为Vue的指令解析顺序是v-for优先级比v-if高,啥意思?比如这样写:
<div v-for="item in list" v-if="item.isValid">{{ item.name }}</div>
实际执行逻辑是:先遍历list
里的每一个item
,然后对每个item
判断item.isValid
是否为true
,这就会导致每次渲染都要遍历整个列表,哪怕大部分item
都不满足isValid
,性能浪费特别严重!
那咋解决?分两种场景:
过滤列表项(只显示list中满足条件的item)
这时候别在DOM上写v-if,而是用computed先把列表过滤好,再用v-for渲染过滤后的结果。
<script> export default { data() { return { list: [/* 原始数据 */] } }, computed: { validList() { return this.list.filter(item => item.isValid); } } } </script> <template> <div v-for="item in validList">{{ item.name }}</div> </template>
判断整个列表是否渲染(比如list为空时不显示列表)
这时候要把v-if放在外层容器上,比如用<template>
包裹v-for:
<template v-if="list.length > 0"> <div v-for="item in list">{{ item.name }}</div> </template>
这样先判断list
有没有数据,有数据再遍历,就避免了无效遍历~
v-if在组件里用有啥特殊点?
当v-if用来控制组件的显示隐藏时,得注意组件的生命周期和数据状态。
生命周期角度
v-if为false
时,组件对应的DOM会被销毁,组件实例也会被销毁;v-if为true
时,组件会重新创建实例,执行created
、mounted
等生命周期钩子,所以可以利用这点做“初始化逻辑”,比如组件显示时请求数据,隐藏时清空定时器。
数据状态角度
如果组件里的data
是组件自身维护的(比如表单组件的输入值),那么v-if从false
切回true
时,组件会重新实例化,data
也会重置为初始值,举个例子:
<script> export default { name: 'LoginForm', data() { return { username: '', password: '' } } } </script> <template> <div v-if="showForm"> <input v-model="username" placeholder="用户名"> <input v-model="password" placeholder="密码"> </div> </template>
当showForm
从false
变true
时,username
和password
会回到空字符串,因为组件重新创建了。
但如果数据是父组件通过props传递的,那组件销毁后再创建时,props
值由父组件决定,如果父组件数据没变化,子组件props
也会保持父组件的数据~
要是想保留组件状态(比如切换tab时,表单内容不想重置),可以用<keep-alive>
包裹v-if控制的组件,这样组件实例不会被销毁,只是被缓存起来~
实际项目中v-if常见错误有哪些?咋排查?
开发时碰到v-if不生效、逻辑不对的情况,大概率是踩了这些坑:
条件表达式写错
比如把v-if="isShow == true"
写成v-if="isShow = true"
(赋值而非判断),导致isShow
被强制改成true
,条件永远成立;或者把写成,在数据类型不一致时逻辑出错(比如isShow
是'false'
字符串,用会被当成true
),排查时先看控制台有没有语法错误,再检查表达式里的符号。
作用域混淆
比如在子组件里用v-if,依赖父组件传的props
,但父组件数据更新后,子组件没反应,这时候要检查:子组件的props
是否正确声明?v-if的条件是否真的依赖了props
?因为Vue的响应式是基于依赖收集的,要是条件里没用到props
,就算props
变了,v-if也不会更新。
DOM操作时机不对
比如在mounted
钩子中获取v-if控制的DOM元素,结果元素还没渲染(因为v-if条件不满足),这时候要把DOM操作放到$nextTick
里,确保DOM更新后再执行:
mounted() { this.$nextTick(() => { const el = this.$el.querySelector('.target-el'); // 确保v-if为true时元素存在 }); }
v-if和样式指令冲突
比如想给v-if的元素加动态class,又用v-if控制显示,要理清逻辑:是先判断显示,再加class;还是加class后再判断显示?比如别写成<div v-if="isShow" :class="{ active: isActive }">
,结果isActive
变化时,因为v-if为false
,class也不会更新,要确保样式逻辑和显示逻辑的依赖关系正确~
v-if性能优化有哪些实用思路?
项目复杂后,v-if太多或条件太复杂,容易拖慢渲染速度,分享几个优化技巧:
用computed缓存条件结果
如果v-if的条件涉及多次计算(比如遍历数组、判断多个属性),把条件逻辑放到computed
里,避免每次渲染都重复计算,比如前面讲的canAccessAdvanced
,用computed
缓存后,只要依赖数据不变,就不会重复计算。
拆分复杂v-if为组件
如果一个v-if里嵌套了很多元素和逻辑,把这部分拆成单独的子组件,用v-if控制子组件的显示,这样代码更模块化,也能利用组件的缓存(比如keep-alive
)优化性能。
替换成v-show(看场景)
如果发现某个模块切换特别频繁,原本用v-if导致卡顿,换成v-show能减少DOM销毁/创建的开销,但要注意初始渲染时的DOM加载问题~
用keep-alive缓存组件
对于v-if控制的多状态组件(比如tab页里的每个面板),用<keep-alive>
包裹后,组件实例不会被销毁,切换时直接从缓存读取,节省初始化时间。
<keep-alive> <component v-if="activeTab === 'tab1'" is="Tab1"></component> <component v-else-if="activeTab === 'tab2'" is="Tab2"></component> </keep-alive>
写到这,Vue2中v-if的核心知识点基本覆盖了,从基础用法到和其他指令的区别,从组件场景到性能优化,再到踩坑排查,这些内容都是项目里高频遇到的,v-if不是越用越多越好,得结合场景选对指令,理清逻辑依赖,才能写出既流畅又高性能的代码~要是你还有其他关于v-if的疑问,或者想聊聊项目里的具体场景,评论区随时唠!
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。