高級 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

相關文章
相關標籤/搜索