预渲染 es6 错误 Angular 2/Typescript/Webpack

Prerender es6 Errors with Angular 2/Typescript/Webpack

我正在尝试使用预呈现节点和私有预呈现服务器来预呈现 Angular2/Express 应用程序。如果我尝试在 tsconfig.json 中以 es5 为目标,预渲染服务器会抛出此错误:

ReferenceError: Can't find variable: Map

undefined:1521 in KeyRegistry
:1540
:7

如果我尝试以 es6 为目标(包括 files 数组中的 node_modules/typescript/lib/lib.es6.d.ts),预呈现服务器会抛出此错误:

SyntaxError: Unexpected token 'const'

http://localhost:3000/app.js:50 in eval
http://localhost:3000/app.js:50
http://localhost:3000/app.js:20 in __webpack_require__
http://localhost:3000/app.js:40

我想我需要包含某种 polyfill 才能使其正常工作,但我不知道要包含什么或在哪里包含它。

这是我的 webpack 配置以防万一:

var webpack = require('webpack');
var path = require('path');
var rootDir = path.resolve();

module.exports =
{
    target: 'node',
    entry: rootDir + "/dist/client/app/bootstrap.js",
    output: {
        path: rootDir + "/dist/client", publicPath: '/', filename: "app.js",
        pathinfo: true
    },
    devtool: 'eval',
    resolve: {
        root: rootDir + "/dist/client/app",
        extensions: ['', '.js']
    },
    module: {
        loaders: [
            {
                test: /\.ts/, loaders: ['ts-loader'], exclude: /node_modules/
            }
        ]
    }
};

我的客户端 tsconfig 以防万一:

{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs",
        "moduleResolution": "node",
        "sourceMap": false,
        "outDir": "../../dist/client",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": true
    },
    "files": ["app/bootstrap.ts", "app/vendor.ts", "app/Window.ts", "tests/index.ts", "../../node_modules/typescript/lib/lib.es6.d.ts"]
}

更新

如果我将我的 webpack 配置更改为目标 web 而不是 node,并在预渲染服务器中注释掉 server.use(prerender.removeScriptTags());,则请求每次都会到达预渲染服务器并且没有错误被抛出,但也没有任何预渲染。不过,似乎比以前更近了,所以我想我会更新。

更新

Prerender 似乎不执行任何 Angular 代码。如果我在 index.html 头部的脚本标记中设置 window.prerenderReady = false,然后在实例化我的根组件时再次尝试将其设置为 true,则预呈现服务器超时:

import { Component, OnInit } from '@angular/core'

@Component({
    selector: 'my-app',
    template: `
        <div id="main"> all the things </div>
    `
})
export class AppComponent implements OnInit
{
    constructor(){}

    ngOnInit()
    {
        // Prerender never executes this code before it times out
        window.prerenderReady = true;
        console.info('Prerender Ready');
    }
}

我设法通过以下更改使其正常工作:

在 Webpack 配置中使用 target: 'web'

使用 target: 'es5' 并将 node_modules/typescript/lib/lib.es6.d.ts 添加到 tsconfig.json 中的文件数组。这可以防止 es6 东西的编译时错误。

显然,尽管针对 web 和 es5,Webpack 仍然保留了 es6 语法,而 PhantomJs 无法处理。我不喜欢在 Typescript 项目中包含 babel 的想法,但这是我发现的唯一可行的方法:require('babel-polyfill'); 在任何可能使用任何 es6 语法的应用程序代码或供应商代码之前。

在文档的head中设置window.prerenderReady = false;,然后在实例化根应用程序组件时设置为true

export class ApplicationComponent implements OnInit
{
    ngOnInit()
    {
        window.prerenderReady = true;
    }
}

注意:要使上述内容在 Typescript 中工作,您需要确保 属性 prerenderReady 存在于类型 Window 上。例如:

interface Window
{
    prerenderReady:boolean;
}

在服务器端,确保在配置任何路由之前配置预呈现节点。例如:

export function routeConfig(app:express.Application):void
{
    // Prerender
    app.use(require('prerender-node').set('prerenderServiceUrl', CONFIG.prerenderServiceUrl));

    // Angular routes should return index.html
    app.route(CONFIG.angularRoutes)
        .get((req:Request, res:Response, next:NextFunction) =>
        {
            return res.sendFile(`${app.get('appPath')}/client/index.html`);
        });
}

希望这对某人有所帮助:)