如何仅按文件夹名称导入桶?

时间:2021-05-11 00:29:34

Angular 2 Barrels

In Angular 2, I'm trying to get barrels to work as described in the documentation.

在Angular 2中,我正试图让文件中描述的桶工作。

The official Angular 2 style guide talks about using barrels to aggregate and shorten import statements.

官方的Angular 2风格指南讨论了使用桶来汇总和缩短进口报表。

I'm finding out that for some barrels, I have to specify the index JavaScript file name on the import when I shouldn't have to.

我发现对于一些桶,我必须在导入时指定索引JavaScript文件名,而我不应该这样做。

Barrel Example

(modify the app/app.component.ts file on line 12)

(修改第12行的app / app.component.ts文件)

After have encountered this in my actual project (running under ASP.NET) I have created a Plunker to demonstrate the problem where I modified the Tour of Heroes to use barrels.

在我的实际项目中遇到这个(在ASP.NET下运行)后,我创建了一个Plunker来演示我修改了英雄之旅以使用桶的问题。

In app/app.component, the basic way to import is like this:

在app / app.component中,导入的基本方法如下:

import { HeroService } from './hero.service';
import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';

But, to use a barrel instead, the import definition would look like this:

但是,要使用桶,导入定义将如下所示:

import {
  HeroService,
  HeroesComponent,
  HeroDetailComponent
} from '../app';

The from '../app'; line indicates a file with the name of index.ts that contain the exported/imported components:

来自'../app'; line表示名称为index.ts的文件,其中包含导出/导入的组件:

// app/index.ts
export * from './hero-detail.component';
export * from './hero.service';
export * from './heroes.component';

But this doesn't work for me in all cases. The only way I've gotten this to work correctly is by explicitly including the index file name:

但在所有情况下,这对我都不起作用。我能够正常工作的唯一方法是明确包含索引文件名:

import {
  HeroService,
  HeroesComponent,
  HeroDetailComponent
} from '../app/index'; // have to indicate 'index'

How can I get this to work where the index.js file name is implied?

如何在隐含index.js文件名的地方使用它?

3 个解决方案

#1


8  

AFAIK SystemJS doesn't understand barrels by itself but Webpack does. BTW, After digging up how Angular does it for it's modules, I've found a solution

AFAIK SystemJS本身并不了解桶,但Webpack确实如此。顺便说一句,在深入了解Angular为它的模块做了什么之后,我找到了一个解决方案


In system.config.js you'll need to do the following things

在system.config.js中,您需要执行以下操作

Note: the parent directory of a index.ts is the barrel, ICYDK

注意:index.ts的父目录是桶,ICYDK

  • Add the paths of your barrels
  • 添加桶的路径

// map tells the System loader where to look for things
  var map = {
    'app':                        'app', // 'dist',
    'rxjs':                       'node_modules/rxjs',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    '@angular':                   'node_modules/@angular',

    'barrel':                 'path/to/your/barrel'
  };
  • Don't add barrels to packages (explained later)##
  • 不要将桶添加到包装中(稍后解释)##

// packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { main: 'app/boot.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' }
  };
  • Add them to packageNames, just like angular
  • 将它们添加到packageNames,就像angular一样

var packageNames = [
    '@angular/common',
    ...
    '@angular/upgrade',

    'barrel'
  ];

And you're done.

而且你已经完成了。


##

Why we used packageNames instead of packages is because you'll have to repeat the { main: 'index.js', defaultExtension: 'js' } (filename and extension) for every barrel, but angular is already doing it by looping with this.

为什么我们使用packageNames而不是package是因为你必须为每个桶重复{main:'index.js',defaultExtension:'js'}(文件名和扩展名),但是角度已经通过循环使用。

packageNames.forEach(function(pkgName) {
    packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});

Which is ultimately adding them to packages.

哪个最终将它们添加到包中。


Usage

import {something} from '../../barrel'; // relative path to directory of barrel

and

import {something} from 'barrel'; // name of barrel

Both work, but the later one fails to provide intellisense and shows an error saying cannot find module 'barrel'. Which I don't have a solution for. But I'll add it when I do.

两者都有效,但后者无法提供智能感知并显示错误,说无法找到模块'桶'。我没有解决方案。但是当我这样做时,我会添加它。

#2


5  

This is all seems overly complicated.

这一切看起来都过于复杂。

