網(wǎng)站首頁 Vue 正文
前言
Vue 指令 是指 對普通DOM元素進(jìn)行底層操作的JS對象, 它們會被掛在Element VNode對象上,在Element VNode的一些生命周期中會被調(diào)用,從而可以操作Element VNode的底層DOM元素。
指令注冊
指令注冊 是指將指令對應(yīng)的JS代碼放置在某些地方,需要使用的時候可以在這些地方進(jìn)行查找。
全局注冊
- 全局注冊是調(diào)用app.directive('指令名稱', { 指令代碼 }) 來實現(xiàn)的
app.directive('pin', (el, binding) => { el.style.position = 'fixed' const s = binding.arg || 'top' el.style[s] = binding.value + 'px' })
- 全局注冊的邏輯是將 指令名稱 和 對應(yīng)的指令代碼 掛載在全局的context的directives對象上
<!-- apiCreateApp.js --> directive(name: string, directive?: Directive) { // 掛載在全局的`context`的`directives`對象上 context.directives[name] = directive return app },
組件內(nèi)注冊
- 組件內(nèi)注冊是在組件內(nèi)添加 directives 的選項
directives: { pin: (el, binding) => { el.style.position = 'fixed' const s = binding.arg || 'top' el.style[s] = binding.value + 'px' } }
- 組件注冊的邏輯是將 指令名稱 和 對應(yīng)的指令代碼 掛載在組件實例對象的directives上
<!-- component.ts --> export function applyOptions(instance: ComponentInternalInstance) { // 掛載在組件實例對象的`directives`上 instance.directives = directives }
指令搜尋
指令搜尋的時機(jī)
開發(fā)者是在模板中使用指令,所以應(yīng)該是在模板渲染的時候需要先搜尋到對應(yīng)的指令。
不使用指令的模板<h4>指令演示</h4>渲染函數(shù)如下:
function render(_ctx, _cache) { with (_ctx) { const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue return (_openBlock(), _createElementBlock("h4", null, "指令演示")) } }
使用指令的模板<h4 v-pin:[right]="20">指令演示</h4>渲染函數(shù)如下:
function render(_ctx, _cache) { with (_ctx) { const { createTextVNode: _createTextVNode, resolveDirective: _resolveDirective, withDirectives: _withDirectives, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue const _directive_pin = _resolveDirective("pin") return _withDirectives((_openBlock(), _createElementBlock("h4", null, _hoisted_2, 512 /* NEED_PATCH */)), [ [_directive_pin, pinPadding, direction] ]) } }
使用指令的模板需要先搜尋對應(yīng)的指令,然后綁定指令到VNode
指令搜尋的邏輯
- 指令搜尋的邏輯是先從組件實例instance的directives上尋找,如果沒找到再在appContext的directives上尋找
export function resolveDirective(name: string): Directive | undefined { return resolveAsset(DIRECTIVES, name) } function resolveAsset( type: AssetTypes, name: string, warnMissing = true, maybeSelfReference = false ) { const res = // local registration // check instance[type] first which is resolved for options API resolve(instance[type] || (Component as ComponentOptions)[type], name) || // global registration resolve(instance.appContext[type], name) return res }
指令綁定VNode
export function withDirectives<T extends VNode>( vnode: T, directives: DirectiveArguments ): T { const bindings: DirectiveBinding[] = vnode.dirs || (vnode.dirs = []) for (let i = 0; i < directives.length; i++) { let [dir, value, arg, modifiers = EMPTY_OBJ] = directives[i] if (isFunction(dir)) { dir = { mounted: dir, updated: dir } as ObjectDirective } bindings.push({ dir, instance, value, oldValue: void 0, arg, modifiers }) } return vnode }
將每個指令dir和其他一些參數(shù) 掛載在 VNode的dirs上。 其他參數(shù)是: instance組件實例, value指令的新值(本例為20),oldValue指令的舊值(本例為0),arg指令的參數(shù)(本例為right)
指令調(diào)用
指令調(diào)用是指 指令的代碼什么時候被執(zhí)行的? 我們最開始提到指令是對普通DOM元素進(jìn)行底層操作的JS對象,所以指令的邏輯應(yīng)該是在 Element VNode中進(jìn)行處理的。
const mountElement = ( vnode: VNode, container: RendererElement, anchor: RendererNode | null, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, isSVG: boolean, slotScopeIds: string[] | null, optimized: boolean ) => { // 1 if (dirs) { invokeDirectiveHook(vnode, null, parentComponent, 'created') } // 2 if (dirs) { invokeDirectiveHook(vnode, null, parentComponent, 'beforeMount') } // 3 queuePostRenderEffect(() => { vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode) needCallTransitionHooks && transition!.enter(el) dirs && invokeDirectiveHook(vnode, null, parentComponent, 'mounted') }, parentSuspense) }
const patchElement = ( n1: VNode, n2: VNode, parentComponent: ComponentInternalInstance | null, parentSuspense: SuspenseBoundary | null, isSVG: boolean, slotScopeIds: string[] | null, optimized: boolean ) => { const el = (n2.el = n1.el!) // 1 if (dirs) { invokeDirectiveHook(n2, n1, parentComponent, 'beforeUpdate') } // 2 queuePostRenderEffect(() => { vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, n2, n1) dirs && invokeDirectiveHook(n2, n1, parentComponent, 'updated') }, parentSuspense) }
const unmount: UnmountFn = ( vnode, parentComponent, parentSuspense, doRemove = false, optimized = false ) => { const { type, props, ref, children, dynamicChildren, shapeFlag, patchFlag, dirs } = vnode // unset ref if (ref != null) { setRef(ref, null, parentSuspense, vnode, true) } if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) { ;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode) return } const shouldInvokeDirs = shapeFlag & ShapeFlags.ELEMENT && dirs let vnodeHook: VNodeHook | undefined | null if ((vnodeHook = props && props.onVnodeBeforeUnmount)) { invokeVNodeHook(vnodeHook, parentComponent, vnode) } if (shapeFlag & ShapeFlags.COMPONENT) { unmountComponent(vnode.component!, parentSuspense, doRemove) } else { if (shouldInvokeDirs) { invokeDirectiveHook(vnode, null, parentComponent, 'beforeUnmount') } queuePostRenderEffect(() => { vnodeHook && invokeVNodeHook(vnodeHook, parentComponent, vnode) shouldInvokeDirs && invokeDirectiveHook(vnode, null, parentComponent, 'unmounted') }, parentSuspense) }
在掛載元素VNode的時候,會調(diào)用指令的created, beforeMount和mounted鉤子函數(shù);
在更新元素VNode的時候,會調(diào)用指令的beforeUpdate, updated鉤子函數(shù);
在卸載元素VNode的時候,會調(diào)用指令的beforeUnmount, unmounted鉤子函數(shù);
關(guān)于指令的思考
組件上使用指令
我們上面提到了指令是作用在元素VNode上的,那組件使用指令(例如<son v-pin:[right]="20"></son>
)是什么效果呢?結(jié)果是組件上使用的指令會作用在組件內(nèi)部的根節(jié)點的元素VNode上。
export function renderComponentRoot( instance: ComponentInternalInstance ): VNode { const { type: Component, vnode, proxy, withProxy, props, propsOptions: [propsOptions], slots, attrs, emit, render, renderCache, data, setupState, ctx, inheritAttrs } = instance // inherit directives if (vnode.dirs) { if (__DEV__ && !isElementRoot(root)) { warn( `Runtime directive used on component with non-element root node. ` + `The directives will not function as intended.` ) } root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs } }
在組件渲染子樹VNode的根VNode時候,會將組件的指令dirs添加在根元素VNode的dirs中。所以作用于組件的指令 等同于 作用于 根節(jié)點的元素VNode上。
組件上的一些使用場景
我覺得一些比較使用的指令的使用場景有:
- v-lazyload: 圖片的懶加載
- v-loading:實現(xiàn)加一個加載動畫
- v-permission: 權(quán)限控制,沒有權(quán)限就隱藏DOM元素
- v-debounce: 輸入防抖,特別是搜素框請求的輸入
總結(jié)
原文鏈接:https://juejin.cn/post/7056614328572575775
相關(guān)推薦
- 2022-08-18 C/C++?extern和static的使用詳解_C 語言
- 2022-09-17 python?字符串模糊匹配Fuzzywuzzy的實現(xiàn)_python
- 2022-04-10 解析React?中的Virtual?DOM_MsSql
- 2023-01-08 ubuntu1804搭建FTP服務(wù)器的詳細(xì)教程_FTP服務(wù)器
- 2022-01-08 iframe 監(jiān)聽滾動事件并滾動到指定位置
- 2022-01-08 使用grid布局解決flex布局最后一行不足的問題
- 2023-01-13 C語言實現(xiàn)繪制貝塞爾曲線的函數(shù)_C 語言
- 2024-02-17 開發(fā)中SpringBoot項目jar包過大的解決辦法
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支