2. 正规军代码

2023-12-24  本文已影响0人  飞天御剑流co

刚刚已经看完了作者 yyx 的第一次尝试,作者可能发现,唉,好像这种方法行得通,然后在 src 下写了一个功能相对更多的代码实现

里面的 dev.html 则是运行 src 代码的 example

由于我折腾了好久也没能将 grunt 跑明白,为了节约时间,决定将代码拷出来,用 rollup 去打包,自己跑一个

将 require 语法改成 import

导出的地方也用 es 的语法进行导出

demo 地址在这里

然后我们来调试这个代码,看一看这个功能相对多一点的是怎么实现的

老规矩,先看函数的调用方式

可以看到,引入 Seed(一个自定义名称的对象) 对象,调用了 Seed.create ,传入对象作为参数,两个变量 msg 和 hello ,还有一个方法 changeMessage

可以看到,导出的 create 函数就是 main.js 中的 Seed 构造函数

然后开始拆解这个构造函数,看一看 new 的过程干了什么

可以看到,和上一篇文章执行的是同一个套路,self 指向自身,获取传入 id 的 dom 下的自定义在标签上的 NodeList , 然后初始化自身的作用域 scope

上图同样的遍历对象,从函数名可知,此流程是 遍历 els -> 处理 Node 节点 -> 克隆节点上的自定义属性 -> 遍历这些属性 -> 解析指令 -> 如果解析成功,将指令绑定

下面就是一段给 self.scope 赋值,和上一篇文章一样,赋值触发对象的 set , 然后进行响应式更新

来先看第一个函数

可以看到,遍历节点的属性,将属性名和对应的值返回一个对象数组

处理 attr key

遍历此数组的每一项去尝试解析指令,看看 vue 提供的语法和用户传入的用法是否能对应的上

此时的 attr 是这种形式的

var attr = {
    "name": "sd-text",
    "value": "msg | capitalize"
}

函数返回前,处理了一系列的逻辑,首先判断了是 sd 开头的指令才向下执行,noprefix 记录了没有前缀的指令名,下文拿 sd-text="msg | capitalize" 举例:

首先,裁剪掉指令前面的 sd- 前缀,留下纯净的指令信息,再去尝试获取 argIndex 的位置

argIndex = noprefix.indexOf('-') 

这里是为了判断 sd-on-click="changeMessage | .button" 这种情况,有两个 -

如下图

对不同的指令进行不同的处理,以获取到对应的 dirname , 如果是普通指令返回诸如 text 这种,如果是事件监听,返回 dirname 为 on

之后去获取 directives.js 文件下对应 dirname 作为 key 的值

处理 attr value

代码如下

判断有无管道符号 ?裁剪管道符前面的变量赋值给 key : 直接将 attr.value 赋值给 key

以上是有无管道符号处理 value 值的区别

处理完成后,将后续所有管道符号进行处理,返回一个 filters 数组

处理完 attr.key attr.value 之后,函数返回,返回了一个对象,在 update 属性中,区分了一下是事件监听,还是普通的指令

进行绑定

下图是参数

  el.removeAttribute(directive.attr.name)

此函数执行了移除标签上的自定义属性,取指令绑定的值对应的变量也就是 key 属性对应的 msg

    if (!binding) {
        bindings[key] = binding = {
            value: undefined,
            directives: []
        }
    }

尝试直接从 bindings 中获取值,但是我们的 bindings 初始化的时候是一个空值,所以函数会进入 if (!binding) 这段逻辑

    directive.el = el
    binding.directives.push(directive)

给 bindings 对象进行赋值 -> 在 directive 上添加 el 属性,指向绑定的 dom -> 将 directive 直接推送到刚刚 bindings[key] 赋值的新对象的 directives 里面,因为 binding 和 bindings[key] 是引用关系,所以面直接向 directives 添加对应的指令信息

    if (directive.bind) {
        directive.bind(el, binding.value)
    }

上面的分析可知,我们每一个 directive 是没有 bind 属性的所以这段逻辑不会执行

    if (!seed.scope.hasOwnProperty(key)) {
        bindAccessors(seed, key, binding)
    }

判断 scope 是否还没有对应绑定的属性(也就是绑定的 key 还没有被 Object.defineProperty 定义对应的 get set),执行 bindAccessors 函数

拿刚刚的绑定的 msg 举例,当给 scope.msg 赋值的时候,会触发 set 逻辑,下面是此函数的参数

首先先给指令上绑定的 value 赋值,后面判断如果 value 存在,尝试执行 filter 逻辑

因为我们没有定义 customFilter 所以执行 else , 在 Filters.js 文件下

去获取定义好的函数,将传进来的 hello 字符串首字母变成大写,然后将处理完成的值 return

  directive.update(
          directive.el,
          value,
          directive.argument,
          directive,
          seed
        );
      });

最后执行了指令定义的 update,看函数传参,我们有 dom ,value ,directive 下的参数,directive 本身,seed 实例本身,也就是说,所有的数据都有了,update 也早就定义好了对应的行为,那么此函数调用的时候,便会执行,对应的逻辑,这些逻辑都是简单的操作 dom , 就不再赘述了

好了,这也就是 vue 0.1 版本做到的,运行时 + 指令 + 响应式 的一个实现了

本文使用 文章同步助手 同步

上一篇 下一篇

猜你喜欢

热点阅读