Vue.js 源码 (15)—— 指令的奥秘1

389 阅读2分钟

这是我参与更文挑战的第16天,活动详情查看: 更文挑战

前言

指令(directive)是 Vue.js 提供的带有 v- 前缀的特殊特性。指令属性的值预期是单个 JavaScript 表达式。指令的职责是,当表达式的值改变时,将其产生的连带影响响应式地作用于 DOM。

指令原理概述

在模板解析阶段,我们在将指令解析到 AST,然后使用 AST 生成代码字符串的过程中实现某些内置指令的功能,最后在虚拟DOM渲染的过程中触发自定义指令的钩子函数使指令生效。

  • 在模板解析阶段,会将节点上的指令解析出来并添加到 AST 的 directives 属性中。
  • 随后 directives 数据会传递到VNode中,接着就可以通过 vnode.data.directives 获取一个节点所绑定的指令
  • 当虚拟DOM进行修补时,会根据节点的对比结果触发一些钩子函数。更新指令的程序会监听createupdatedestroy 钩子函数,并在这三个钩子函数触发时对 VNode 和 oldVNode 进行对比,最终根据对比结果触发指令的钩子函数。

v-if 指令的原理概述

<li v-if="has">if></li>
<li v-else>else</li>

在模板编译的代码生成阶段会生成这样的代码字符串:

(has) 
? _c('li', [_v('if')]) 
: _c('li', [_v('else')])

v-for 指令的原理概述

<li v-for="(item, index) in list">v-for {{index}}</li>

在模板编译阶段会生成这样的代码字符串:

_l(list, function(item, index){
    return _c('li', [
        _v('v-for ' + _s(index))
       ])
})

_l 是函数 renderList 的别名。当执行这段代码字符串时,_l函数会循环变量 list 并依次调用第二个参数所传递的函数。同时,会传递两个参数:item 和 index。此外,当 _c 函数被调用时,会执行 _v 函数创建一个文本节点。

v-on 指令

v-on 指令的作用是绑定事件监听器,事件类型由参数指定。它用在普通元素上时,可以监听原生DOM事件;用在自定义元素组件上时,可以监听子组件触发的自定义事件。

从模板解析到生成VNode,最终事件会被保存在VNode中,然后可以通过 vnode.data.on 得到一个节点注册的所有事件。

<buttton v-on:click="doSomething"></button>

在最终生成的VNode中,我们可以通过vnode.data.on读出下面的事件对象:

{
    click: function(){}
}

件绑定相关的处理逻辑分别设置了 createupdate 钩子函数,也就是说在修补的过程中,每当一个DOM元素被创建或更新时,都会触发事件绑定相关的处理逻辑。事件绑定相关的处理逻辑是一个叫 updateDOMListeners 的函数,而 create 与 update 钩子函数执行的都是这个函数。其代码如下:

let target
function updateDOMListeners(oldVnode, vnode) {
    if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)){
        return;
    }
    const on = vnode.data.on || {};
    const oldOn = oldVnode.data.on || {};
    target = vnode.elm
    normalizeEvents(on)
    updateListeners(on, oldOn, add, remove, vnode.context)
    target = undefined
}

未完待续...