vue自定义指令用法,vue的基础指令

  vue自定义指令用法,vue的基础指令

  在Vue的项目中,我们经常会遇到v-if、v-show、v-for或者v-model等内置指令,这些指令为我们提供了不同的功能。除了使用这些内置指令,Vue还允许您注册自定义指令。接下来,我们将使用Vue 3官方文档中自定义说明一章中使用的示例来一步步揭开自定义说明背后的秘密。

  

目录

  一、自定义指令1。注册全局用户定义指令2。使用全局用户定义指令3。完成使用示例2。注册全局用户自定义指令的过程3。安装4的过程。阿宝哥有话说4.1 vue 3有哪些内置指令?4.2有多少种类型的指令?4.3注册全局指令和本地指令有什么区别?4.4内置指令和自定义指令生成的渲染函数有什么区别?4.5如何在渲染函数中应用指令?

  提示:在阅读本文之前,建议您先阅读Vue 3官方文档中关于自定义说明一章的内容。

  

一、自定义指令

  

1、注册全局自定义指令

  constapp=Vue.createApp({})

  //注册全局自定义指令v-focus

  app.directive(focus ,{

  //在DOM中装入绑定元素时调用

  已安装(el){

  //焦点元素

  焦点()

  }

  })

  

2、使用全局自定义指令

  divid=app

  输入-焦点/

  /div

  

3、完整的使用示例

  divid=app

  输入-焦点/

  /div

  脚本

  const{createApp}=Vue

  constapp=Vue.createApp({})//

  app.directive(focus ,{//

  //在DOM中装入绑定元素时调用

  已安装(el){

  El.focus()//焦点元素

  }

  })

  app.mount(#app)//

  /脚本

  当页面被加载时,页面中的输入框元素将自动获得焦点。这个例子的代码相对简单,主要包括三个步骤:创建一个App对象,注册一个全局定制命令,应用mount。创建App对象的细节将在后续文章中单独介绍,下面我们将重点介绍另外两个步骤。首先,我们来分析一下注册全局自定义指令的流程。

  

二、注册全局自定义指令的过程

  在上面的示例中,我们使用app对象的directive方法来注册全局自定义指令:

  app.directive(focus ,{

  //在DOM中装入绑定元素时调用

  已安装(el){

  El.focus()//焦点元素

  }

  })

  当然,除了注册全局定制指令之外,我们还可以注册局部指令,因为在组件中也接受指令选项:

  指令:{

  焦点:{

  已安装(el){

  焦点()

  }

  }

  }

  对于上面的示例,我们使用的app.directive方法是在runtime-core/src/apicreateapp . ts文件中定义的:

  //packages/runtime-core/src/apicreateapp . ts

  exportfunctioncreateappapiohostelement(

  render:rootdrenderfunction,

  水合物?RootHydrateFunction

  ):CreateAppFunctionHostElement{

  returnfunctioncreateApp(root component,rootProps=null){

  constcontext=createAppContext()

  letisMounted=false

  constapp:App=(context.app={

  //省略一些代码

  _context:上下文,

  //用于注册或检索全局指令。

  指令(名称:字符串,指令?指令){

  if(__DEV__){

  有效方向名称(名称)

  }

  如果(!指令){

  return context . directives[name]asany

  }

  if(_ _ DEV _ _ context . directives[name]){

  warn(` directive $ { name } hasalreadybeenregisteredintargetapp . `)

  }

  context . directives[name]=directive

  退货pp

  },

  退货pp

  }

  }

  通过观察上面的代码,我们可以知道directive方法支持以下两个参数:

  名称:指示指令的名称;

  指令(可选):指示指令的定义。

  name参数很简单,所以我们重点分析directive参数,它的类型是Directive:

  //packages/runtime-core/src/指令. ts

  exporttypeDirectiveT=any,V=any=

  ObjectDirectiveT,V

  FunctionDirectiveT,V

  从上面可以看出,Directive类型属于union类型,所以我们需要继续分析ObjectDirective和FunctionDirective类型。这里,我们先来看看ObjectDirective类型的定义:

  //packages/runtime-core/src/指令. ts

  exportinterfaceObjectDirectiveT=any,V=any{

  创造了?DirectiveHookT,null,V

  在蒙特之前?DirectiveHookT,null,V

  装裱?DirectiveHookT,null,V

  更新前?DirectiveHookT,VNodeany,T,V

  更新了?DirectiveHookT,VNodeany,T,V

  卸载前?DirectiveHookT,null,V

  未安装?DirectiveHookT,null,V

  getSSRProps?SSRDirectiveHook

  }

  该类型定义了对象类型的指令,对象上的每个属性表示指令生命周期上的钩子。而函数指令类型则表示函数类型的指令:

  //包/运行时核心/src/指令。分时(同timesharing)

  exporttypeFunctionDirectiveT=any,V=any=DirectiveHookT,any,V

  exporttypeDirectiveHookT=any,Prev=VNodeany,Tnull,V=any=(

  艾尔:T,

  binding:DirectiveBindingV,

  vnode:VNodeany,T,

  prevVNode:Prev

  )=void

  介绍完管理的类型,我们再回顾一下前面的示例,相信你就会清晰很多:

  app.directive(focus ,{

  //当被绑定的元素挂载到数字正射影像图中时触发

  已安装(el){

  el.focus()//聚焦元素

  }

  })

  对于以上示例,当我们调用app.directive方法注册自定义集中指令时,就会执行以下逻辑:

  指令(名称:字符串,指令?指令){

  if(__DEV__){//避免自定义指令名称,与已有的内置指令名称冲突

  有效方向名称(名称)

  }

  如果(!指令){//获取名字对应的指令对象

  返回上下文。指令[名称]asany

  }

  if(_ _ DEV _ _ context。指令[名称]){

  warn(` directive $ { name } hasalreadybeenregisteredintargetapp。`)

  }

  语境。指令[名称]=指令//注册全局指令

  退货过去分词

  }

  当集中指令注册成功之后,该指令会被保存在语境对象的指令属性中,具体如下图所示:

  顾名思义语境是表示应用的上下文对象,那么该对象是如何创建的呢?其实,该对象是通过createAppContext函数来创建的:

  constcontext=createAppContext()

  而createAppContext函数被定义在运行时-core/src/apiCreateApp.ts文件中:

  //packages/runtime-core/src/apicreateapp。分时(同timesharing)

  exportfunctioncreateAppContext():应用程序上下文{

  返回{

  app:nullasany,

  配置:{

  isNativeTag:没有,

  表现:假的,

  全局属性:{},

  选项合并策略:{},

  isCustomElement:否,

  错误处理程序:未定义,

  警告处理程序:未定义

  },

  混合:[],

  组件:{},

  指令:{},

  提供:对象.创建(空)

  }

  }

  看到这里,是不是觉得注册全局自定义指令的内部处理逻辑其实挺简单的。那么对于已注册的集中指令,何时会被调用呢?要回答这个问题,我们就需要分析另一个步骤—— 应用挂载。

  

三、应用挂载的过程

  为了更加直观地了解应用挂载的过程,阿宝哥利用铬开发者工具,记录了应用挂载的主要过程:

  通过上图,我们就可以知道应用挂载期间所经历的主要过程。此外,从图中我们也发现了一个与指令相关的函数解决方向。很明显,该函数用于解析指令,且该函数在提出方法中会被调用。在源码中,我们找到了该函数的定义:

  //packages/runtime-core/src/helpers/resolve资产。分时(同timesharing)

  exportfunctionresolveDirective(名称:字符串):指令未定义{

  returnresolveAsset(指令,名称)

  }

  在resolveDirective函数内部,会继续调用resolveAsset函数来执行具体的解析操作。在分析resolveAsset函数的具体实现之前,我们在resolveDirective函数内部加个断点,来一睹提出方法的"芳容":

  在上图中,我们看到了与集中指令相关的_resolveDirective(focus )函数调用。前面我们已经知道在resolveDirective函数内部会继续调用resolveAsset函数,该函数的具体实现如下:

  //packages/runtime-core/src/helpers/resolve资产。分时(同timesharing)

  functionresolveAsset(

  类型:组件类型指令类型,

  名称:字符串,

  warnMissing=true

  ){

  const instance=currentRenderingInstance currentInstance

  如果(实例){

  constComponent=instance.type

  //省略解析组件的处理逻辑

  约束=

  //局部注册

  解决(实例[类型](组件组件选项)[类型],名称)

  //全局注册

  解决(实例。应用程序上下文[类型],名称)

  返回者

  }elseif(__DEV__){

  警告(

  ` resolve $ { capital(type。slice(0,-1))}

  ` canonlybeusedinrender()或setup(),

  )

  }

  }

  因为focus指令是全局注册的,所以resolve(实例。appcontext [type],name)语句将在解析过程中执行,其中resolve方法定义如下:

  functionresolve(注册表:记录字符串,任何未定义,名称:字符串){

  返回(

  登记处

  (注册表[名称]

  注册表[camelize(name)]

  注册表[大写(camelize(name))])

  )

  }

  分析上面的处理流程后,我们可以知道,在解析全局注册指令时,会通过resolve函数从应用上下文对象中获取注册指令对象。获得_directive_focus指令对象后,render方法将继续调用_withDirectives函数,该函数用于向VNode对象添加指令。这个函数是在runtime-core/src/directives.ts文件中定义的:

  //packages/runtime-core/src/指令. ts

  exportfunctionwithDirectivesTextendsVNode(

  vnode:T,

  指令:指令参数

  ):T{

  consta instance=currentrenderinginstance//获取当前呈现的实例。

  const instance=internal instance . proxy

  const bindings:directive binding[]=vnode . dirs (vnode . dirs=[])

  for(leti=0;idirectives.lengthi ){

  设[dir,value,arg,modifiers=EMPTY _ OBJ]=指令[i]

  //挂载和更新时,触发相同的行为,不考虑其他钩子函数。

  If(isFunction(dir)){//处理函数类型指令

  dir={

  已安装:dir,

  更新:目录

  }asObjectDirective

  }

  bindings.push({

  dir,

  实例,

  值,

  旧值:void0,

  arg,

  修饰语

  })

  }

  returnvnode

  }

  因为可以在一个节点上应用多个指令,所以withDirectives函数在VNode对象上定义了一个dirs属性,属性值是一个数组。对于前面的示例,在调用withDirectives函数后,一个新的dirs属性将被添加到VNode对象中,如下图所示:

  通过上面的分析,我们已经知道,在组件的render方法中,我们会通过withDirectives函数在对应的VNode对象上注册指令。那么focus指令上定义的钩子什么时候调用呢?在继续分析之前,我们先介绍一下指令对象支持的钩子函数。

  指令定义对象可以提供以下挂钩函数(都是可选的):

  Created:在应用绑定元素的属性或事件侦听器之前调用。

  BeforeMount:当指令第一次绑定到一个元素并在挂载父组件之前被调用时。

  Mounted:在绑定元素的父组件安装后调用。

  Before:在更新包含组件的VNode之前调用。

  Updated:在包含组件及其子组件的VNode更新后调用。

  BeforeUnmount:在卸载绑定元素的父组件之前调用。

  Unmounted:当指令与元素解除绑定并且父组件被卸载时,它只被调用一次。

  介绍完这些钩子函数后,让我们回顾一下前面介绍的ObjectDirective类型:

  //packages/runtime-core/src/指令. ts

  exportinterfaceObjectDirectiveT=any,V=any{

  创造了?DirectiveHookT,null,V

  在蒙特之前?DirectiveHookT,null,V

  装裱?DirectiveHookT,null,V

  更新前?DirectiveHookT,VNodeany,T,V

  更新了?DirectiveHookT,VNodeany,T,V

  卸载前?DirectiveHookT,null,V

  未安装?DirectiveHookT,null,V

  getSSRProps?SSRDirectiveHook

  好了,接下来我们来分析一下focus指令上定义的钩子什么时候被调用。同样,Po Ge在focus指令的mounted方法中添加了一个断点:

  在图右侧的调用堆栈中,我们看到了invokeDirectiveHook函数。很明显,函数是调用指令上注册的钩子。限于篇幅,具体细节我就不继续介绍了。有兴趣的朋友可以自己调试断点。

  

四、阿宝哥有话说

  

4.1 Vue 3 有哪些内置指令?

  在注册全局自定义指令的过程中,我们看到了一个validateDirectiveName函数,用来验证自定义指令的名称,避免自定义指令名称与现有内置指令名称冲突。

  //packages/runtime-core/src/指令. ts

  exportfunctionvalidateDirectiveName(名称:字符串){

  if(isbuiltindirect(name)){

  warn( donotusebuild-indirectiveidsascustomdirectiveid: name )

  }

  }

  在validateDirectiveName函数内部,会通过isBuiltInDirective(名称)语句来判断是否为内置指令:

  consists build indirect=/* # _ _ PURE _ _ */makeMap(

  绑定,遮盖 else-if,else,for,html,if,model,on,once,pre,show,slot,text

  )

  以上代码中的制作地图函数,用于生成一个地图对象(Object.create(null))并返回一个函数,用于检测某个键是否存在地图对象中。另外,通过以上代码,我们就可以很清楚地了解Vue 3中为我们提供了哪些内置指令。

  

4.2 指令有几种类型?

  在Vue 3中指令分为对象指令和函数指令两种类型:

  //包/运行时核心/src/指令。分时(同timesharing)

  exporttypeDirectiveT=any,V=any=

  ObjectDirectiveT,V

  FunctionDirectiveT,V

  对象指令

  exportinterfaceObjectDirectiveT=any,V=any{

  创造了?DirectiveHookT,null,V

  在蒙特之前?DirectiveHookT,null,V

  装裱?DirectiveHookT,null,V

  更新前?DirectiveHookT,VNodeany,T,V

  更新了?DirectiveHookT,VNodeany,T,V

  卸载前?DirectiveHookT,null,V

  未安装?DirectiveHookT,null,V

  getSSRProps?SSRDirectiveHook

  }

  函数指令

  exporttypeFunctionDirectiveT=any,V=any=DirectiveHookT,any,V

  exporttypeDirectiveHookT=any,Prev=VNodeany,Tnull,V=any=(

  艾尔:T,

  binding:DirectiveBindingV,

  vnode:VNodeany,T,

  prevVNode:Prev

  )=void

  如果你想在安装好的和更新时触发相同行为,而不关心其他的钩子函数。那么你可以通过将回调函数传递给指令来实现

  app.directive(pin ,(el,binding)={

  el.style.position=fixed

  consts=binding.argtop

  el.style[s]=binding.value px

  })

  

4.3 注册全局指令与局部指令有什么区别?

  注册全局指令

  app.directive(focus ,{

  //当被绑定的元素挂载到数字正射影像图中时被调用

  已安装(el){

  el.focus()//聚焦元素

  }

  });

  注册局部指令

  常量组件=定义组件({

  指令:{

  焦点:{

  已安装(el){

  焦点()

  }

  }

  },

  render(){

  const{directives}=this .$选项

  return[withDirectives(h(input ),[[directives.focus,]])]

  }

  });

  解析全局注册和局部注册的指令

  //packages/runtime-core/src/helpers/resolve资产。分时(同timesharing)

  functionresolveAsset(

  类型:组件类型指令类型,

  名称:字符串,

  warnMissing=true

  ){

  const instance=currentRenderingInstance currentInstance

  如果(实例){

  constComponent=instance.type

  //省略解析组件的处理逻辑

  约束=

  //局部注册

  解决(实例[类型](组件组件选项)[类型],名称)

  //全局注册

  解决(实例。应用程序上下文[类型],名称)

  返回者

  }

  }

  

4.4 内置指令和自定义指令生成的渲染函数有什么区别?

  要了解内置指令和自定义指令生成的渲染函数的区别,阿宝哥以虚拟如果,虚拟显示内置指令和垂直聚焦自定义指令为例,然后使用Vue 3模板浏览器这个在线工具来编译生成渲染函数:

  控制显示内置指令

  inputv-if=isShow/

  const_Vue=Vue

  returnfunctionrender(_ctx,_cache,$props,$setup,$data,$options){

  带有(_ctx){

  const{createVNode:_createVNode,openBlock:_openBlock,

  createBlock:_createBlock,createCommentVNode:_ createCommentVNode }=_ Vue

  returnisShow

  ?(_openBlock(),_createBlock(input ,{key:0}))

  :_createCommentVNode(v-if ,true)

  }

  }

  对于控制显示指令来说,在编译后会通过?三目运算符来实现动态创建节点的功能。

  虚拟展示内置指令

  inputv-show=isShow/

  const_Vue=Vue

  returnfunctionrender(_ctx,_cache,$props,$setup,$data,$options){

  带有(_ctx){

  const{vShow:_vShow,createVNode:_createVNode,withDirectives:_withDirectives,

  openBlock:_openBlock,createBlock:_createBlock}=_Vue

  return _ with指令((_ open block(),_createBlock(input ,null,null,512/*NEED_PATCH*/)),[

  [_vShow,isShow]

  ])

  }

  }

  以上示例中的威尚指令被定义在包/运行时-DOM/src/指令/vs how。分时(同timesharing)文件中,该指令属于对象指令类型的指令,该指令内部定义了在装载、装载、更新之前和卸载前四个钩子。

  垂直聚焦自定义指令

  输入-焦点/

  const_Vue=Vue

  returnfunctionrender(_ctx,_cache,$props,$setup,$data,$options){

  带有(_ctx){

  const { resolve directive:_ resolve directive,createVNode:_createVNode,

  withDirectives:_withDirectives,openBlock:_openBlock,createBlock:_createBlock}=_Vue

  const _ directive _ focus=_ resolve directive( focus )

  return _ with directives((_ open block(),_createBlock(input ,null,null,512/*NEED_PATCH*/)),[

  [_directive_focus]

  ])

  }

  }

  通过比较v-focus和v-show指令生成的渲染函数,我们知道v-focus自定义指令和v-show内置指令都会通过withDirectives函数在VNode对象上注册它们的指令。与内置指令相比,自定义指令会多一个指令解析过程。

  此外,如果在输入元素上同时应用了v-show和v-focus指令,则在调用_ WITH DIRECTIONS函数时将使用二维数组:

  inputv-show=isShowv-focus/

  const_Vue=Vue

  returnfunctionrender(_ctx,_cache,$props,$setup,$data,$options){

  带有(_ctx){

  const{vShow:_vShow,resolve directive:_ resolve directive,createVNode:_createVNode,

  withDirectives:_withDirectives,openBlock:_openBlock,createBlock:_createBlock}=_Vue

  const _ directive _ focus=_ resolve directive( focus )

  return _ with directives((_ open block(),_createBlock(input ,null,null,512/*NEED_PATCH*/)),[

  [_vShow,isShow],

  [_directive_focus]

  ])

  }

  }

  

4.5 如何在渲染函数中应用指令?

  除了在模板中应用指令之外,我们还可以通过使用前面介绍的withDirectives函数方便地在呈现函数中应用指定的指令:

  divid=app/div

  脚本

  const{createApp,h,vShow,defineComponent,withDirectives}=Vue

  const component=define component({

  data(){

  返回{value:true}

  },

  render(){

  return[带方向(h (div ,我是Po哥),[[vshow,this.value]]]

  }

  });

  constapp=Vue.createApp(组件)

  app . mount(# app)

  /脚本

  本文主要介绍如何在Vue 3中自定义命令,注册全局和局部命令。为了让大家更加了解自定义指令,阿宝从源代码的角度分析了指令的注册和应用流程。

  后续文章中,阿宝哥会介绍一些特别的说明,当然也会重点分析一下双向绑定的原理。感兴趣的朋友不要错过。

  以上是Vue 3.0自定义指令使用的详细介绍。更多关于Vue 3.0自定义指令使用的信息,请关注我们的其他相关文章!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: