Angular7 Library库的使用
创建 Angular 工作区
使用 –createApplication
这个 --createApplication 选项与 ng new 结合一起使用,设置它为 false 时,它会告诉 ng new 命令不要在工作区内创建初始化的 Angular 应用。
为了创建一个不包含初始化应用的 Angular 工作区,我们使用下面方法:
ng new module-library --createApplication=false
当你被询问 router 选择时,选择默认 No 即可,css 选择 less。
使用这个命令创建的 Angular 工作区,对比之前,工作区中的文件少了很多,尽管这个工作区显得很“空”,它仍然包含有一些重要的配置信息:
package.json
包含了所有 Angular 所需的所有常见依赖
angular.json
不包含 projects 配置项的 Angular 配置文件
README.md, tsconfig.json, tslint.json
以及 node_modules
文件都与以前保持一致。
你可能会注意到,这里没有 src 文件夹,之后 projects 文件夹将会在生成库项目或者测试应用项目时被添加。
自从我们设置 --createApplication 选项为 false 后,我们的工程就不是一个初始化的应用。因此,如果你试着去运行一些命令,例如:ng build
或者 ng serve
时,我们会看到下面错误:Could not determine a single project for the ‘build’ target
。
库项目
创建一个库模块
现在,我们有了 Angular 工作区,我们可以添加库项目到工作区之中。
cd module-library
ng generate library module-library --prefix=ma
这个命令添加了一个 projects
目录,并包含有一个 module-library
子目录,它就是我们新生成的 module-library Angular
库。
注意到我们在命令中使用了 --prefix
标签,其目的是让库组件变得更加有辨识度(注:就像 ng-zorro 所做的 nz-xxx
一样)。如果我们不对其进行配置,Angular CLI 将使用 lib
作为默认前缀标签。
注意!在创建 Library 时总是显示地使用 prefix
标签进行配置。
以下是对于生成 Library 指令行为结果的一个简单总结:
-
在 angular.json 文件中为我们的库添加了一个新的 module-library 项目。
-
将 ng-packagr 的依赖项添加到 package.json 文件中。
-
在 tsconfig.json 文件中添加对 module-library 构建路径的引用
-
在 projects/module-library 文件夹下创建库的初始源代码。
在 angular.json
文件中的 module-library
项目
查看 angular.json 文件会发现我们在 projects
对象下创建了一个名为 module-library
的新项目。
"projects": {
"module-library": {
"root": "projects/module-library",
"sourceRoot": "projects/module-library/src",
"projectType": "library",
"prefix": "ma",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/module-library/tsconfig.lib.json",
"project": "projects/module-library/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/module-library/src/test.ts",
"tsConfig": "projects/module-library/tsconfig.spec.json",
"karmaConfig": "projects/module-library/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/module-library/tsconfig.lib.json",
"projects/module-library/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
这里需要注意一些关键元素:
-
root 其指向我们的库项目的根文件夹。
-
sourceRoot 其指向我们的库项目的源代码位置。
projectType
其特别指出了这是一个 library 项目,而不像是其他两个类型名称为 application 的应用项目。
prefix
这是将会用于我们的组件选择器的前缀标识符。记得我们在创建库时制定了 ma
作为指定前缀。你可能熟悉 app 的前缀,其标识出哪些组件属于主应用程序。
architect 此对象的内容用于指定 Angular CLI 如何处理项目的构建,测试和 lint
。值得注意的是,构建部分中的构建器使用了 ng-packagr
。
package.json
文件中的 ng-packagr
依赖项
在生成 Library 时 Angular CLI 需要 ng-packagr
这个包,因此他将其添加到了工作区的 package.json
文件中 devDependencies
依赖中。
"ng-packagr": "^3.0.0-rc.2"
在 tsconfig.json
文件中的构建路径
当测试 module-library
时,我们希望能够像日常使用的方式那样引入他,而不是仅仅作为整个应用中的一组文件。通常,当我们使用第三方库时,我们使用 npm install 指令安装,并将其安装到我们的 node_modules
文件夹中。
即使在当前的情况下,module-library
不会安装到 node_modules
文件夹中,但是他会被构建到工作区的 dist 文件夹下的某个子文件夹中。Angular CLI 将这个文件夹添加到 tsconfig.json
文件中,这样 module-library
就可以像一个 Library 一样以常见的方式被测试应用所引用了。 下述是在 tsconfig.json 文件中添加的路径:
"paths": {
"module-library": [
"dist/module-library"
]
}
module-library
的源代码
库的 src 文件夹被包含在 projects/module-library
文件夹中。在库中,Angular CLI 创建了一个包含服务和组件的新模块。除此之外还包含了更多文件:
package.json
这是专门用于库的 package.json
文件,也是库作为 npm 包发布所使用的 package.json
文件。当用户通过 npm 安装库时,该文件用于指定其依赖项。
public_api.ts
该文件作为入口文件存在,他用于描述库中哪个部分是外部可见的。我们需要在我们的入口文件中添加要导出的类以告知 ng-packagr 这个类应该暴露给使用库的用户。
ng-package.json
这是 ng-packagr
的配置文件。在 CLI 和 ng-packagr
没有集成的时代我们需要尽可能地熟悉该文件的内容,但是现在,在拥有新版 Angular CLI 的情况下,我们只需要知道该文件是用于告知 ng-packagr
去哪找到入口文件以及去哪构建库的内容即可。
创建测试应用项目
最后,我们希望建立一个测试应用项目,能够调用我们建立的 Angular 库。我们使用这个项目来做库的测试或者文档等工作。
ng generate application module-library-tester
这条命令,在 project 文件夹下添加了 module-library-tester
目录。此外,Angular CLI 还添加了一个 module-library-tester-e2e
项目来用做 e2e 测试。
构建库
使用下面的命令来构建 module-library 库:
ng build module-library
向根目录下的 package.json 文件夹中添加一个 build_lib 脚本:
"scripts": {
"build_lib": "ng build module-library"
},
现在我们可以通过 npm run build_lib
指令去构建库了。
该命令会将库构建于下述文件夹中:
module-library\dist\module-library
注意,从 v6.1 起,Angluar CLI 始终在生产模式下构建库,所以不需要额外指定 --prod 选项。
与构建库不同,构建一个应用,需要使用 --prod 选项:
ng build module-library-tester --prod
package.json
文件
在库构建完成后,当前工作区内已经有3个 package.json
文件:
- 根目录下的
package.json
文件
这个 package.json 是库工作区的主package.json
文件。我们用该文件来列出主应用和库都需要的依赖项。运行和构建主应用和库所依赖的所有包都必须列举在该文件中。
当我们在开发过程中使用 npm install
指令时,新加入的包将会添加到该文件中。
- 库项目中的 package.json 文件
库项目中的 package.json 文件位于projects\module-library
目录下,其用于告知 ng-packagr 将什么信息放入库项目的发布版 package.json 文件中。其 package.json 文件中有三个重要的部分需要注意:
1.名称
这里的名称是指库的名称。未来如果某位用户引入库中的模块,这个名称就是出现在 from 部分内的引号中的名称。举例来说大概就是:
import { MALibraryModule } from 'module-library';
2.版本号
版本号对于库而言格外重要,版本号能够帮助用户判断他们是否在使用库的最新版本。
3.依赖项
此项目中只包含用于运行库所必须的依赖项。因此,你将会看到 dependencies
和 peerDependencies
,但是没有 devDependencies
。
同样应该在该 package.json
文件中添加那些常见的 npm 内容比如:License,作者,仓库地址等。
值得注意的是,当使用 npm install 指令时,新安装的包只会被添加到根目录下的 package.json
文件中而不是在库项目的 package.json
文件中。因此,当你安装一个库所需要的包时,你需要将包名称手动添加到库项目的 package.json
文件依赖项中。
4.库的发布版本 package.json
文件
当我们构建库时库的发布版本 package.json 文件 由 ng-packagr 生成于 dist\module-library 文件目录下。该 package.json 文件会随着我们的库一并发布。
使用我们的库的开发者将会使用 npm install 指令安装该文件中所涉及的那些依赖项。
因为发布版本的 package.json 文件由 ng-packagr 生成,故而我们不应直接对其进行修改。如果你希望对发布版本的 package.json 文件进行修改,需要更新 projects\module-library 目录下的 库项目的 package.json 文件。ng-packagr 以 库项目的 package.json 文件为基准去生成发布版本的package.json 文件。
记住!永远不要直接对发布版本的 package.json 文件作出修改
5.打包库
打包库是指将生成的发布文件进行打包以生成一个 tgz 文件用以手动分享或发布于 npm。
使用 npm pack
指令在根目录下的 package.json
文件中创建一个脚本,该脚本用于打包生成的库。
"scripts": {
"npm_pack": "cd dist/module-library && npm pack"
},
现在我们只需要使用指令 npm run npm_pack
就可以完成对库的打包。这条命令用于将文件目录指向工作区的 dist 文件夹并执行 npm pack 指令,命令将会在同一目录下生成一个形如 module-library-0.0.1.tgz
的包文件。
创造一个新的脚本包含 build_lib
和 npm_pack
两个脚本的内容。将下述内容加入到主目录下的 package.json 文件中的脚本对象中去:
"package": "npm run build_lib && npm run npm_pack"
现在看看主目录下的 package.json 文件,和构建打包相关的脚本如下:
"scripts": {
"build_lib": "ng build module-library",
"npm_pack": "cd dist/module-library && npm pack",
"package": "npm run build_lib && npm run npm_pack"
},
注意,执行 package 脚本将会顺序执行 build_lib 脚本 和 npm_pack 脚本。
使用如下指令完成对你的库的构建和打包:
npm run package
package 脚本做了两件事:
在 dist/module-library
目录下构建了库。
在同一目录下使用 npm pack
指令将库打包为一个 npm 包,其形如:module-library-0.0.1.tgz
。
需要注意!即使生成的包是 tgz 文件,你也不能以 tgz 格式直接压缩 dist 目录作为包,必须使用 npm pack
创建 tgz 文件。
扩展库
以下是我们将要执行的步骤:
- 在库中创造一个新的组件
- 将新添加的组件加入到库模块的 exports 属性中
- 将新添加的组件加入到入口文件中
- 在执行完上述步骤后重新构建库
在应用中使用新的组件
- 创建一个库组件
- 当需要为库生成组件的时候我们需要使用 --project 标识来告诉 Angular CLI 在库项目中生成组件。现在我们在库里生成一个简单的组件并命名为 list:
ng generate component list --project=module-library
-
现在我们的库拥有了一个新的组件,并且 Angular CLI 将其添加到了库模块文件projects\module-library\src\lib\module-library.module.ts 的 declarations 属性中。
-
将组件从库的模块中导出
将 ListComponent 添加到 module-library.module.ts 的 exports 数组中。而添加后的 MALibraryModule 文件应如下所示:
import { NgModule } from '@angular/core';
import { MALibraryComponent } from './module-library.component';
import { ListComponent } from './list/list.component';
@NgModule({
imports: [
],
declarations: [
MALibraryComponent,
ListComponent
],
exports: [
MALibraryComponent,
ListComponent
]
})
export class MALibraryModule { }
- 将组件添加到入口文件中
在用于定义库 API 的入口文件projects\module-library\src\public_api.ts
中,添加以下内容以告知 ng-packagr 这个组件类应该暴露给使用库的用户:
export * from './lib/list/list.component';
- 现在 public_api.ts 入口文件应该像如下这样:
/*
* Public API Surface of module-library
*/
export * from './lib/module-library.service';
export * from './lib/module-library.component';
export * from './lib/module-library.module';
export * from './lib/list/list.component';
- 重新构建库
在对库进行修改之后,我们需要重新构建库:
ng build module-library
迄今维持我们所有的操作都是纯手动的。事实上,Angular CLI 在 6.2 版本中增加了一个增量构建的功能。每当有文件发生了修改,Angular CLI 将会进行部分构建并抛出修改后的文件。使用这个新的观察功能你只需要执行如下指令:
ng build module-library --watch
- 运行
我们无法直接运行一个库项目,但可以运行创建的测试应用项目:
ng serve module-library-tester
- 测试
我们能够为创建的 Angular 库项目和测试应用项目运行单元测试。
库项目运行单元测试:
ng test module-library
测试应用项目运行单元测试:
ng test module-library-tester
在其他 Angular 应用中使用库
安装库
- 希望在其他应用中使用库的内容。我们需要执行以下指令:
npm install ../module-library/dist/module-library/module-library-0.0.1.tgz
- 引入库模块
我们首先需要向 App module 中添加库的module和Http拦截器 。
为此我们需要在 src\app\app.module.ts
文件中作出两处修改:
引入 MALibraryModule
模块和 HttpInterceptorService
import { MALibraryModule, HttpInterceptorService } from 'module-library';
将 MALibraryModule 模块加入到 AppModule 的 imports 数组中,并用forRoot把environment引入库中,给库提供可配置的API地址。
现在 app.module.ts 应如下所示:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppComponent } from './app.component';
import { MALibraryModule, HttpInterceptorService } from 'module-library';
import { environment } from 'src/environments/environment';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
MALibraryModule.forRoot(environment)
],
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: HttpInterceptorService,
multi: true
}],
bootstrap: [AppComponent]
})
export class AppModule { }
使用一个库中的组件
在应用中修改 AppComponent 组件的 html 模板文件并展示源自于库的 list 组件。修改后的 app.component.html 文件如下所示:
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<ma-list></ma-list>
</div>