vue常见指令以及语法,vue的指令及用法
自定义指令是vue中第二常用的指令,它包含五个生命周期挂钩:绑定、插入、更新、组件更新和解除绑定。本文将介绍vue指令的工作原理。
目录
一、基本用法二。指导工作原理2.1。初始化2.2。模板编译2.3。生成渲染方法2.4。生成VNode2.5 .生成real DOM III。通知四。摘要
一、基本使用
官网案例:
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”)
/脚本
二、指令工作原理
2.1、初始化
在初始化全局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) {
//.
}
}
2.2、模板编译
编译模板是为了解析指令参数,具体解构的ASTElement如下:
{
标签:“输入”,
父元素:ASTElement,
指令:[
{
Arg: null,//参数
End: 56,//指令的结束字符位置
IsdDynamicArg: false,//动态参数,以v-xxx[dynamicParams]=xxx 的形式调用
修饰符:未定义,//指令修饰符
名称:型号,
RawName: v-model ,//命令名
Start: 36,//指令起始字符位置
值:输入值//模板
},
{
arg: null,
end: 67,
isDynamicArg:假,
修饰符:未定义,
名称:“焦点”,
raw name:“v焦点”,
开始:57,
值:“”
}
],
//.
}
2.3、生成渲染方法
Vue推荐使用指令操作DOM。由于自定义指令可能会修改DOM或属性,因此有必要避免指令对模板解析的影响。在生成渲染方法的时候,首先要处理的就是指令,比如v-model,本质上就是一个语法糖。拼接渲染函数时,元素会加入值属性和输入事件(以输入为例,这个也可以自定义)。
用(这个){
return _c(div ,{
属性:{
id :应用程序
}
},[_c(输入,{
指令:[{
名称:型号,
原始名称:“v型”,
值:(inputValue),
表达式:“输入值”
}, {
名称:“焦点”,
raw name:“v焦点”
}],
属性:{
键入:文本
},
domProps: {
值:(输入值)//处理v型车指令时添加的属性
},
开:{
输入:函数($event) { //处理v型车指令时添加的自定义事件
if(event。目标。合成)
返回;
输入值=$event.target.value
}
}
})])
}
2.4、生成VNode
某视频剪辑软件的指令设计是方便我们操作多姆,在生成虚拟节点时,指令并没有做额外处理。
2.5、生成真实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)
}
}否则{
//现有指令,更新
//已经存在元素,会执行一次组件更新钩子方法
目录。旧值=旧目录。值
dir.oldArg=oldDir.arg
呼叫挂钩(目录,更新,虚拟节点,旧虚拟节点)
如果(目录定义目录定义组件更新){
dirsWithPostpatch.push(dir)
}
}
}
if (dirsWithInsert.length) {
//真实数字正射影像图插入到页面中,会调用此回调方法
const callInsert=()={
对于(设I=0;i dirsWithInsert.lengthi ) {
callHook(dirsWithInsert[i], inserted ,vnode,oldVnode)
}
}
//VNode合并插入挂钩
如果(isCreate) {
mergeVNodeHook(vnode, insert ,callInsert)
}否则{
callInsert()
}
}
if (dirsWithPostpatch.length) {
mergeVNodeHook(vnode, postpatch ,()={
对于(设I=0;i dirsWithPostpatch.lengthi ) {
callHook(dirsWithPostpatch[i], componentUpdated ,vnode,oldVnode)
}
})
}
如果(!isCreate) {
对于(键入旧目录){
如果(!newDirs[key]) {
//不再存在,解除绑定
callHook(oldDirs[key], unbind ,oldVnode,oldVnode,isDestroy)
}
}
}
}
对于第一次创建,执行过程如下:
1.oldVnode===emptyNode,isCreate为真,调用当前元素中的所有绑定钩子方法。
2.检查指令中是否存在插入的钩子,如果存在,将插入的钩子合并到VNode.data.hooks属性中。
3.3之后。DOM挂载,将执行invokeInsertHook,所有挂载的节点,如果VNode.data.hooks中有insert钩子被调用,触发指令绑定的insert方法。
一般来说,第一次创建只使用bind和inserted方法,而update和componentUpdated对应于bind和inserted。当组件依赖状态发生变化时,会使用VNode diff算法对节点进行补丁更新,其调用过程如下:
1.当响应数据改变时,调用dep.notify通知数据更新。
2.调用patchVNode,有区别地更新新旧VNode,完整更新当前VNode属性(包括指令,将进入updateDirectives方法)。
3.如果指令中存在update hook方法,则调用update hook方法,并初始化componentUpdated回调以将postpatch挂钩挂载到VNode.data.hooks中
4.当前节点和子节点更新后,会触发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。
指令的声明循环按照绑定-插入-更新-组件更新的顺序执行。如果指令需要依赖子组件的内容,建议在组件更新中编写相应的业务逻辑。
在vue中,很多方法都是循环调用,比如hooks方法,事件回调等。通常,调用被包装在try catch中。这样做的目的是防止一个处理方法报错导致整个程序崩溃,可以供我们开发过程借鉴。
四、小结
当我开始看整个vue源代码的时候,我对很多细节都不太了解。通过梳理每个具体功能的实现,我可以逐渐看到整个vue,同时也可以避免开发和使用中的一些坑。
以上是分析Vue指令实现原理的详细内容。更多关于Vue指令原理的信息,请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。