从接触 Angular 到如今,做了不少 Angular 项目,使用了不少第三方库,但是却没有勇气触碰第三方库的开发,一是没有太多的积累,二是没有找到合适的“Hello world”的文档。 最近有机会要把项目中常用的 Component 做成第三方库,方便更多的项目使用。根据网上找到的各种资料,加上走的各种弯路,终于摸清楚了开发第三方库的流程。
为了方便第三方库的开发,我们首先要创建一个主应用 A:
ng new projectnameA //创建主应用
主应用 A 创建好之后,创建 Lib:
cd projectnameA
ng g library libName --prefix prefixName //--prefix是Lib使用的前缀
上述命令会对主应用如下改变:
在主应用 A 下创建 projects/libName 目录,并将 Lib 的相关文件放于此;
在 angular.json 文件中添加 libName 项目;
"libName": {
"root": "projects/libName",
"sourceRoot": "projects/libName/src",
"projectType": "library",
"prefix": "dteam-top",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/libName/tsconfig.lib.json",
"project": "projects/libName/ng-package.json"
},
"configurations": {
"production": {
"project": "projects/libName/ng-package.prod.json"
}
}
},
"test": {...},
"lint": {...}
}
}
其中: root 为 Lib 的根目录; sourceRoot 为 Lib 的源代码目录; projectType 为项目的类型; prefix 为组件使用的前缀; architect 为 Angular 的构建配置,可设置 build、test 和 lint。
在 package.json 文件中添加 ng-packagr 依赖;
在 tsconfig.json 文件中添加 libName 库的引用;
{
...
"paths": {
"libName": [
"dist/libName"
]
}
...
}
这样项目框架就搭建好了,可以开始写 Lib 的代码了。
在 Lib 中需要注意一个文件:public_api.ts,这个文件是 Lib 的入口文件,取代了之前使用的 index.ts 文件,其中定义了 Export 的内容:
export * from "./app/libName.component";
export * from "./app/libName.module";
主应用 A 要使用 Lib,在 app.module.ts 文件中直接引用:
import { LibNameModule } from '../../../../projects/libName/src/app/libName.module'
imports: [
...
LibNameModule,
...
]
对于 Lib 中使用的其他组件,可在主应用 A 下通过 npm 安装,这里提醒下,这里是安装到主应用 A 的目录下,修改的是主应用 A 的 package.json 和 package.lock.json 文件。 这样,就可以开发自己的 Lib 了。
Lib 开发好之后,需要先在本地试安装。这时有疑问了,Lib 中用到的其他组件的引入都是在主应用 A 的 package.json 中声明的,这对于 Lib 是不对滴。 细心的开发者会发现,在 Lib 的目录下也有一个 package.json 文件,其缺省内容为:
{
"name": "libName",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "7.0.2",
"@angular/core": "7.0.2"
}
}
我们需要把这个文件补充完整,将 Lib 中用到的其他第三方组件在 peerDependencies 中引入,并将 Lib 的其他属性也加入。例如:
{
"name": "ligName",
"version": "0.0.1",
"keywords": ["keyword1", "keyword2"],
"license": "MIT",
"author": "authorName",
"description": "description",
"peerDependencies": {
"@angular/common": "7.0.2",
"@angular/core": "7.0.2",
"@angular/forms": "7.0.2",
"@angular/router": "7.0.2",
"@angular/cdk": "7.0.2",
"@angular/material": "7.0.2",
"ngx-spinner": "7.0.0",
"ngx-clipboard": "12.0.0",
"ethers": "4.0.27",
"rxjs": "6.3.3"
}
}
同时,还可以添加一个 README.md 文件,介绍 Lib 的使用方法。 准备工作做好之后,将 Lib 编译成产品:
ng build libName --prod
编译后的文件会放置在主应用 A 的 dist/libName 目录下。另外创建一个 Angular 应用 B,在此应用下执行:
npm install 主应用A/dist/libName
安装时会对 libName 使用的其他第三方库给出类似如下的提示:
npm WARN libName@0.0.1 requires a peer of @angular/core@^7.2.0 but none is installed. You must install peer dependencies yourself.
根据提示,自行安装缺少的组件即可。 在应用 B 的 package.json 中会看到对 libName 的引入:
"libName": "file:../projectnameA/dist/
在应用 B 的 package-lock.json 文件中加入如下内容:
"libName": {
"version": "file:../projectnameA/dist/libName",
"requires": {
"tslib": "^1.9.0"
},
"dependencies": {
"tslib": {
"version": "1.9.3",
"bundled": true
}
}
}
在应用 B 的 app.module.ts 文件中引入 libName:
import { LibNameModule } from 'libName';
...
imports: [
...
LibNameModule,
...
],
...
在应用 B 中可以测试 libName 能否正常使用。
上述过程结束后,就可以将应用发布到 npm 上。准备工作:
好了,可以发布 libName 了。进入到 projectnameA/dist/libName 目录下执行:
npm publish
看到了发布成功提示,还有点小激动。进入到 npm 中,可以看到已经发布成功的 libName。 如果想删除这个 libName,可以使用如下命令:
npm unpublish libNamet@0.0.1 --force
这里需要特别注意的是:删除后的 libName,在 24 小时内不能再发布了,见如下提示:
npm ERR! libName cannot be republished until 24 hours have passed.
npm 为第三方库的开发还提供了一些便利的方法,如npm link命令。这些都有待我们在今后的开发中摸索和体验,以便更好的打磨精品组件。
在开发过程中遇到了几个错误,现汇总如下:
权限错误:
npm ERR! publish Failed PUT 403
npm ERR! code E403
npm ERR! You do not have permission to publish "libName". Are you logged in as the correct user? : libName
解决办法,执行: npm adduser
libName 若注入了其第三方的服务,主应用调用时出现如下错误:
Error: inject() must be called from an injection context
解决方法,在主应用的 angular.json 文件中添加:
"projects": {
"projectName": {
"architect": {
"build": {
"options": {
"preserveSymlinks":true
},
},
}
}
}
},
若 libName 定义了自己的 route,需要在主应用中注入 RouterModule。
觉得有帮助的话,不妨考虑购买付费文章来支持我们 🙂 :
付费文章