自定義事件
我們知道,父組件是使用 props 傳遞數據給子組件,但如果子組件要把數據傳遞迴去,應該怎樣做?那就是自定義事件!使用 v-on 綁定自定義事件每個 Vue 實例都實現了事件接口(Events interface),即:
使用 $on(eventName) 監聽事件使用 $emit(eventName) 觸發事件
另外,父組件可以在使用子組件的地方直接用 v-on 來監聽子組件觸發的事件。
舉個慄子:
如下:
給組件綁定原生事件
有時候,你可能想在某個組件的根元素上監聽一個原生事件。可以使用 .native 修飾 v-on 。例如:
使用自定義事件的表單輸入組件
自定義事件也可以用來創建自定義的表單輸入組件,使用 v-model 來進行數據雙向綁定。牢記,表單控制項進行數據綁定時的語法:
僅僅是一個語法糖:
所以在組件中使用時,它相當於下面的簡寫:
所以要讓組件的 v-model 生效,它必須:
接受一個 value 屬性在有新的 value 時觸發 input 事件
舉個慄子:
非父子組件通信
有時候非父子關係的組件也需要通信。在簡單的場景下,使用一個空的 Vue 實例作為中央事件總線:
使用 Slots 分發內容
在使用組件時,常常要像這樣組合它們:
注意兩點:
<app> 組件不知道它的掛載點會有什麼內容。掛載點的內容是由<app>的父組件決定的。<app> 組件很可能有它自己的模版。
為了讓組件可以組合,我們需要一種方式來混合父組件的內容與子組件自己的模板。這個過程被稱為 內容分發 (或 「transclusion」 如果你熟悉 Angular)。Vue.js 實現了一個內容分發 API ,參照了當前 Web組件規範草案,使用特殊的 <slot> 元素作為原始內容的插槽。
編譯作用域
在深入內容分發 API 之前,我們先明確內容的編譯作用域。假定模板為:
message 應該綁定到父組件的數據,還是綁定到子組件的數據?答案是父組件。組件作用域簡單地說是:
父組件模板的內容在父組件作用域內編譯;子組件模板的內容在子組件作用域內編譯。
一個常見錯誤是試圖在父組件模板內將一個指令綁定到子組件的屬性/方法:
假定 someChildProperty 是子組件的屬性,上例不會如預期那樣工作。父組件模板不應該知道子組件的狀態。
如果要綁定子組件內的指令到一個組件的根節點,應當在它的模板內這麼做:
類似地,分發內容是在父組件作用域內編譯。
單個 Slot
除非子組件模板包含至少一個 <slot> 插口,否則父組件的內容將會被丟棄。當子組件模板只有一個沒有屬性的 slot 時,父組件整個內容片段將插入到 slot 所在的 DOM 位置,並替換掉 slot 標籤本身。
最初在 <slot> 標籤中的任何內容都被視為備用內容。備用內容在子組件的作用域內編譯,並且只有在宿主元素為空,且沒有要插入的內容時才顯示備用內容。
假定 my-component 組件有下面模板:
父組件模版:
渲染結果:
具名Slots
元素可以用一個特殊的屬性 name 來配置如何分發內容。多個 slot 可以有不同的名字。具名 slot 將匹配內容片段中有對應 slot 特性的元素。
仍然可以有一個匿名 slot ,它是默認 slot ,作為找不到匹配的內容片段的備用插槽。如果沒有默認的 slot ,這些找不到匹配的內容片段將被拋棄。
例如,假定我們有一個 app-layout 組件,它的模板為:
父組件模版:
渲染結果為:
在組合組件時,內容分發 API 是非常有用的機制。
動態組件
多個組件可以使用同一個掛載點,然後動態地在它們之間切換。使用保留的
也可以直接綁定到組件對象上:
keep-alive如果把切換出去的組件保留在內存中,可以保留它的狀態或避免重新渲染。為此可以添加一個 keep-alive 指令參數:
雜項
編寫可復用組件在編寫組件時,記住是否要復用組件有好處。一次性組件跟其它組件緊密耦合沒關係,但是可復用組件應當定義一個清晰的公開接口。
Vue 組件的 API 來自三部分 - props, events 和 slots :
Props 允許外部環境傳遞數據給組件Events 允許組件觸發外部環境的副作用Slots 允許外部環境將額外的內容組合在組件中。
使用 v-bind 和 v-on 的簡寫語法,模板的縮進清楚且簡潔:
子組件索引
儘管有 props 和 events ,但是有時仍然需要在 JavaScript 中直接訪問子組件。為此可以使用 ref 為子組件指定一個索引 ID 。例如:
當 ref 和 v-for 一起使用時, ref 是一個數組或對象,包含相應的子組件。$refs 只在組件渲染完成後才填充,並且它是非響應式的。它僅僅作為一個直接訪問子組件的應急方案——應當避免在模版或計算屬性中使用 $refs。
異步組件
在大型應用中,我們可能需要將應用拆分為多個小模塊,按需從伺服器下載。為了讓事情更簡單, Vue.js 允許將組件定義為一個工廠函數,動態地解析組件的定義。Vue.js 只在組件需要渲染時觸發工廠函數,並且把結果緩存起來,用於後面的再次渲染。例如:
工廠函數接收一個 resolve 回調,在收到從伺服器下載的組件定義時調用。也可以調用 reject(reason) 指示加載失敗。這裡 setTimeout 只是為了演示。怎麼獲取組件完全由你決定。推薦配合使用 :Webpack 的代碼分割功能:
你可以使用 Webpack 2 + ES2015 的語法返回一個 Promise resolve 函數:
如果你是 Browserify 用戶,可能就無法使用異步組件了,它的作者已經表明 Browserify 是不支持異步加載的。如果這個功能對你很重要,請使用 Webpack。
組件命名約定
當註冊組件(或者 props)時,可以使用 kebab-case ,camelCase ,或 TitleCase 。Vue 不關心這個。
在 HTML 模版中,請使用 kebab-case 形式:
當使用字符串模式時,可以不受 HTML 的 case-insensitive 限制。這意味實際上在模版中,你可以使用 camelCase 、 PascalCase 或者 kebab-case 來引用你的組件和 prop:
如果組件未經 slot 元素傳遞內容,你甚至可以在組件名後使用 / 使其自閉合:
當然,這只在字符串模版中有效。因為自閉的自定義元素是無效的 HTML ,瀏覽器原生的解析器也無法識別它。