You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular独立组件结合Universal使用ng2-pdf-viewer启动SSR报错求助

Angular SSR(独立组件)集成ng2-pdf-viewer时canvas模块打包错误解决

问题场景

使用Angular独立组件搭配Angular Universal(Express Engine),引入ng2-pdf-viewer后,ng serve启动正常,但执行dev:ssr时出现canvas相关打包错误,仅在SSR环境下触发。


Angular 16.2.0 环境详情

错误信息:

./node_modules/canvas/build/Release/canvas.node:1:0 - Error: 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.
(Source code omitted for this binary file)

依赖清单:

"dependencies": {
    "@angular/animations": "^16.2.0",
    "@angular/cdk": "^16.2.9",
    "@angular/common": "^16.2.0",
    "@angular/compiler": "^16.2.0",
    "@angular/core": "^16.2.0",
    "@angular/forms": "^16.2.0",
    "@angular/material": "^16.2.9",
    "@angular/platform-browser": "^16.2.0",
    "@angular/platform-browser-dynamic": "^16.2.0",
    "@angular/platform-server": "^16.2.0",
    "@angular/router": "^16.2.0",
    "@ngneat/hot-toast": "^6.0.1",
    "@ngneat/overview": "^5.1.0",
    "@nguniversal/express-engine": "^16.2.0",
    "aos": "^2.3.4",
    "express": "^4.15.2",
    "ng2-pdf-viewer": "^10.0.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.13.0"
  }

Angular 17.0.0 环境详情

错误信息:

node_modules/pdfjs-dist/build/pdf.js:8671:35:
8671 │ const Canvas = require("canvas");
╵ ~~~~~~~~
提示:可将"canvas"标记为外部资源排除在打包之外,或用try/catch包裹require调用处理运行时错误

依赖清单:

"dependencies": {
    "@angular/animations": "^17.0.0",
    "@angular/common": "^17.0.0",
    "@angular/compiler": "^17.0.0",
    "@angular/core": "^17.0.0",
    "@angular/forms": "^17.0.0",
    "@angular/platform-browser": "^17.0.0",
    "@angular/platform-browser-dynamic": "^17.0.0",
    "@angular/platform-server": "^17.0.0",
    "@angular/router": "^17.0.0",
    "@angular/ssr": "^17.0.0",
    "express": "^4.18.2",
    "ng2-pdf-viewer": "^10.0.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0",
    "zone.js": "~0.14.2"
  }

解决方案

核心思路

ng2-pdf-viewer依赖的pdfjs-dist在SSR环境下会尝试加载Node端的canvas二进制模块,而Webpack无法解析该二进制文件。解决方式是将canvas标记为外部资源,让Express在运行时直接加载,而非打包进bundle。

针对Angular 16(@nguniversal/express-engine)

  1. 在项目根目录创建webpack.server.config.js文件:
module.exports = {
  externals: {
    canvas: 'commonjs canvas'
  }
};
  1. 修改angular.json中server配置的builder节点,添加Webpack配置引用:
"server": {
  "builder": "@angular-devkit/build-angular:server",
  "options": {
    "outputPath": "dist/server",
    "main": "server.ts",
    "tsConfig": "tsconfig.server.json",
    "webpackConfig": "./webpack.server.config.js"
  }
  // 保留其他原有配置
}

针对Angular 17(@angular/ssr)

方式一:直接修改server.ts,在启动应用前声明外部依赖:

import { APP_BASE_HREF } from '@angular/common';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { provideServerRendering } from '@angular/platform-server';
import { provideRouter } from '@angular/router';
import { routes } from './app/app.routes';

// 标记canvas为外部依赖,让运行时直接加载
(global as any).canvas = require('canvas');

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes),
    provideServerRendering(),
    { provide: APP_BASE_HREF, useValue: '/' }
  ]
});

方式二:同Angular 16,创建webpack.server.config.js并在angular.json中配置引用。

额外优化:仅在浏览器端渲染PDF组件

如果无需在SSR阶段渲染PDF内容,可通过平台判断仅在浏览器环境加载组件:

import { isPlatformBrowser } from '@angular/common';
import { Component, Inject, PLATFORM_ID } from '@angular/core';

@Component({
  selector: 'app-pdf-wrapper',
  template: `
    <ng-container *ngIf="isBrowser">
      <pdf-viewer [src]="pdfSource"></pdf-viewer>
    </ng-container>
  `
})
export class PdfWrapperComponent {
  isBrowser: boolean;
  pdfSource = 'path/to/your.pdf';

  constructor(@Inject(PLATFORM_ID) platformId: Object) {
    this.isBrowser = isPlatformBrowser(platformId);
  }
}

内容的提问来源于stack exchange,提问作者arun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 20:37:44