怎樣理解Angular2中的ViewContainerRef,ViewRef和TemplateRef?
這三者之間有啥聯繫與區別,它們跟html的標籤是啥關係呢?
很有深度的問題。
歡迎參考我的(還沒寫完的)文章:知乎專欄
寫在前面
先說一個可能很多人都不知道的事情, ViewContainerRef,ViewRef 和 TemplateRef 這三個東西都是無法通過注入器進行注入的。肯定有人會說,他/她今天就注入了(ViewContainerRef 或 TemplateRef)這個東西,要怎麼解釋呢?
如果仔細讀的話,會發現我說的是無法通過注入器注入。是的,你沒猜錯,Angular (2+,下同)中並不是所有依賴注入都走注入器的。或者說:
constructor(service: Service) {
// code use service
}
和
constructor(injector: Injector) {
const service = injector.get(Service)
// code use service
}
並不總是等價的(這裡不考慮覆蓋 Token 的情況,雖然那個情況也可能導致這個結論,但和本文沒有關係)。
比如我們嘗試使用 injector.get(ViewContainerRef) 來得到 ViewContainerRef 的話,只能得到一個:
EXCEPTION: No provider for ViewContainerRef!
當然,並不僅僅是 ViewContainerRef,其他的一些特殊依賴,比如 TemplateRef 或 ElementRef,也是只能通過構造函數直接注入,並不經過注入器。
ViewRef 本來就不是可以注入的,當然和注入器也沒什麼關係。
開頭廢話了這麼多,只是想強調一下,有困惑是正常的,這些東西確實就是特殊的存在。當然,其實也可以放心,正常的應用開發並不需要用到這些東西。
為什麼會有這些東西?
太長不讀:Angular 2 為了保持平台無關,又要能實現細粒度操作,自己構造了一個抽象視圖層。(並不是 Virtual DOM)
Angular 2+ 一樣繼承了 Angular 1 的 Compile 和 Link 的過程,不過並不完全一樣。對於所有 Template,都需要獲得一個 Context(對應 Angular 1 中的 Scope),才能實例化得到 View。不過由於 Component 中這個過程是自動化的,所以我們不怎麼能感受得到,而在 Structural Directive 中,一個 Template 可能對應不止一個 View,而且可能會涉及到動態增刪 View,所以需要提供相關的 API,也就是這一系列東西。
這幾個東西的關係?
ViewContainerRef 提供了一些動態內容操作的 API,比如 createEmbeddedView 和 createComponent,能夠通過 Component 或者 Template 來實例化得到 ViewRef,以及 insert、move、indexOf、remove、detach、clear、get 等方法來提供抽象的視圖層操作(類似於 DOM 操作)。
因為是抽象的,所以不論是在瀏覽器中,原生 App 還是伺服器端都能運行(配合不同 Renderer)。
至於後面加 Ref 的一般就是提供 API 供用戶操作的類型(往往還有對應的不加 Ref 的內部類)。
和 HTML 的關係?
每一個 Component 和 Directive 的實例都必須對應一個 Host(宿主),在 Web 平台上,也就是 DOM Element(或者說 DOM Node)。
由於 Angular 的 View 層是比 DOM API 更高的抽象,所以其實並沒有完全的一一對應。比如對於非 template 的 Host 而言,ViewContainerRef 和 ElementRef 實際上都對應的宿主 Element,只是對應不同的 Role(職責),ViewContainerRef 對應於 Element 的容器身份,確切地說是 Element 所處的位置空間的容器身份(比如 append 操作的主體,但注意添加的內容會和宿主同級),而 ElementRef 對應 Element 的控制項身份(比如 className、value 這些屬性)。
而對於宿主為 template 的情況,因為語義上的特殊性,ElementRef 往往沒有意義,從而提供一個額外的身份——TemplateRef,通過接受動態的 Context 來實現複雜的實例化。
而不論是 Component,還是 TemplateRef,實例化之後都會得到 ViewRef(先別糾結 HostView 和 EmbeddedView 的區別),ViewRef 對應的當然也還是 DOM Element(也可能是一組 Element,針對 ng-container 的情況),當然這裡的身份是相對容器而言的客體(例如 append 操作所接受的參數)。
如果有 OO 基礎的話,可以理解成,(在 Web 上)這些東西都是 Element 類型所實現的某個介面。
總結
沒什麼好總結的,看得懂就看,看不懂的話,其實我也沒什麼辦法。
如果想知道不通過注入器是如果注入的依賴?不妨先點個贊,我有空加上來。(其實只要有心基本上自己也能找的到)


個人認為
ViewContainerRef主要和渲染節點相關, templateRef是模板和組件(我們寫的component的代碼)的所有信息.
viewRef包含所有綁定的信息, 最全的就是viewRef.
至於注入的問題, 因為組件(NgIf之類)要用到所以開放了ViewContainerRef給我們.
補充@VTHINKXIE , ng-bootstrap (ng-bootstrap/ng-bootstrap) 軍功章上有 Pawel Kozlowski 的一半,repo 里到處都是這些 ref,有興趣的話可以黑一下。
推薦閱讀:
