vue中常见指令及作用,vue的指令及用法

  vue中常见指令及作用,vue的指令及用法

  用户定义指令是vue中第二常用的指令,它包括五个生命周期挂钩:bind、inserted、update、componentUpdated和unbind。接下来,我们将通过本文详细介绍Vue指令的工作原理和实现方法。有需要的朋友可以参考一下。

  

Vue简介

  现在的大前端时代,是一个动荡和纷争的时代。江湖已经分成了很多门派,主要以Vue,React,Angular为首,形成了前端框架的三足鼎立局面。Vue在前端框架中的地位就像jQuery一样。由于其简单性和较高的开发效率,成为前端工程师的必备技能之一。

  Vue是一个渐进式的JavaScript框架,完美集成了第三方插件和UI组件库。Vue和jQuery最大的区别在于,Vue可以改变页面渲染内容,而不需要开发者直接操作DOM节点。应用开发者在具备一定HTML、CSS、JavaScript的基础上,可以快速上手,开发出优雅简洁的应用模块。

  

前言

  自定义指令是vue中第二常用的指令,它包含五个生命周期挂钩:绑定、插入、更新、组件更新和解除绑定。本文将相应介绍vue指令的工作原理。从本文中,您将获得:

  指令的工作原理

  使用说明书的注意事项

  

