vue中v-if和v-show使用區(qū)別源碼分析
高頻面試題:vue中的v-show和v-if的區(qū)別?
一、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為三目運(yùn)算符的判斷條件,起始條件下其值為false。
2、vNode

獲取到的vNode在v-if條件為false的情況下,獲取到的是空的注釋節(jié)點(diǎn)用來占位,包含屬性isComment: true和text: ""。
3、patch
當(dāng)前例子中,v-if為false,patch的過程中執(zhí)行到:
else if (isTrue(vnode.isComment)) {
vnode.elm = nodeOps.createComment(vnode.text);
insert(parentElm, vnode.elm, refElm);
}
通過nodeOps中的方法創(chuàng)建注釋空節(jié)點(diǎn),并插入到父元素中,最終執(zhí)行結(jié)果為:

小結(jié)
在v-if的情況下,如果起始為false,只會(huì)生成空的注釋節(jié)點(diǎn)用來占位,在需要考慮白屏場景下,使用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
在當(dāng)前例子中v-show控制的節(jié)點(diǎn)會(huì)執(zhí)行到createElm方法中的以下邏輯:
{
createChildren(vnode, children, insertedVnodeQueue);
if (isDef(data)) {
invokeCreateHooks(vnode, insertedVnodeQueue);
}
insert(parentElm, vnode.elm, refElm);
}
當(dāng)執(zhí)行完createChildren(vnode, children, insertedVnodeQueue)后vnode中elm中包含outerHTML: "<div>顯示</div>"。
data存在,會(huì)執(zhí)行到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)
}
}
這里對(duì)data中的directives進(jìn)行處理的方法是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獲取到關(guān)于v-show的操作,通過callHook$1(dir, 'bind', vnode, oldVnode)的方式進(jìn)行屬性的綁定
(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方法中type為directives,即從Vue的options中獲得directives的值為一個(gè)原型上存在model和show方法的對(duì)象。
那么這里有個(gè)疑問,這個(gè)directives是什么時(shí)候掛載上去的呢?
答案:在源碼文件platform/web/runtime/index.js有代碼extend(Vue.options.directives, platformDirectives),將model和show進(jìn)行原型掛載。
通過 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方法
}
這里定義了節(jié)點(diǎn)樣式屬性display綁定bind、解綁unbind和更新update的方法。
(2)callHook
當(dāng)獲取到可執(zhí)行的show中bind方法后再看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方法,最終執(zhí)行到邏輯el.style.display = value ? originalDisplay : 'none',在當(dāng)前例子中v-show控制的節(jié)點(diǎn)elm就有了屬性outerHTML: "<div style=\"display: none;\">顯示</div>"。
總結(jié)
當(dāng)v-show點(diǎn)擊切換成true時(shí)將會(huì)通過diff算法進(jìn)行本地復(fù)用策略的優(yōu)化,執(zhí)行到v-show節(jié)點(diǎn)控制的節(jié)點(diǎn)渲染時(shí)節(jié)點(diǎn)key相同,采取原地復(fù)用的原則只對(duì)其屬性display進(jìn)行修改比從占位空注釋節(jié)點(diǎn)變?yōu)檎鎸?shí)節(jié)點(diǎn)更優(yōu),如果在transition這種頻繁切換的場景中,進(jìn)行v-show控制展示隱藏更合理。
v-if和v-show的使用需要根據(jù)場景,一般來說,v-if 有更高的切換開銷,更多的使用在需要考慮白屏?xí)r間或者切換次數(shù)很少的場景;
而 v-show 有更高的初始渲染開銷但切換開銷較小,因此,如果在transition控制的動(dòng)畫或者需要非常頻繁地切換場景,則使用 v-show 較好。
以上就是vue中v-if和v-show使用區(qū)別源碼分析的詳細(xì)內(nèi)容,更多關(guān)于vue v-if v-show區(qū)別的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Vue?websocket封裝實(shí)現(xiàn)方法詳解
項(xiàng)目中需要使用websocke,這個(gè)是我自己修修改改好多次之后用得最順手的一版,分享一下。這個(gè)封裝需要搭配vuex使用,因?yàn)槭盏降臄?shù)據(jù)都保存在vuex中了,真的絕絕子好用,解決了我一大堆問題2022-09-09
vue 實(shí)現(xiàn)可拖曳的樹狀結(jié)構(gòu)圖
這篇文章主要介紹了vue 實(shí)現(xiàn)可拖曳的樹狀結(jié)構(gòu)圖,幫助大家更好的理解和學(xué)習(xí)使用vue框架,感興趣的朋友可以了解下2021-04-04
vue2+element-ui使用vue-i18n進(jìn)行國際化的多語言/國際化詳細(xì)教程
這篇文章主要給大家介紹了關(guān)于vue2+element-ui使用vue-i18n進(jìn)行國際化的多語言/國際化的相關(guān)資料,I18n是Vue.js的國際化插件,項(xiàng)目里面的中英文等多語言切換會(huì)使用到這個(gè)東西,需要的朋友可以參考下2023-12-12
vue實(shí)現(xiàn)移動(dòng)端圖片上傳功能
這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)移動(dòng)端圖片上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
vue監(jiān)聽滾動(dòng)事件實(shí)現(xiàn)滾動(dòng)監(jiān)聽
本文主要介紹了vue監(jiān)聽滾動(dòng)事件實(shí)現(xiàn)滾動(dòng)監(jiān)聽的相關(guān)資料。具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-04-04

