Angular 服务器端渲染中的坑

Angular 官方文档已经有 Angular Universal 那一章,专门用于讲解服务器端渲染。但是在使用服务器渲染的时候还是碰到不少的坑。

1. 文档中的坑

文档中已经大致介绍了服务器渲染的过程,以及需要的配置,但文档中还是有不少的问题。

1.1 index-aot.html

在使用 Express 路由的时候,有下面的代码,但是文档中并没有 index-aot.html 的说明。

server.get(['/', '/dashboard', '/heroes', '/detail/:id'], (req, res) => {
    res.render('index-aot.html', {req});
});

可以使用下面的 index-aot.html,其中重要的是加入了3个 JS 文件。
- shim.min.js 是为浏览器提供一些还没有支持的新特性,比如 ES6 中的 promises, symbols, collections, iterators。
- zone.js 是 Angular 依赖的核心库。
- build.js 是 aot 编译后文件

<!DOCTYPE html>
<html>
  <head>
    <base href="/">
    <title>Angular Tour of Heroes</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="styles.css">

    <!-- Polyfills -->
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script src="node_modules/zone.js/dist/zone.js"></script>
  </head>

  <body>
    <my-app>Loading...</my-app>
  </body>
  <script src="dist/build.js"></script>
</html>

1.2 webpack output.path

webpack 新版本中 output.path 需要是绝对路径,按照文档中使用相对路径配置会报错

- configuration.output.path: The provided value "src/dist" is not an absolute path!

所以要将

output: {
  path: 'src/dist',
  filename: 'server.js'
}

改为

var path = require('path');

output: {
  path: path.join(__dirname, 'src/dist'),
  filename: 'server.js'
}

1.3 webpack tsConfigPath

文档中说 Webpack 配置需要改两个地方,但其实是三个地方

The Webpack configuration file for Universal is almost the same as for AOT. There are two differences:

1.The entry points
2.The output file name

AotPlugin 中的 tsConfigPath 需要改为 tsconfig-uni.json

plugins: [
    new ngtools.AotPlugin({
      tsConfigPath: './tsconfig-uni.json'
    }),
    new webpack.optimize.UglifyJsPlugin({ sourceMap: true })
  ],

1.4 @angular/animations

使用 npm 安装 @angular/platform-server 的时候会提示缺少 @angular/animations,如果不安装后面 build 的时候也会报错:

Could not resolve @angular/animations/browse ...

使用 npm 安装即可

npm i @angular/animations --save

2. 其他的问题

2.1 window is undefined

因为服务器渲染运行在 Node 环境中,因此是不存在 window 这个全局对象的,所以例如 LocalStorage,Cookie 等也是不能使用的。

所以在调用 window.localStorage 就会抛出 window is undefined 的错误。

最简单的方法就是判断如果没有 window 对象,就不使用 LocalStorage :

if (typeof window !== 'undefined') {
  const token = localStorage.getItem('token');
}

更好一点的方法是在服务器端模拟一个 storage 对象,并使用 PLATFORM_ID 来判断当前平台是浏览器还是服务器

@Injectable()
export class LocalStorageService {

  storage: {};

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    if (isPlatformBrowser(this.platformId)) {
      this.storage = window.localStorage;
    }
    if (isPlatformServer(this.platformId)) {
      this.storage = {};
    }
  }

  get(key: string) {
    return this.storage[key];
  }

  set(key: string, value: string) {
    this.storage[key] = value;
  }

  remove(key: string) {
    delete this.storage[key];
  }

}

2.2 setTimeout & setInterval

使用 setTimeout 会降低服务器端渲染的速度,而使用 setInterval 可能会导致渲染失败。

如果在 AppComponent 的构造函数中使用了 setInterval ,而且没有关掉则会导致在浏览器端没有响应。推测应该是在执行定时器的时候,程序会认为渲染还没有结束,所以导致失败。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值