There's no need to add anything to map, since app, which should be containing everything is already in there. We can just create an array with the subpackages. In my case that was:

没有必要添加任何东西来映射,因为应该包含所有内容的app已经在那里。我们可以用子包创建一个数组。在我的情况下是:

var subPackageNames = [
        '+heroes',
        '+heroes/shared',
        '+heroes/hero-list',
        '+heroes/hero-detail',
        '+heroes/hero-dashboard'
    ];

and then modify the provided packIndex function to take a second argument

然后修改提供的packIndex函数以获取第二个参数

function packIndex(pkgName, baseName) {
    packages[baseName+pkgName] = { main: 'index.js', defaultExtension: 'js' };
}

now we can add our sub packages to the packages object just like angular does.

现在我们可以像包括angular一样将子包添加到packages对象。

ngPackageNames.forEach(name => setPackageConfig(name, '@angular/'));
subPackageNames.forEach(name => packIndex(name, 'app/'));

#3


0  

I followed A_Singh's idea, but had to adapt it a little since my systemjs.config.js is different, based on the tour-of-heroes tutorial as of right now. (June 3, 2016 - RC1).

我遵循了A_Singh的想法,但由于我的systemjs.config.js不同,因此我必须根据现在的英雄之旅教程调整它。 (2016年6月3日 - RC1)。

This modification works, and under VisualStudio Code, Intellisense worked well for me.

这种修改有效,在VisualStudio Code下,Intellisense对我来说效果很好。

I have also, been playing with directory structure to keep things modular, and here is where the barrels made sense.

我也一直在玩目录结构以保持模块化,这里的桶是有意义的。

This is how I have modified the tour-of-heroes project

这就是我修改“英雄之旅”项目的方法

app
|- main.ts
|- app.component.ts
|- app.component.css
|-+ components
| |-+ dashboard
|   |- dashboard.component.css
|   |- dashboard.component.html
|   |- dashboard.component.ts
|-+ modules
| |-+ hero
|   |- index.ts   <-- //The barrel for the Hero module
|   |-+ components 
|   | |-+ detail
|   | | |- hero-detail.component.css
|   | | |- hero-detail.component.html
|   | | |- hero-detail.component.ts
|   | |-+ list
|   |   |- hero-list.component.css
|   |   |- hero-list.component.html
|   |   |- hero-list.component.ts
|   |-+ models
|   | |- hero.model.ts
|   |-+ services
|     |- hero.service.ts
|     |- mock-heroes.ts

And here the updated systemjs.config.js

这里更新了systemjs.config.js

(function(global) {
  // map tells the System loader where to look for things
  var map = {
    'app':                        'app', // 'dist',
    '@angular':                   'node_modules/@angular',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    'rxjs':                       'node_modules/rxjs',

    // The added barrel map
    'hero':                       'app/modules/hero'
  };
  var packages = {
    'app':                        { main: 'main.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' },

    // The package definition (notice I had to declare index.js)
    'hero':                       { main: 'index.js',  defaultExtension: 'js' }
  };
  var ngPackageNames = [
    'common',
    'compiler',
    'core',
    'http',
    'platform-browser',
    'platform-browser-dynamic',
    'router',
    'router-deprecated',
    'upgrade',
  ];
  ngPackageNames.forEach(function(pkgName) {
    packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
  });
  var config = {
    map: map,
    packages: packages
  }
  System.config(config);
})(this);

And finally, here is how the app.component.ts now looks like

最后,这是app.component.ts现在的样子

import { Component } from '@angular/core';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';

import { DashboardComponent } from './components/dashboard/dashboard.component';
import { HeroListComponent, HeroDetailComponent, HeroService } from './modules/hero';

@Component({
    selector:   'my-app',
    directives: [ROUTER_DIRECTIVES],
    providers:  [
        ROUTER_PROVIDERS,
        HeroService
    ],
    template:   `
<h1>{{title}}</h1>
<nav>
    <a [routerLink]="['Dashboard']">Dashboard</a>
    <a [routerLink]="['HeroList']">Hero List</a>
</nav>
<router-outlet></router-outlet>
`,
    styleUrls: ['app/app.component.css']
})
@RouteConfig([
    {
        path: '/dashboard',
        name: 'Dashboard',
        component: DashboardComponent,
        useAsDefault: true
    },
    {
        path: '/hero-list',
        name: 'HeroList',
        component: HeroListComponent
    },
    {
        path: '/detail/:id',
        name: 'HeroDetail',
        component: HeroDetailComponent
    }
])
export class AppComponent {
    title: string = 'Tour of Heroes';
}

