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