亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一篇文章告訴你Vue3指令是如何實(shí)現(xiàn)的

 更新時(shí)間:2022年01月27日 15:11:50   作者:chonglingliu  
在計(jì)算機(jī)技術(shù)中,指令是由指令集架構(gòu)定義的單個(gè)的CPU操作,在更廣泛的意義上,“指令”可以是任何可執(zhí)行程序的元素的表述,例如字節(jié)碼,下面這篇文章主要給大家介紹了關(guān)于Vue3指令是如何實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下

前言

Vue 指令 是指 對普通DOM元素進(jìn)行底層操作的JS對象, 它們會(huì)被掛在Element VNode對象上,在Element VNode的一些生命周期中會(huì)被調(diào)用,從而可以操作Element VNode的底層DOM元素。

指令注冊

指令注冊 是指將指令對應(yīng)的JS代碼放置在某些地方,需要使用的時(shí)候可以在這些地方進(jìn)行查找。

全局注冊

  • 全局注冊是調(diào)用app.directive('指令名稱', { 指令代碼 }) 來實(shí)現(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 的選項(xiàng)
directives: {
  pin: (el, binding) => {
    el.style.position = 'fixed'
    const s = binding.arg || 'top'
    el.style[s] = binding.value + 'px'
  }
}
  • 組件注冊的邏輯是將 指令名稱 和 對應(yīng)的指令代碼 掛載在組件實(shí)例對象的directives上
<!-- component.ts -->
export function applyOptions(instance: ComponentInternalInstance) {
  
  // 掛載在組件實(shí)例對象的`directives`上    
  instance.directives = directives
  
}

指令搜尋

指令搜尋的時(shí)機(jī)

開發(fā)者是在模板中使用指令,所以應(yīng)該是在模板渲染的時(shí)候需要先搜尋到對應(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

指令搜尋的邏輯

  • 指令搜尋的邏輯是先從組件實(shí)例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
}

將每個(gè)指令dir和其他一些參數(shù) 掛載在 VNode的dirs上。 其他參數(shù)是: instance組件實(shí)例, value指令的新值(本例為20),oldValue指令的舊值(本例為0),arg指令的參數(shù)(本例為right)

指令調(diào)用

指令調(diào)用是指 指令的代碼什么時(shí)候被執(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的時(shí)候,會(huì)調(diào)用指令的created, beforeMount和mounted鉤子函數(shù);

在更新元素VNode的時(shí)候,會(huì)調(diào)用指令的beforeUpdate, updated鉤子函數(shù);

在卸載元素VNode的時(shí)候,會(huì)調(diào)用指令的beforeUnmount, unmounted鉤子函數(shù);

關(guān)于指令的思考

組件上使用指令

我們上面提到了指令是作用在元素VNode上的,那組件使用指令(例如<son v-pin:[right]="20"></son>)是什么效果呢?結(jié)果是組件上使用的指令會(huì)作用在組件內(nèi)部的根節(jié)點(diǎn)的元素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時(shí)候,會(huì)將組件的指令dirs添加在根元素VNode的dirs中。所以作用于組件的指令 等同于 作用于 根節(jié)點(diǎn)的元素VNode上。

組件上的一些使用場景

我覺得一些比較使用的指令的使用場景有:

  • v-lazyload: 圖片的懶加載
  • v-loading:實(shí)現(xiàn)加一個(gè)加載動(dòng)畫
  • v-permission: 權(quán)限控制,沒有權(quán)限就隱藏DOM元素
  • v-debounce: 輸入防抖,特別是搜素框請求的輸入

總結(jié)

到此這篇關(guān)于Vue3指令是如何實(shí)現(xiàn)的的文章就介紹到這了,更多相關(guān)Vue3指令實(shí)現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue富文本框(插入文本、圖片、視頻)的使用及問題小結(jié)

    vue富文本框(插入文本、圖片、視頻)的使用及問題小結(jié)

    這篇文章主要介紹了vue富文本框(插入文本、圖片、視頻)的使用及問題小結(jié),需要的朋友可以參考下
    2018-08-08
  • Intellij IDEA搭建vue-cli項(xiàng)目的方法步驟

    Intellij IDEA搭建vue-cli項(xiàng)目的方法步驟

    這篇文章主要介紹了Intellij IDEA搭建vue-cli項(xiàng)目的方法步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-10-10
  • vue手寫<RouterLink/>組件實(shí)現(xiàn)demo詳解

    vue手寫<RouterLink/>組件實(shí)現(xiàn)demo詳解

    這篇文章主要為大家介紹了vue手寫<RouterLink/>組件實(shí)現(xiàn)demo詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • Vue.js 中的 $watch使用方法

    Vue.js 中的 $watch使用方法

    本篇文章中主要介紹了Vue.js 中的 $watch使用方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • 詳解如何使用Vue2做服務(wù)端渲染

    詳解如何使用Vue2做服務(wù)端渲染

    本篇文章主要介紹了如何使用Vue2做服務(wù)端渲染 ,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-03-03
  • 在vue中axios設(shè)置timeout超時(shí)的操作

    在vue中axios設(shè)置timeout超時(shí)的操作

    這篇文章主要介紹了在vue中axios設(shè)置timeout超時(shí)的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 詳解vue 圖片上傳功能

    詳解vue 圖片上傳功能

    這篇文章主要介紹了vue 圖片上傳功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • vue props數(shù)據(jù)傳遞類型限制方式

    vue props數(shù)據(jù)傳遞類型限制方式

    這篇文章主要介紹了vue props數(shù)據(jù)傳遞類型限制方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue-video-player視頻播放器使用配置詳解

    vue-video-player視頻播放器使用配置詳解

    這篇文章主要為大家詳細(xì)介紹了vue-video-player視頻播放器的使用和配置,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • vue usePop彈窗控制器的實(shí)現(xiàn)

    vue usePop彈窗控制器的實(shí)現(xiàn)

    本文主要介紹了vue usePop彈窗控制器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04

最新評論