注入器 vs 视图容器参考注入器 vs 视图容器参考的父注入器

3
假设我们有以下内容:
@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

首先,你在构造函数中获取的注入器(Injector)被称为合并注入器(Merge Injector)

这是它的定义

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

为了创建指令实例,Angular会通过专用函数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 获取 ViewContainerRef,Angular 会为 h2 节点打上特殊标志,以便实例化 ViewContainerRef 并将此实例存储在 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);
}

这里有一个:指向相同视图和相同 elDef 的 InjectorViewContainer

现在让我们看一下 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);
  }
  ...
}

案例1

injector === viewContainerRef.injector  => fail

因为 viewContainerRef.injector 获取器会创建具有相同视图和 elDef 的新 Injector 实例。

因此以下内容是正确的:

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

Case 2

injector === viewContainerRef.parentInjector => fail

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

这里的父视图是宿主视图,而elDef是my-app。

案例3

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

很明显它们不相等,因为它们指向不同的视图和elDef,并且是通过new运算符创建的。
最后,您可以阅读:

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接