vue.js深入篇(四、Render函數)

vue.js深入篇(四、Render函數)

vue.js深入篇(四、Render函數)

基礎

Vue 推薦使用在絕大多數情況下使用 template 來創建你的 HTML。然而在一些場景中,你真的需要 JavaScript 的完全編程的能力,這就是 render 函數,它比 template 更接近編譯器。

讓我們先深入一個使用 render 函數的簡單例子,假設你想生成一個帶錨鏈接的標題:

vue.js深入篇(四、Render函數)

在 HTML 層, 我們決定這樣定義組件接口:

vue.js深入篇(四、Render函數)

當我們開始寫一個通過 level prop 動態生成heading 標籤的組件,你可很快能想到這樣實現:

vue.js深入篇(四、Render函數)

template 在這種場景中就表現的有些冗餘了。雖然我們重複使用 來接收每一個級別的標題標籤,在標題標籤中添加相同的錨點元素。但是些都會被包裹在一個無用的 div 中,因為組件必須有根節點。

雖然模板在大多數組件中都非常好用,但是在這裡它就不是很簡潔的了。那麼,我們來嘗試使用 render 函數重寫上面的例子:

vue.js深入篇(四、Render函數)

簡單清晰很多!簡單來說,這樣代碼精簡很多,但是需要非常熟悉 Vue 的實例屬性。在這個例子中,你需要知道當你不使用 slot 屬性向組件中傳遞內容時,比如 anchored-heading 中的 Hello world!, 這些子元素被存儲在組件實例中的 $slots.default中。如果你還不瞭解, 在深入 render 函數之前推薦閱讀 instance properties API。

createElement 參數

第二件你需要熟悉的是如何在 createElement 函數中生成模板。這裡是 createElement 接受的參數:

vue.js深入篇(四、Render函數)

完整數據對象

有一件事要注意:在 templates 中,v-bind:class 和 v-bind:style ,會有特別的處理,他們在 VNode 數據對象中,為最高級配置。

vue.js深入篇(四、Render函數)

vue.js深入篇(四、Render函數)

完整示例

有了這方面的知識,我們現在可以完成我們最開始想實現的組件:

vue.js深入篇(四、Render函數)

約束

VNodes 必須唯一

所有組件樹中的 VNodes 必須唯一。這意味著,下面的 render function 是無效的:

vue.js深入篇(四、Render函數)

如果你真的需要重複很多次的元素/組件,你可以使用工廠函數來實現。例如,下面這個例子 render 函數完美有效地渲染了 20 個重複的段落:

vue.js深入篇(四、Render函數)

使用 JavaScript 代替模板功能

無論什麼都可以使用原生的 JavaScript 來實現,Vue 的 render 函數不會提供專用的 API。比如, template 中的 v-if 和 v-for:

vue.js深入篇(四、Render函數)

這些都會在 render 函數中被 JavaScript 的 if/else 和 map 重寫:

vue.js深入篇(四、Render函數)

JSX

如果你寫了很多 render 函數,可能會覺得痛苦:

vue.js深入篇(四、Render函數)

特別是模板如此簡單的情況下:

vue.js深入篇(四、Render函數)

這就是會有一個 Babel plugin 插件,用於在 Vue 中使用 JSX 語法的原因,它可以讓我們回到於更接近模板的語法上。

vue.js深入篇(四、Render函數)

將 h 作為 createElement 的別名是一個通用慣例,你會發現在 Vue 生態系統中,實際上必須用到 JSX,如果在作用域中 h 失去作用, 在應用中會觸發報錯。

函數化組件

之前創建的錨點標題組件是比較簡單,沒有管理或者監聽任何傳遞給他的狀態,也沒有生命週期方法。它只是一個接收參數的函數。

在這個例子中,我們標記組件為 functional, 這意味它是無狀態(沒有 data),無實例(沒有 this 上下文)。

一個 函數化組件 就像這樣:

vue.js深入篇(四、Render函數)

組件需要的一切都是通過上下文傳遞,包括:

  • props: 提供props 的對象
  • children: VNode 子節點的數組
  • slots: slots 對象
  • data: 傳遞給組件的 data 對象
  • parent: 對父組件的引用

在添加 functional: true 之後,錨點標題組件的 render 函數之間簡單更新增加 context 參數,this.$slots.default 更新為 context.children,之後this.level 更新為 context.props.level。

函數化組件只是一個函數,所以渲染開銷也低很多。但同樣它也有完整的組件封裝,你需要知道這些, 比如:

  • 程序化地在多個組件中選擇一個
  • 在將 children, props, data 傳遞給子組件之前操作它們。

下面是一個依賴傳入 props 的值的 smart-list 組件例子,它能代表更多具體的組件:

vue.js深入篇(四、Render函數)

slots() 和 children 對比

你可能想知道為什麼同時需要 slots() 和 children。slots().default 不是和 children 類似的嗎?在一些場景中,是這樣,但是如果是函數式組件和下面這樣的 children 呢?

vue.js深入篇(四、Render函數)

對於這個組件,children 會給你兩個段落標籤,而 slots().default 只會傳遞第二個匿名段落標籤,slots().foo 會傳遞第一個具名段落標籤。同時擁有 children 和 slots() ,因此你可以選擇讓組件通過 slot() 系統分發或者簡單的通過 children 接收,讓其他組件去處理。


分享到:


相關文章: