ngx-center
v0.3.2
Published
一个中心,多个......
Downloads
10
Maintainers
Readme
ngx-center
一个中心,多个......
这是什么?
- 一个 Angular 单运行时共享依赖的解决方案
- 只启动一个 Angular 平台(
PlatformRef
),其他子项目进行导出(类 es6 module),通过资源文件清单(或单独 js 文件)进行请求加载
功能
- 支持子项目在开发时代码变更的刷新
- 支持子项目调用主项目导出的命名(
export * from 'xxx'
) - 支持子项目及主项目的 splitChunks(包括懒加载等...)
- 支持懒加载路由的子项目请求及其他动态模块的请求
- 支持
可信
的远程
子项目开发 - 不需要了解各种概念,因为导出导入处理完全遵循
动态import
的类处理方案 - 子模块构建速度极快,且在大多数时间不需要向 node 申请内存
- 理论上子项目同时可以加载子项目,也就是可以存在多层级子项目的加载
- 可以通过子项目嵌套其他框架支持的调用
- 通过在编译时构建构成关系,对源代码无侵入性,不需要过多改造
依赖
- Angular10 Angular11 Angular12 Angular14
- node16+
| ngx-center 版本 | Angular 版本 | webpack-ng-dll-plugin 版本 | | --------------- | ------------ | -------------------------- | | 0.3.0 | 14.2.1 | 2.3.1 | | 0.2.5 | 12.2.0 | 2.2.2 | | 0.2.3 | 12.0.3 | | 0.1.1 | 10-11 |
版本升级
- 初次使用直接安装
- ng10.11->ng12 使用 ng update
- ng13 由于时间未适配,可以自己提pr...
- ng12->ng14 修改下
webpack.config.center-main.ts
估计也没人用我也懒得写更新脚本了....
import * as webpack from 'webpack';
import * as path from 'path';
import { NgNamedExportPlugin } from 'webpack-ng-dll-plugin';
export default (config: webpack.Configuration) => {
config.plugins!.push(
new webpack.DllReferencePlugin({
context: path.resolve(__dirname),
manifest: require('./dist/manifest.json'),
})
);
const exportFilePath = path.resolve(
__dirname,
'<%= sourceRoot %>',
'export-module.ts'
);
// 这里移除掉
// config.output!.library = 'centerLib';
(config.entry as any).main.push(exportFilePath);
config.plugins!.push(
new NgNamedExportPlugin(exportFilePath, {
name: 'centerLib',
path: path.resolve(__dirname, 'dist', 'main-manifest.json'),
watchWrite: true,
})
);
// 新增部分
let entry = config.entry as Record<string, string[]>;
Object.keys(entry).forEach((item) => {
if (item === 'main') {
entry[item] = {
import: entry[item],
library: { name: 'centerLib', type: 'window' },
} as any;
} else {
entry[item] = { import: entry[item] } as any;
}
});
return config;
};
原理
依赖共享
- 也就是
node_modules
中的依赖变成 dll(动态链接库),主,子应用都使用这个 dll 保证实例的一致性
命名共享
- 主项目的命名会导出,供子项目用,实现子项目调用主项目的组件/模块/服务/管道等 ng 与非 ng 的导出
子项目资源启动
- 主项目通过启动子项目的资源文件/资源配置,来加载子项目资源,并且在加载后实现自动获得子项目的导出,可以作为懒加载路由,懒加载模块等动态使用的地方调用
使用方法
- 本项目依赖的是 Angular 的
ng generate
命令,快速生成开发骨架 - 相关插件可在在
webpack-ng-dll-plugin
,'webpack-bootstrap-assets-plugin
查看,或者可手动配置文件 - 建议先在新项目中使用,待了解实现后,再使用到已有项目中
主项目初始化
- 运行
npx ng g ngx-center:main-init
- 配置
/**
* 主项目初始化
*/
declare interface MainInitSchematics {
/**
* 链接库(lib)名称,默认dll
*/
dllName?: string;
/**
* 主项目的名字(为空读取默认值)
*/
projectName?: string;
/**
* 使用暴露webpack配置的方式(交互)
*/
webpackMode?: string;
/**
* 将webpack提升到根依赖(yarn会默认提升)
*/
webpackPromotion?: boolean;
}
- 除了
webpackMode
外,其他配置均为静默设置,除非使用-xxx yyy
显式设置 - 运行完成后,可以先执行
npm run build:center-dll
生成 dll 库,再运行npm run start:center-main
启动主项目如依赖无变更,每次可仅启动
npm run start:center-main
- 主项目的依赖一旦变更,就需要重新生成
dll
(npm run build:center-dll
). - 如果主项目想导出某些命名给子项目使用(包括不限于 ts 正常语法导出,组件,指令,管道,模块,服务等),可以声明在
export-module.ts
文件中,声明方式为export * from './xxx'
,同时子项目使用可以使用相对路径,但是也可以使用impot {a} from "@center-main/.../xxx"
这种方式(@center-main
为配置 tsconfig 中的 paths,默认主项目的 sourceRoot) - 主项目的导出命名变化后(添加依赖),需要重启子项目
- 主项目使用子项目使用的是 ng 原生方法,懒加载,只不过懒加载的不是原来的 ts 文件,而是资源文件,示例:
RouterModule.forRoot([
{
path: 'sub1',
loadChildren: () => {
return fetch(`http://127.0.0.1:4201/bootstrap.json`)
.then((item) => item.json())
.then((res) => loadRemoteModuleManifest(res))
.then((item) => item.module);
},
},
]);
子项目生成
- 运行
npx ng g ngx-center:sub
- 配置
/**
* 子项目生成
*/
declare interface SubSchematics {
/**
* 子项目的名字(交互)
*/
name: string;
/**
* 主项目的名字
*/
mainProjectName?: string;
/**
* 使用暴露webpack配置的方式(交互)
*/
webpackMode?: string;
/**
* 开发端口(交互,每一个子项目开发时独立占据一个端口)
*/
port: number;
}
- 先启动主项目,等
center-dll
,center-main
都启动成功后,再启动子项目 - 子项目的开发,与普通的开发完全一致,可以理解为,就是一个独立的项目开发,支持所有你日常开发的操作
远程子项目生成
新建项目
安装
ngx-center
在新建的项目中运行
npx ng g ngx-center:remote-sub-init
获得主项目
manifest.json
(依赖),main-manifest.json
(项目导出命名)如果需要使用主项目服务,则需要建立虚拟主项目(即建立与原主项目导出层级相同的文件),文件中只需要将类型配置正确即可.
如导出一个文件,那么就需要建立一个层级相同的路径,使最后的资源引用上下文与资源文件 json 中的路径结合时,能找到这个文件即可 导出模块组件(指令,管道)使用时,只需要指定下 selector 及公开属性,方便提示即可, 虚拟主项目相当于一个桩只要 ng 的 loader 通过,就没有用了
最后由主项目调用远程子项目的资源清单(或者直接拿到资源内容)即可和普通的子项目一同使用
注意事项
- 子项目的依赖要和主项目保持一样(最好是指定版本)
- 主项目资源清单变化时(增加/减少依赖),需要同步子项目资源清单保持一致
构建生成
- 参考命令,先构建,再讲子项目复制进主项目文件夹中
{
"deploy": "yarn build:center-dll:prod&&yarn build:center-main:prod&&yarn build:sub1:prod&&cpx \"dist/sub1/**/*\" dist/ng-cli-plugin/sub1"
}
- 如果你的部署文件存放有要求,可以修改
angular.json
中子项目的deployUrl
配置,改为你要放的子文件夹位置,然后再将命令修改为复制到对应的位置即可,当然主项目中的请求路径也要相应修改比如,原项目为
deployUrl:'sub1'
,想放到主文件夹中的router/sub1
中,那么就要修改deployUrl:'router/sub1'
,同时,代码请求部分也改成fetch('router/sub1/bootstrap.json')
,复制到cpx \"dist/sub1/**/*\" dist/ng-cli-plugin/router/sub1
如果没有要求,默认子项目名与部署地址位置相同
模板
demo 参考
- 源码地址:https://github.com/wszgrcy/ng-cli-plugin-demo
- 演示地址:https://wszgrcy.github.io/ng-cli-plugin-demo/
- 远程子项目演示:https://wszgrcy.github.io/ng-window/
注意事项
- tsconfig 有独立进行配置的开发者,请自行修改由原理图(schematics)生成的相关配置,因为可能与您具体使用场景不同
- 声明文件名如果与默认不同(typings.d.ts),也需要自行复制或添加到 tsconfig 中
- 如果使用非
@angular-builders/custom-webpack
作为 webapck 配置的导出,也请自行根据生成的文件修改为您自身的 builder 需要的格式,暂时没有安排适配其他 builder.
ngx-center 与 webpack-ng-dll-plugin
ngx-center
作为原理图,生成相关配置用来实现相关功能webpack-ng-dll-plugin
中的插件也可以单独使用,实现部分功能
license 提取插件问题
- 可能是升级 webpack5(ng12 使用)的原因,license-webpack-plugin 与 webpack 自带的 dll 不兼容
已经单独开项目测试,证明确实是此问题
- 目前已经对该依赖库做了 pr,不知道什么时候会合并进去
- 就算合并进去也要再等待
angular-cli
的更新 - 所以目前在使用 dll 的部分,都会禁止提取 license
常见问题
- 主项目路由声明了子项目访问,也点击访问子项目路由了,导航地址已经变化,但是没有任何报错,也没有任何反应
可能需要重新构建
center-dll
,因为 ng 项目初始化时,如果你没有选择生成路由,这就导致了构建center-dll
时,@angular/router
没有在center-dll
中,所以子项目,主项目用的两套路由依赖,所以出的问题 - 构建警告,如下:
Warning: /Users/chen/my-project/ng-cli-plugin-demo/src/polyfills.ts is part of the TypeScript compilation but it's unused.
Add only entry points to the 'files' or 'include' properties in your tsconfig.
当声明了
polyfills
,styles
等文件(可能也有导出依赖),但是在构建时将入口去掉后,ng 的自身检查机制,目前通过ngx-center
构建的,基本上已经规避这个问题,即使不规避也没问题,只不过警告看的难受
- 构建警告,如下:
/Users/chen/my-project/ng-cli-plugin-demo/src/app/show-in-main/show-in-main.component.ts not export from main project!
这个是检查插件
NgNamedImportCheckPlugin
在起作用,因为主项目和子项目并不是强关联,有时候开发可能不自觉的引用了一些没有到导出的,虽然构建不会报错,但是会直接引用主项目的代码,也就是生成了两份,这时就需要将报警告的文件加入到export-module.ts
中,不需要加绝对路径,只需要相对引用就 ok 了
- 在主项目加入一个导出到
export-module.ts
中后,子项目引用没有找到.
重启子项目即可,如果重启依然复现,请检查其他原因
- 子项目出现某些模块,明明引入了,但是不知道为啥使用对应组件时报错
重启子项目,具体原因未知,似乎是使用导出这种方式后,ng 没有对应的检查更新,但是重启就会好了,出现概率较低,已知就是如果使用自定义表单控件模块,先使用,再引入就有可能发生...,但是,如果重启后依旧出现问题,那么就不是这个问题导致的,请排查其他问题
- 构建异常,如下:
An unhandled exception occurred: Dll Reference Plugin Invalid Options
options.manifest.content should NOT have fewer than 1 properties
options.manifest.content should match some schema in anyOf
options.manifest should be string
options.manifest should match exactly one schema in oneOf
options should NOT have additional properties
options should have required property 'content'
options should have required property 'name'
options should match some schema in anyOf
导出模块
export-module.ts
未导出任何命名,请添加一个,或者移除这个功能(搜索export-module
,移除相关代码及文件即可)
- license 插件导致的报错
- 报错如下
[error] HookWebpackError: ENOENT: no such file or directory, scandir
at makeWebpackError (/xxx/projectName/node_modules/webpack/lib/HookWebpackError.js:48:9)
at /xxx/projectName/node_modules/webpack/lib/Compilation.js:2513:12
at eval (eval at create (/xxx/projectName/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:31:1)
at fn (/xxx/projectName/node_modules/webpack/lib/Compilation.js:452:17)
at _next4 (eval at create (/xxx/projectName/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:28:1)
at eval (eval at create (/xxx/projectName/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:46:1)
at fn (/xxx/projectName/node_modules/webpack/lib/Compilation.js:464:9)
at _next3 (eval at create (/xxx/projectName/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:41:1)
at eval (eval at create (/xxx/projectName/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:60:1)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
-- inner error --
Error: ENOENT: no such file or directory, scandir
at Object.readdirSync (fs.js:1043:3)
at CacheBackend.provideSync (/xxx/projectName/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:233:32)
at WebpackFileSystem.listPaths (/xxx/projectName/node_modules/license-webpack-plugin/dist/WebpackFileSystem.js:78:24)
at LicenseTextReader.readLicense (/xxx/projectName/node_modules/license-webpack-plugin/dist/LicenseTextReader.js:37:54)
at PluginChunkReadHandler.processModule (/xxx/projectName/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:46:62)
at /xxx/projectName/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:22:23
at WebpackModuleFileIterator.internalCallback (/xxx/projectName/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:25:13)
at WebpackModuleFileIterator.iterateFiles (/xxx/projectName/node_modules/license-webpack-plugin/dist/WebpackModuleFileIterator.js:10:9)
at /xxx/projectName/node_modules/license-webpack-plugin/dist/PluginChunkReadHandler.js:20:32
at WebpackChunkModuleIterator.iterateModules (/xxx/projectName/node_modules/license-webpack-plugin/dist/WebpackChunkModuleIterator.js:42:21)
- 此错误一般出现在 ng12 上,
license-webpack-plugin
与webpack
原生的 dll 插件冲突 - 解决方法就是在 angular.json 中关闭 license 即可
"extractLicenses": false,
- export-module 没有使用,也没有删除
- 错误日志
[error] ValidationError: Invalid options object. Dll Reference Plugin has been initialized using an options object that does not match the API schema.
- options.manifest.content should be a non-empty object.
-> The mappings from request to module info.
at validate (/Users/chen/my-project/ngx-center-template/node_modules/schema-utils/dist/validate.js:105:11)
at /Users/chen/my-project/ngx-center-template/node_modules/webpack/lib/util/create-schema-validation.js:16:17
at new DllReferencePlugin (/Users/chen/my-project/ngx-center-template/node_modules/webpack/lib/DllReferencePlugin.js:34:3)
at exports.default (/Users/chen/my-project/ngx-center-template/webpack.config.project1.ts:17:5)
at Function.<anonymous> (/Users/chen/my-project/ngx-center-template/node_modules/@angular-builders/custom-webpack/src/custom-webpack-builder.ts:47:14)
at Generator.next (<anonymous>)
at /Users/chen/my-project/ngx-center-template/node_modules/@angular-builders/custom-webpack/dist/custom-webpack-builder.js:8:71
at new Promise (<anonymous>)
at __awaiter (/Users/chen/my-project/ngx-center-template/node_modules/@angular-builders/custom-webpack/dist/custom-webpack-builder.js:4:12)
at Function.buildWebpackConfig (/Users/chen/my-project/ngx-center-template/node_modules/@angular-builders/custom-webpack/dist/custom-webpack-builder.js:19:16)