Injector vs ViewContainerRef.injector vs ViewContainerRef.parentInjector

Ale*_*mov 1 dependency-injection typescript angular

假设我们有以下内容:

@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}`);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

yur*_*zui 5

首先,您在构造函数中获得的Injector是所谓的Merge Injector

这是它的定义

class Injector_ implements Injector {
  constructor(private view: ViewData, private elDef: NodeDef|null) {}
  ...
}
Run Code Online (Sandbox Code Playgroud)

Angular 只获取视图数据和节点定义,并且可以在需要时通过createInjector函数实例化 Injector 实例:

export function createInjector(view: ViewData, elDef: NodeDef): Injector {
  return new Injector_(view, elDef);
}
Run Code Online (Sandbox Code Playgroud)

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

                                 SomeDirective 
                                     |
                                    deps
                               /            \
                        Injector         ViewContainer
Run Code Online (Sandbox Code Playgroud)

创建指令实例 Angular 通过专用函数resolveDep解析依赖

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

让我们假设你有一个像这样的组件:

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

在这种情况下:

                                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)       
Run Code Online (Sandbox Code Playgroud)

ViewContainerRef实例是在创建视图节点的早期创建的。由于您需要ViewContainerRef通过 DI Angular 标记h2具有特殊标志的节点,因此它可以实例化此实例ViewContainerRef并将其存储在 h2 节点数据中。

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

其中createViewContainerData

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

所以,我们在这里:InjectorViewContainer该点到相同的观点,并同elDef。

现在让我们看看 ViewContainerRef定义

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);
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

情况1

injector === viewContainerRef.injector  => fail
Run Code Online (Sandbox Code Playgroud)

因为viewContainerRef.injector getter 创建了具有相同视图和 elDef 的 Injector新实例

所以以下是正确的:

injector.view === viewContainerRef.injector.view
injector.elDef === viewContainerRef.injector.elDef
Run Code Online (Sandbox Code Playgroud)

案例二

injector === viewContainerRef.parentInjector => fail
Run Code Online (Sandbox Code Playgroud)

因为 parentInjector getter 将获得具有父视图和父 elDef 的 Injector 的新实例。

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

案例3

viewContainerRef.injector === viewContainerRef.parentInjector  => fail
Run Code Online (Sandbox Code Playgroud)

应该很明显,它们不相等,因为也指向不同的视图和 elDef 并且是通过new操作符创建的。


最后,您可以阅读:

你一直想知道的关于 Angular 依赖注入树的知识