基本使用

  官网案例:

  div id=应用程序

  输入类型=文本 v-模型=输入值 v-焦点

  /div

  脚本

  Vue.directive(focus ,{

  //第一次绑定元素时调用

  bind () {

  console . log(“bind”)

  },

  //当绑定元素插入DOM时.

  插入:函数(el) {

  console.log(inserted )

  焦点()

  },

  //当其组件的VNode更新时调用

  update () {

  console.log(更新)

  },

  //指令所在组件的VNode及其子VNode都被更新调用。

  componentUpdated () {

  console . log(“component updated”)

  },

  //当指令从元素解除绑定时,只调用一次

  unbind () {

  console.log(unbind )

  }

  })

  新Vue({

  数据:{

  输入值:“”

  }

  }).$ mount(“# app”)

  /脚本

  

指令工作原理

  

初始化

  在初始化全局API时,在platforms/web下,调用createPatchFunction生成一个patch方法,将VNode转换成真正的DOM。初始化的一个重要步骤是定义对应于DOM节点的hooks方法。在创建、激活、更新、删除和销毁DOM的过程中,通过轮询调用相应的钩子方法,这些钩子有的是指令声明循环的入口。

  //src/core/vdom/patch.js

  const hooks=[create , activate , update , remove , destroy]

  导出函数createPatchFunction(后端){

  让我,j

  const cbs={}

  const { modules,nodeOps }=后端

  for(I=0;I挂钩.长度;i) {

  cbs[hooks[i]]=[]

  //模块对应vue中的模块,包括class、style、domlistener、domprops、attrs、directive、ref和transition。

  for(j=0;j模块.长度;j) {

  if(isDef(modules[j][hooks[I]]){

  //最后,钩子被转换成{hookEvent: [cb1,cb2.],}

  cbs[hooks[i]]。push(模块[j][挂钩[i]])

  }

  }

  }

  //.

  返回功能补丁(oldVnode,Vnode,hydrating,removeOnly) {

  //.

  }

  }

  

模板编译

  编译模板是为了解析指令参数,具体解构的ASTElement如下:

  {

  标签:“输入”,

  父元素:ASTElement,

  指令:[

  {

  Arg: null,//参数

  End: 56,//指令的结束字符位置

  IsdDynamicArg: false,//动态参数,以v-xxx[dynamicParams]=xxx 的形式调用

  修饰符:未定义,//指令修饰符

  名称:型号,

  RawName: v-model ,//命令名

  Start: 36,//指令起始字符位置

  值:输入值//模板

  },

  {

  arg: null,

  end: 67,

  isDynamicArg:假,

  修饰符:未定义,

  名称:“焦点”,

  原始名称:“v焦点",

  开始:57,

  值:""

  }

  ],

  //.

  }

  

生成渲染方法

  某视频剪辑软件推荐采用指令的方式去操作多姆,由于自定义指令可能会修改数字正射影像图或者属性,所以避免指令对模板解析的影响,在生成渲染方法时,首先处理的是指令,如v型车,本质是一个语法糖,在拼接渲染函数时,会给元素加上价值属性与投入事件(以投入为例,这个也可以用户自定义)。

  用(这个){

  return _c(div ,{

  属性:{

  id :应用程序

  }

  },[_c(输入,{

  指令:[{

  名称:型号,

  原始名称:"五型",

  值:(输入值),

  表达式:"输入值"

  }, {

  名称:"焦点",

  原始名称:“v焦点"

  }],

  属性:{

  键入:文本

  },

  domProps: {

  值:(输入值)//处理v型车指令时添加的属性

  },

  开:{

  输入:函数($event) { //处理v型车指令时添加的自定义事件

  if(event。目标。合成)

  返回;

  输入值=$event.target.value

  }

  }

  })])

  }

  

生成VNode

  某视频剪辑软件的指令设计是方便我们操作多姆,在生成虚拟节点时,指令并没有做额外处理。

  

生成真实DOM

  在某视频剪辑软件初始化过程中,我们需要记住两点:

  状态的初始化是父-子,如创建前、创建后、安装前,调用顺序是父-子

  真实数字正射影像图挂载顺序是子-父,如安装,这是因为在生成真实数字正射影像图过程中,如果遇到组件,会走组件创建的过程,真实数字正射影像图的生成是从子到父一级级拼接。

  在修补过程中,每此调用createElm生成真实数字正射影像图时,都会检测当前虚拟节点是否存在数据属性,存在,则会调用invokeCreateHooks,走初创建的钩子函数,核心代码如下:

  //src/core/vdom/patch.js

  函数createElm(

  vnode,

  insertedVnodeQueue,

  parentElm,

  雷菲尔姆,

  嵌套,

  ownerArray,

  指数

  ) {

  //.

  //createComponent有返回值,是创建组件的方法,没有返回值,则继续走下面的方法

  if (createComponent(vnode,insertedVnodeQueue,parentElm,refElm)) {

  返回

  }

  常数数据=vnode.data

  //.

  if (isDef(data)) {

  //真实节点创建之后,更新节点属性,包括指令

  //指令首次会调用约束方法,然后会初始化指令后续钩住方法

  invokeCreateHooks(vnode,insertedVnodeQueue)

  }

  //从底向上,依次插入

  insert(parentElm,vnode.elm,refElm)

  //.

  }

  以上是指令钩子方法的第一个入口,是时候揭露directive.js神秘的面纱了,核心代码如下:

  //src/core/vdom/modules/directions。射流研究…

  //默认抛出的都是更新指令方法

  导出默认值{

  创建:更新指令,

  更新:更新指令,

  销毁:函数解除绑定指令(vnode:VNodeWithData){

  //销毁时,vnode===emptyNode

  更新指令(vnode,emptyNode)

  }

  }

  函数更新指令(旧的vnode:VNodeWithData,vnode: VNodeWithData) {

  if(旧vnode。数据。指令 vnode。数据。指令){

  _update(oldVnode,Vnode)

  }

  }

  function _update (oldVnode,vnode) {

  const is create=old vnode===空节点

  const is destroy=vnode===空节点

  const old dirs=规范化指令(旧vnode。数据。指令,oldVnode.context)

  const new dirs=规范化指令(vnode。数据。指令,vnode.context)

  //插入后的回调

  const dirsWithInsert=[

  //更新完成后回调

  const dirsWithPostpatch=[]

  字母键,旧目录,目录

  对于(键入新目录){

  oldDir=oldDirs[key]

  方向=新目录[答案]

  //新元素指令,会执行一次插入的钩子方法

  如果(!旧目录){

  //新指令,绑定

  callHook(dir, bind ,vnode,oldVnode)

  如果(目录定义目录定义插入){

  dirsWithInsert.push(dir)

  }

  }否则{

  //现有指令,更新

  //元素已经存在,所以componentUpdated hook方法将执行一次。

  目录.旧值=旧目录.值

  dir.oldArg=oldDir.arg

  callHook(目录,更新,虚拟节点,旧虚拟节点)

  if(目录定义目录定义组件更新){

  dirsWithPostpatch.push(dir)

  }

  }

  }

  if (dirsWithInsert.length) {

  //当真正的DOM插入到页面中时,将调用这个回调方法

  const callInsert=()={

  for(设I=0;i dirsWithInsert.lengthi ) {

  callHook(dirsWithInsert[i], inserted ,vnode,oldVnode)

  }

  }

  //VNode合并插入挂钩

  如果(isCreate) {

  mergeVNodeHook(vnode, insert ,callInsert)

  }否则{

  callInsert()

  }

  }

  if (dirsWithPostpatch.length) {

  mergeVNodeHook(vnode, postpatch ,()={

  for(设I=0;i dirsWithPostpatch.lengthi ) {

  callHook(dirsWithPostpatch[i], componentUpdated ,vnode,oldVnode)

  }

  })

  }

  如果(!isCreate) {

  对于(键入旧目录){

  如果(!newDirs[key]) {

  //不再存在,解除绑定

  callHook(oldDirs[key], unbind ,oldVnode,oldVnode,isDestroy)

  }

  }

  }

  }

  对于第一次创建,执行过程如下:

  OldVnode===emptyNode,isCreate为true,调用当前元素中的所有绑定钩子方法。

  检查指令中是否存在插入的挂钩,如果存在,则将插入的挂钩合并到VNode.data.hooks属性中。

  DOM挂载后,将执行invokeInsertHook,所有挂载的节点,如果VNode.data.hooks中有insert钩子被调用,触发指令绑定的insert方法。

  一般来说,第一次创建只使用bind和inserted方法,而update和componentUpdated对应于bind和inserted。当组件依赖状态发生变化时,会使用VNode diff算法对节点进行补丁更新,其调用过程如下:

  响应数据的变化,调用dep.notify通知数据更新。

  调用patchVNode对新旧VNode进行差分更新,完整更新当前VNode属性(包括指令,将进入updateDirectives方法)。

  如果指令中有update hook方法,则调用update hook方法并初始化componentUpdated回调,以将postpatch挂钩挂载到VNode.data.hooks中

  当前节点和子节点更新后,将触发postpatch挂钩,即指令的componentUpdated方法。

  核心代码如下:

  //src/core/vdom/patch.js

  函数patchVnode(

  奥尔德夫诺德,

  vnode,

  insertedVnodeQueue,

  ownerArray,

  索引,

  仅移除

  ) {

  //.

  const oldCh=oldVnode.children

  const ch=vnode.children

  //完全更新节点的属性。

  if(isDef(data)is patchable(vnode)){

  for(I=0;I CBS . update . length;cbs.update[i](oldVnode,Vnode)

  if(isDef(I=data . hook)isDef(I=I . update))I(old Vnode,vnode)

  }

  //.

  if (isDef(data)) {

  //调用postpatch挂钩

  if(isDef(I=data . hook)isDef(I=I . post patch))I(old Vnode,vnode)

  }

  }

  Unbind方法是在节点销毁时调用invokeDestroyHook,这里不做描述。

  

注意事项

  使用自定义指令时,v-model和通用模板数据绑定还是有一些区别的。例如,虽然我传递的参数(v-xxx=param )是一个引用类型,但当数据发生变化时,它不能触发指令的bind或inserted。这是因为在指令的声明期间,bind和inserted在初始化期间只调用一次,后面只会跟着update和componentUpdated。

  指令的声明周期按照绑定-插入-更新-组件更新的顺序执行。如果指令需要依赖子组件的内容,建议在componentUnpdated中编写相应的业务逻辑。

  在vue中,很多方法都是循环调用,比如hooks方法,事件回调等。通常,调用被包装在try catch中。这样做的目的是防止一个处理方法报错导致整个程序崩溃,可以供我们开发过程借鉴。

  

小结

  当我开始看整个vue源代码的时候,我对很多细节都不太了解。通过梳理每个具体功能的实现,我可以逐渐看到整个vue,同时也可以避免开发和使用中的一些坑。

  开源代码库

  以上是Vue指令工作原理实现方法的详细内容。更多关于Vue指令原理的信息,请关注我们的其他相关文章!

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

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