Angular 5 Universal:排除浏览器模块及AppShell构建报错求助
我之前做Angular 5 Universal的时候也碰到过这俩问题,给你分享下亲测有效的解决方案:
Unexpected token import错误 这个错误一般是因为第三方库用了未转译的ES6语法(比如import),而Node.js在服务器端构建时无法直接解析。你可以试试这两个方案:
方案1:转译第三方库
找到你的webpack.server.config.js,在模块规则里添加针对该库的转译配置,用Babel把ES6语法转成Node能识别的CommonJS:module.exports = { // ...其他已有配置 module: { rules: [ // 原有TypeScript编译规则 { test: /\.ts$/, loader: 'ts-loader', options: { configFile: 'tsconfig.server.json' } }, // 新增:转译目标第三方库,替换成你的库名 { test: /node_modules\/your-third-party-lib\/.+\.js$/, loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } ] } };然后安装所需的Babel依赖:
npm install @babel/core @babel/preset-env babel-loader --save-dev方案2:使用库的UMD/CommonJS版本
很多库会提供预编译的UMD或CommonJS版本(一般在dist目录下),你可以直接在server.ts里用require引入,而非import:const thirdPartyLib = require('your-third-party-lib/dist/library.umd.js');这样就绕开了ES6语法解析的问题。
Angular的BrowserModule和一些浏览器特有的模块不能在服务器端运行,需要通过环境区分和模块拆分来排除:
拆分服务器端专属模块
创建app.server.module.ts,导入服务器端所需的核心模块,不要引入BrowserModule:import { NgModule } from '@angular/core'; import { ServerModule, ServerTransferStateModule } from '@angular/platform-server'; import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; import { AppModule } from './app.module'; import { AppComponent } from './app.component'; @NgModule({ imports: [ AppModule, // 导入App的基础模块 ServerModule, // 服务器端核心模块 ModuleMapLoaderModule, // 支持路由预加载 ServerTransferStateModule // 用于前后端状态传递 ], bootstrap: [AppComponent] }) export class AppServerModule {}在AppModule中区分浏览器环境
在app.module.ts里,确保BrowserModule只在浏览器环境生效,并且开启服务器过渡:import { NgModule } from '@angular/core'; import { BrowserModule, BrowserTransferStateModule } from '@angular/platform-browser'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule.withServerTransition({ appId: 'your-app-unique-id' }), AppRoutingModule, BrowserTransferStateModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}条件加载浏览器专属代码
对于依赖window、document等浏览器API的服务或组件,用PLATFORM_ID和isPlatformBrowser来判断环境,避免在服务器端执行:import { Inject, PLATFORM_ID } from '@angular/core'; import { isPlatformBrowser } from '@angular/common'; constructor(@Inject(PLATFORM_ID) private platformId: Object) {} ngOnInit(): void { if (isPlatformBrowser(this.platformId)) { // 这里放只能在浏览器运行的代码,比如初始化第三方UI库 console.log('Running in browser environment!'); } }Mock浏览器全局对象(可选)
如果有些第三方库硬依赖window或document,可以在server.ts开头添加全局mock:// 模拟浏览器全局对象,避免服务器端报错 global['window'] = {}; global['document'] = { createElement: () => ({}) };
内容的提问来源于stack exchange,提问作者Tom Schreck

