文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Vue3指令是怎么实现的

2023-06-29 01:14

关注

今天小编给大家分享一下Vue3指令是怎么实现的的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

前言

Vue 指令 是指 对普通DOM元素进行底层操作的JS对象, 它们会被挂在Element VNode对象上,在Element VNode的一些生命周期中会被调用,从而可以操作Element VNode的底层DOM元素。

指令注册

指令注册 是指将指令对应的JS代码放置在某些地方,需要使用的时候可以在这些地方进行查找。

全局注册

app.directive('pin', (el, binding) => {  el.style.position = 'fixed'  const s = binding.arg || 'top'  el.style[s] = binding.value + 'px'})
<!-- apiCreateApp.js -->directive(name: string, directive?: Directive) {    // 挂载在全局的`context`的`directives`对象上  context.directives[name] = directive    return app},

组件内注册

directives: {  pin: (el, binding) => {    el.style.position = 'fixed'    const s = binding.arg || 'top'    el.style[s] = binding.value + 'px'  }}
<!-- 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 )), [      [_directive_pin, pinPadding, direction]    ])  }}

Vue3指令是怎么实现的

使用指令的模板需要先搜寻对应的指令,然后绑定指令到VNode

指令搜寻的逻辑

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上。

组件上的一些使用场景

我觉得一些比较使用的指令的使用场景有:

以上就是“Vue3指令是怎么实现的”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