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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。