我有一个用角度通用"@nguniversal/express-engine": "^9.1.1"
构建的应用程序。我正在尝试添加服务器端节点画布("canvas": "^2.6.1"
(以在服务器上渲染某些图像。我不需要这个库在客户端可用,因此想从捆绑包中排除它。
尝试使用npm run dev:ssr
运行应用程序时 - 我收到与该库相关的错误:
ERROR in ./node_modules/canvas/build/Release/canvas.node 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are
configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
this.debug is not a function
经过一些研究,我发现externalDependencies
选项被添加到angular.json
服务器构建选项中。我试图将canvas
库路径添加到externalDependencies
数组中,但这没有给出任何结果。错误仍然存在。构建正在失败。
目前,如果平台server
,我正在使用动态import()
将画布库加载到角度服务之一中。出于某种原因,我认为默认情况下它不会尝试捆绑动态导入的脚本。 也许还有其他一些解决方法。
因此,总而言之,理想情况下,我希望拥有仅在服务器端可用的特定服务,并且将使用其中的canvas
库。并且不会破坏棱角分明的构建。
仅包含在服务器捆绑包中
目前,如果平台是服务器,我正在使用动态import((将画布库加载到角度服务之一中
这包括将此依赖项包含在使用您的服务的任何捆绑包中(带有 canvas 的块可能仍然延迟加载 - 但 webpack 无论如何都必须处理它(。
在 Angular 中执行此操作的一种惯用方法是通过依赖注入:
// no side effect here - safe to import in browser or server files
// canvas-polyfill.token.ts
export type PolyfillLoader = () => Promise<any>;
export const CANVAS_POLYFILL_LOADER = new InjectionToken<PolyfillLoader>();
// app.server.module.ts
const canvasLoader = () => import('canvas');
@NgModule({
...
providers: [{provide: CANVAS_POLYFILL_LOADER, useValue: canvasLoader}]
})
export class AppServerModule {}
// your service
@Injectable()
class MyService {
constructor(@Optional() @Inject(CANVAS_POLYFILL_LOADER) private polyfillLoader: PolyfillLoader) {}
doYourStuff() {
const whenReady = this.polyfillLoader ? this.polyfillLoader() : Promise.resolve();
return whenReady.then(() => {
// do something useful
})
}
}
网络包问题
综上所述 - 在构建服务器捆绑包时,仍然不能保证 Angular 的 webpack 能够处理这种依赖关系。 如果要避免将其包含在捆绑包中并保持外部,则可以在angular.json
中为服务器构建器设置bundleDependencies: false
选项。 不过,它有一个副作用,即不会从角度库中删除任何调试代码 - 因此您需要执行额外的步骤来准备您的应用程序以进行生产(有关一些提示,请参阅 https://angular.io/guide/ivy-compatibility#payload-size-debugging(