One final note, you could do the import as

最后一点,你可以做导入

import { HeroListComponent, HeroDetailComponent, HeroService } from 'hero';

And it will work, however, since it is not technically a NodeJS imported module, VS Code complains that it cannot find it. So, my personal choice is to leave the explicit ./modules/hero, it helps me know that it is one of my modules and not an imported one and I'm happy to not see red lines.

然而,它会起作用,因为它在技术上不是NodeJS导入的模块,VS Code抱怨它无法找到它。因此,我个人的选择是留下明确的./modules/hero,它帮助我知道它是我的模块之一而不是导入的模块,我很高兴看不到红线。

#1


8  

AFAIK SystemJS doesn't understand barrels by itself but Webpack does. BTW, After digging up how Angular does it for it's modules, I've found a solution

AFAIK SystemJS本身并不了解桶,但Webpack确实如此。顺便说一句,在深入了解Angular为它的模块做了什么之后,我找到了一个解决方案


In system.config.js you'll need to do the following things

在system.config.js中,您需要执行以下操作

Note: the parent directory of a index.ts is the barrel, ICYDK

注意:index.ts的父目录是桶,ICYDK

  • Add the paths of your barrels
  • 添加桶的路径

// map tells the System loader where to look for things
  var map = {
    'app':                        'app', // 'dist',
    'rxjs':                       'node_modules/rxjs',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    '@angular':                   'node_modules/@angular',

    'barrel':                 'path/to/your/barrel'
  };
  • Don't add barrels to packages (explained later)##
  • 不要将桶添加到包装中(稍后解释)##

// packages tells the System loader how to load when no filename and/or no extension
  var packages = {
    'app':                        { main: 'app/boot.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' }
  };
  • Add them to packageNames, just like angular
  • 将它们添加到packageNames,就像angular一样

var packageNames = [
    '@angular/common',
    ...
    '@angular/upgrade',

    'barrel'
  ];

And you're done.

而且你已经完成了。


##

Why we used packageNames instead of packages is because you'll have to repeat the { main: 'index.js', defaultExtension: 'js' } (filename and extension) for every barrel, but angular is already doing it by looping with this.

为什么我们使用packageNames而不是package是因为你必须为每个桶重复{main:'index.js',defaultExtension:'js'}(文件名和扩展名),但是角度已经通过循环使用。

packageNames.forEach(function(pkgName) {
    packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});

Which is ultimately adding them to packages.

哪个最终将它们添加到包中。


Usage

import {something} from '../../barrel'; // relative path to directory of barrel

and

import {something} from 'barrel'; // name of barrel

Both work, but the later one fails to provide intellisense and shows an error saying cannot find module 'barrel'. Which I don't have a solution for. But I'll add it when I do.

两者都有效,但后者无法提供智能感知并显示错误,说无法找到模块'桶'。我没有解决方案。但是当我这样做时,我会添加它。

#2


5  

This is all seems overly complicated.

这一切看起来都过于复杂。

There's no need to add anything to map, since app, which should be containing everything is already in there. We can just create an array with the subpackages. In my case that was:

没有必要添加任何东西来映射,因为应该包含所有内容的app已经在那里。我们可以用子包创建一个数组。在我的情况下是:

var subPackageNames = [
        '+heroes',
        '+heroes/shared',
        '+heroes/hero-list',
        '+heroes/hero-detail',
        '+heroes/hero-dashboard'
    ];

and then modify the provided packIndex function to take a second argument

然后修改提供的packIndex函数以获取第二个参数

function packIndex(pkgName, baseName) {
    packages[baseName+pkgName] = { main: 'index.js', defaultExtension: 'js' };
}

now we can add our sub packages to the packages object just like angular does.

现在我们可以像包括angular一样将子包添加到packages对象。

ngPackageNames.forEach(name => setPackageConfig(name, '@angular/'));
subPackageNames.forEach(name => packIndex(name, 'app/'));

#3


0  

I followed A_Singh's idea, but had to adapt it a little since my systemjs.config.js is different, based on the tour-of-heroes tutorial as of right now. (June 3, 2016 - RC1).

