中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue3指令是怎么實現的

發布時間:2022-01-31 13:10:59 來源:億速云 閱讀:451 作者:iii 欄目:開發技術

今天小編給大家分享一下Vue3指令是怎么實現的的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    前言

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

    指令注冊

    指令注冊 是指將指令對應的JS代碼放置在某些地方,需要使用的時候可以在這些地方進行查找。

    全局注冊

    • 全局注冊是調用app.directive('指令名稱', { 指令代碼 }) 來實現的

    app.directive('pin', (el, binding) => {
      el.style.position = 'fixed'
      const s = binding.arg || 'top'
      el.style[s] = binding.value + 'px'
    })
    • 全局注冊的邏輯是將 指令名稱 和 對應的指令代碼 掛載在全局的context的directives對象上

    <!-- apiCreateApp.js -->
    directive(name: string, directive?: Directive) {
      
      // 掛載在全局的`context`的`directives`對象上
      context.directives[name] = directive
      
      return app
    },

    組件內注冊

    • 組件內注冊是在組件內添加 directives 的選項

    directives: {
      pin: (el, binding) => {
        el.style.position = 'fixed'
        const s = binding.arg || 'top'
        el.style[s] = binding.value + 'px'
      }
    }
    • 組件注冊的邏輯是將 指令名稱 和 對應的指令代碼 掛載在組件實例對象的directives上

    <!-- component.ts -->
    export function applyOptions(instance: ComponentInternalInstance) {
      
      // 掛載在組件實例對象的`directives`上    
      instance.directives = directives
      
    }

    指令搜尋

    指令搜尋的時機

    開發者是在模板中使用指令,所以應該是在模板渲染的時候需要先搜尋到對應的指令。

    不使用指令的模板<h5>指令演示</h5>渲染函數如下:

    function render(_ctx, _cache) {
      with (_ctx) {
        const { openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
    
        return (_openBlock(), _createElementBlock("h5", null, "指令演示"))
      }
    }

    使用指令的模板<h5 v-pin:[right]="20">指令演示</h5>渲染函數如下:

    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("h5", null, _hoisted_2, 512 /* NEED_PATCH */)), [
          [_directive_pin, pinPadding, direction]
        ])
      }
    }

    Vue3指令是怎么實現的

    使用指令的模板需要先搜尋對應的指令,然后綁定指令到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和其他一些參數 掛載在 VNode的dirs上。 其他參數是: instance組件實例, value指令的新值(本例為20),oldValue指令的舊值(本例為0),arg指令的參數(本例為right)

    指令調用

    指令調用是指 指令的代碼什么時候被執行的? 我們最開始提到指令是對普通DOM元素進行底層操作的JS對象,所以指令的邏輯應該是在 Element VNode中進行處理的。

    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的時候,會調用指令的created, beforeMount和mounted鉤子函數;

    在更新元素VNode的時候,會調用指令的beforeUpdate, updated鉤子函數;

    在卸載元素VNode的時候,會調用指令的beforeUnmount, unmounted鉤子函數;

    關于指令的思考

    組件上使用指令

    我們上面提到了指令是作用在元素VNode上的,那組件使用指令(例如<son v-pin:[right]="20"></son>)是什么效果呢?結果是組件上使用的指令會作用在組件內部的根節點的元素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中。所以作用于組件的指令 等同于 作用于 根節點的元素VNode上。

    組件上的一些使用場景

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

    • v-lazyload: 圖片的懶加載

    • v-loading:實現加一個加載動畫

    • v-permission: 權限控制,沒有權限就隱藏DOM元素

    • v-debounce: 輸入防抖,特別是搜素框請求的輸入

    以上就是“Vue3指令是怎么實現的”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    鄂托克旗| 灌阳县| 扎囊县| 乌拉特前旗| 曲沃县| 和龙市| 磐石市| 治县。| 灵台县| 阜城县| 鞍山市| 上饶市| 黄陵县| 渑池县| 白城市| 延庆县| 吉隆县| 梅河口市| 湟源县| 陆丰市| 察哈| 德江县| 陵水| 凤台县| 宜城市| 营口市| 监利县| 札达县| 萨嘎县| 甘孜| 喀喇沁旗| 修武县| 宜黄县| 新密市| 监利县| 台前县| 诸暨市| 东乌珠穆沁旗| 缙云县| 杭州市| 普兰店市|