高级 Vue 组件模式 (6)

06 经过 directive 加强组件内容

目标

以前的五篇文章中,switch 组件一直是被视为内部组件存在的,细心的读者应该会发现,这个组件除了帮咱们提供开关的交互之外,还会根据当前 toggle 的开关状态,为 button 元素增长 aria-expanded 属性,以 aira 开头的属性叫做内容加强属性,它用于描述当前元素的某种特殊状态,帮助残障人士更好地浏览网站内容。html

可是,做为组件调用者,未必会对使用这种相关属性对网站内容进行加强,那么如何更好地解决这个问题呢?答案就是使用 directive。vue

咱们指望可以显示地声明当前的元素是一个 toggler 职能的组件或者元素,这个组件或者元素,能够根据当前 toggle 组件的开关状态,动态地更新它自己的 aria-expanded 属性,以便针对无障碍访问提供适配。node

实现

简单实现

首先建立一个 toggler 指令函数,以下:git

export default function(el, binding, vnode) {
  const on = binding.value

  if (on) {
    el.setAttribute(`aria-expanded`, true);
  } else {
    el.removeAttribute(`aria-expanded`, false);
  }
}

这个指令函数很简单,就是经过传入指令的表达式的值来断定,是否在当前元素上增长一个 aria-expanded 属性。以后再 app 引入该指令,以下:angularjs

directives: {
  toggler
}

以后就能够在 app 组件的模板中使用该指令了,好比:github

<custom-button v-toggler="status.on" ref="customButton" :on="status.on" :toggle="toggle"></custom-button>

一切都将按预期中运行,当 toggle 组件的状态为开时,custom-button 组件的根元素会增长一个 aria-expanded="true" 的内容加强属性。web

Note: 这里关于指令的引入,使用的函数简写的方式,会在指令的 bind 和 update 钩子函数中触发相同的逻辑,vue 中的指令包含 5 个不一样的钩子函数,这里就不赘述了,不熟悉的读者能够经过阅读官方文档来了解。app

注入当前组件实例

上文中的指令会经过 binding.value 来获取 toggle 组件的开关状态,这样虽然可行,但在使用该指令时,custom-button 自己的 prop 属性 on 已经表明了当前的开关状态,可否直接在指令中获取当前所绑定的组件实例呢?答案是能够的。指令函数的第三个参数即为当前所绑定组件的虚拟 dom 节点实例,其 componentInstance 属性指向当前组件实例,因此能够将以前的指令改版以下:dom

export default function(el, binding, vnode) {
  const comp = vnode.componentInstance;
  const on = binding.value || comp.on;

  if (on) {
    el.setAttribute(`aria-expanded`, true);
  } else {
    el.removeAttribute(`aria-expanded`, false);
  }
}

这样,即便不向指令传入表达式,它也能够自动去注入当前修饰组件所拥有的 prop 属性 on 的值,以下:函数

<custom-button v-toggler ref="customButton" :on="status.on" :toggle="toggle"></custom-button>

提供更多灵活性

指令函数的第二个参数除了能够获取传入指令内部的表达式的值之外,还有其余若干属性,好比 name、arg、modifiers等,详细说明能够去参考官方文档。

为了尽量地使指令保证灵活性,咱们指望能够自定义无障碍属性 aria 的后缀名称,好比叫作 aria-on,这里咱们能够经过 arg 这个参数轻松实现,改版以下:

export default function(el, binding, vnode) {
  const comp = vnode.componentInstance;
  const suffix = binding.arg || "expanded";
  const on = binding.value || comp.on;

  if (on) {
    el.setAttribute(`aria-${suffix}`, true);
  } else {
    el.removeAttribute(`aria-${suffix}`, false);
  }
}

能够发现,这里经过 binding.arg 来获取无障碍属性的后缀名称,并当没有传递该参数时,降级至 expanded。这里仅仅是为了演示,读者有兴趣的话,还能够利用 binding 对象的其余属性提供更多的灵活性。

成果

最终的运行结果就不用语言描述了,直接截了一个图,是 toggle 组件开关状态为开时的截图:
clipboard.png

你能够下面的连接来看看这个组件的实现代码以及演示:

总结

关于指令的概念,我自身仍是在 angularjs(v1.2如下版本) 中第一次接触,当时其实不兴组件化开发这个概念,指令自己的设计理念也是基于加强这个概念的,即加强某个 html 标签。到后来兴起了组件化开发的开发思想,指令彷佛是随着 angularjs 的没落而消失了踪迹。

但仔细想一想的话,web 开发流程中,并非全部的场景均可以拿组件来抽象和描述的,好比说,你想提供一个相似高亮边框的公用功能,到底如何来按组件化的思想抽象它呢?这时候使用指令每每是一个很好的切入点。

所以,当你面临解决的问题,颗粒度小于组件化抽象的粒度,同时又具有复用性,那就大胆的使用指令来解决它吧。

目录

github gist

关注公众号 全栈101,只谈技术,不谈人生

clipboard.png

相关文章
相关标签/搜索