代码之家  ›  专栏  ›  技术社区  ›  Alexander Abakumov

注入器vs viewContainerRef.Injector vs viewContainerRef.ParentInjector

  •  1
  • Alexander Abakumov  · 技术社区  · 7 年前

    假设我们有以下内容:

    @Directive({ selector: "[appSome]" })
    export class SomeDirective {
        public constructor(
            private viewContainerRef: ViewContainerRef,
            private injector: Injector,
        ) {
          console.log(`injector === viewContainerRef.injector: ${injector === viewContainerRef.injector}`);
          console.log(`injector === viewContainerRef.parentInjector: ${injector === viewContainerRef.parentInjector}`);
          console.log(`viewContainerRef.injector === viewContainerRef.parentInjector: ${viewContainerRef.injector === viewContainerRef.parentInjector}`);
        }
    }
    

    这三个喷油器有什么区别?

    1. this.injector
    2. this.viewContainerRef.injector
    3. this.viewContainerRef.parentInjector

    在上面的测试中,它们都是不同的实例。

    1 回复  |  直到 7 年前
        1
  •  3
  •   yurzui    7 年前

    首先, 喷油器 你进入构造器是所谓的 Merge Injector .

    这是它 definition 以下内容:

    class Injector_ implements Injector {
      constructor(private view: ViewData, private elDef: NodeDef|null) {}
      ...
    }
    

    Angular只获取视图数据和节点定义,并可以在需要时通过 createInjector 功能:

    export function createInjector(view: ViewData, elDef: NodeDef): Injector {
      return new Injector_(view, elDef);
    }
    

    现在让我们回到你的指示:

                                     SomeDirective 
                                         |
                                        deps
                                   /            \
                            Injector         ViewContainer
    

    创建指令实例,角度通过专用函数解析依赖项 resolveDep

    export function resolveDep(view, elDef) {
      ...
      case ViewContainerRefTokenKey:
         return asElementData(searchView, elDef.nodeIndex).viewContainer;
      ...
      case InjectorRefTokenKey:
         return createInjector(searchView, elDef);
      ...
    }
    

    假设您有一个组件,比如:

    @Component({
      selector: 'my-app',
      template: '<h2 appSome>Hello</h2>'
    })
    export class AppComponent {} 
    

    在这种情况下:

                                    SomeDirective 
                                         |
                                        deps
                                   /               \
                       Injector                      ViewContainer
                          ||                              ||
                          \/                              \/
      resolveDep(AppComponent view, h2 elDef)       resolveDep(AppComponent view, h2 elDef)
                          ||                              ||    
                          \/                              \/   
                     createInjector               viewContainerRef (AppComponent view, h2 elDef)
                                                   (created early)
                          ||
                          \/
          new Injector(AppComponent view, h2 elDef)       
    

    ViewContainerRef 实例是在视图节点创建的早期创建的。因为你需要 查看容器引用 通过DI角标记 h2 带有特殊标志的节点,这样它就可以实例化 查看容器引用 store h2节点数据中的此实例。

    if (nodeDef.flags & 16777216 /* EmbeddedViews */) {
       nodeData.viewContainer = createViewContainerData(view, nodeDef, nodeData);
    }
    

    在哪里? createViewContainerData 以下内容:

    export function createViewContainerData(
        view: ViewData, elDef: NodeDef, elData: ElementData): ViewContainerData {
      return new ViewContainerRef_(view, elDef, elData);
    }
    

    所以我们这里有: Injector ViewContainer 这表明了相同的观点和相同的长辈。

    现在让我们看看viewContainerRef definition 以下内容:

    class ViewContainerRef_ implements ViewContainerData {
      ...
      constructor(private _view: ViewData, private _elDef: NodeDef, private _data: ElementData) {}
      ...
      get injector(): Injector { return new Injector_(this._view, this._elDef); }
    
      get parentInjector(): Injector {
        let view = this._view;
        let elDef = this._elDef.parent;
        while (!elDef && view) {
          elDef = viewParentEl(view);
          view = view.parent !;
        }
    
        return view ? new Injector_(view, elDef) : new Injector_(this._view, null);
      }
      ...
    }
    

    案例1

    injector === viewContainerRef.injector  => fail
    

    因为 viewContainerRef.Injector getter创建新实例 具有相同视图和接骨板的注入器。

    因此,以下是正确的:

    injector.view === viewContainerRef.injector.view
    injector.elDef === viewContainerRef.injector.elDef
    

    案例2

    injector === viewContainerRef.parentInjector => fail
    

    因为parentInjector getter将使用父视图和父eldef获取新的注入器实例。

    这里的父视图是主机视图,eldef是我的应用程序。

    案例3

    viewContainerRef.injector === viewContainerRef.parentInjector  => fail
    

    应该很明显,他们不平等,因为指向不同的观点和长辈也是通过 new 操作员。


    最后,您可以阅读:

    What you always wanted to know about Angular Dependency Injection tree