我遵循了A_Singh的想法,但由于我的systemjs.config.js不同,因此我必须根据现在的英雄之旅教程调整它。 (2016年6月3日 - RC1)。

This modification works, and under VisualStudio Code, Intellisense worked well for me.

这种修改有效,在VisualStudio Code下,Intellisense对我来说效果很好。

I have also, been playing with directory structure to keep things modular, and here is where the barrels made sense.

我也一直在玩目录结构以保持模块化,这里的桶是有意义的。

This is how I have modified the tour-of-heroes project

这就是我修改“英雄之旅”项目的方法

app
|- main.ts
|- app.component.ts
|- app.component.css
|-+ components
| |-+ dashboard
|   |- dashboard.component.css
|   |- dashboard.component.html
|   |- dashboard.component.ts
|-+ modules
| |-+ hero
|   |- index.ts   <-- //The barrel for the Hero module
|   |-+ components 
|   | |-+ detail
|   | | |- hero-detail.component.css
|   | | |- hero-detail.component.html
|   | | |- hero-detail.component.ts
|   | |-+ list
|   |   |- hero-list.component.css
|   |   |- hero-list.component.html
|   |   |- hero-list.component.ts
|   |-+ models
|   | |- hero.model.ts
|   |-+ services
|     |- hero.service.ts
|     |- mock-heroes.ts

And here the updated systemjs.config.js

这里更新了systemjs.config.js

(function(global) {
  // map tells the System loader where to look for things
  var map = {
    'app':                        'app', // 'dist',
    '@angular':                   'node_modules/@angular',
    'angular2-in-memory-web-api': 'node_modules/angular2-in-memory-web-api',
    'rxjs':                       'node_modules/rxjs',

    // The added barrel map
    'hero':                       'app/modules/hero'
  };
  var packages = {
    'app':                        { main: 'main.js',  defaultExtension: 'js' },
    'rxjs':                       { defaultExtension: 'js' },
    'angular2-in-memory-web-api': { defaultExtension: 'js' },

    // The package definition (notice I had to declare index.js)
    'hero':                       { main: 'index.js',  defaultExtension: 'js' }
  };
  var ngPackageNames = [
    'common',
    'compiler',
    'core',
    'http',
    'platform-browser',
    'platform-browser-dynamic',
    'router',
    'router-deprecated',
    'upgrade',
  ];
  ngPackageNames.forEach(function(pkgName) {
    packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };
  });
  var config = {
    map: map,
    packages: packages
  }
  System.config(config);
})(this);

And finally, here is how the app.component.ts now looks like

最后,这是app.component.ts现在的样子

import { Component } from '@angular/core';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '@angular/router-deprecated';

import { DashboardComponent } from './components/dashboard/dashboard.component';
import { HeroListComponent, HeroDetailComponent, HeroService } from './modules/hero';

@Component({
    selector:   'my-app',
    directives: [ROUTER_DIRECTIVES],
    providers:  [
        ROUTER_PROVIDERS,
        HeroService
    ],
    template:   `
<h1>{{title}}</h1>
<nav>
    <a [routerLink]="['Dashboard']">Dashboard</a>
    <a [routerLink]="['HeroList']">Hero List</a>
</nav>
<router-outlet></router-outlet>
`,
    styleUrls: ['app/app.component.css']
})
@RouteConfig([
    {
        path: '/dashboard',
        name: 'Dashboard',
        component: DashboardComponent,
        useAsDefault: true
    },
    {
        path: '/hero-list',
        name: 'HeroList',
        component: HeroListComponent
    },
    {
        path: '/detail/:id',
        name: 'HeroDetail',
        component: HeroDetailComponent
    }
])
export class AppComponent {
    title: string = 'Tour of Heroes';
}

One final note, you could do the import as

最后一点,你可以做导入

import { HeroListComponent, HeroDetailComponent, HeroService } from 'hero';

And it will work, however, since it is not technically a NodeJS imported module, VS Code complains that it cannot find it. So, my personal choice is to leave the explicit ./modules/hero, it helps me know that it is one of my modules and not an imported one and I'm happy to not see red lines.

然而,它会起作用,因为它在技术上不是NodeJS导入的模块,VS Code抱怨它无法找到它。因此,我个人的选择是留下明确的./modules/hero,它帮助我知道它是我的模块之一而不是导入的模块,我很高兴看不到红线。