Angular 通用:从捆绑中排除仅限节点的依赖项

Angular Universal: exclude node-only dependencies from bundling

我有一个使用 angular 通用 "@nguniversal/express-engine": "^9.1.1" 构建的应用程序。我正在尝试添加服务器端节点 canvas ("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 库加载到 angular 服务之一。出于某种原因,我认为默认情况下它不会尝试捆绑动态导入的脚本。 也许还有其他解决方法。

因此,总而言之,理想情况下,我希望拥有仅在服务器端可用且将使用其中的 canvas 库的特定服务。并且不会破坏 angular 构建。

仅包含在服务器包中

Currently I'm using the dynamic import() to load the canvas library into one of angular services if the platform is server

这会将此依赖项包含到任何使用您的服务的包中(带有 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
    })
  }
}

Webpack 问题

综上所述 - 仍无法保证 angular 的 webpack 在构建服务器包时能够处理此依赖项。 如果你想避免将它包含在你的包中并保留在外部,你可以在 angular.json 中为你的服务器构建器设置 bundleDependencies: false 选项。 不过,它有一个副作用,即不会从 angular 库中删除任何调试代码 - 因此您需要执行额外的步骤来准备您的应用程序以进行生产(请参阅 https://angular.io/guide/ivy-compatibility#payload-size-debugging 了解一些提示)