【问题标题】:Server side rendering using Angular4(Angular Universal) [closed]使用Angular4(Angular Universal)的服务器端渲染[关闭]
【发布时间】:2017-11-22 11:15:03
【问题描述】:

我正在开发一个 Angular4 webpack 项目,我想在其中添加 AngularUniversal 以使服务器端渲染成为可能。但是大多数教程都使用 angular cli。我想将 Universal 与 webpack 集成。我尝试遵循这个 tutorial没有运气。有人可以帮忙吗。

【问题讨论】:

  • 服务器端是否使用 node.js 和 .net core?
  • 我正在使用 nodejs。

标签: angular webpack angular-universal


【解决方案1】:

给定教程中提到的示例是使用 Angular 资源部分中提到的示例。他们最近更新了他们的文档,还没有提供详细的文档来实现@angular/universal。 This 曾经是您要查找的页面,但其中提到了here 的一些问题。可能这就是他们删除它并决定重写它的原因。

【讨论】:

    【解决方案2】:

    Angular Universal 仅适用于 Angular 2。如果您想从头开始,您可以使用此 Angular 4 Universal Seed,它具有以下所有功能:

    • 角 4
    • WebPack
    • 开发/生产模式
    • SCSS 编译
    • i18n、SEO 和 TSLint/codelyzer
    • 延迟加载、配置、缓存

    或者,如果您已经运行了 Angular 4 项目,您可以通过在代码中进行以下设置来集成 Universal:

    安装这些包:
    npm install @angular/{common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router,animations}@latest typescript@latest --save

    npm install express @types/express --save-dev

    将此添加到您的 app.module.ts 文件中

    import { BrowserModule } from '@angular/platform-browser';
    BrowserModule.withServerTransition({
      appId: 'my-app-id'   // withServerTransition is available only in Angular 4
    }),
    

    创建以下文件

    src/uni/app.server.ts

    import { NgModule } from '@angular/core';
    import { APP_BASE_HREF } from '@angular/common';
    import { ServerModule } from '@angular/platform-server';
    import { AppComponent } from '../app/app';
    import { AppModule } from '../app/app.module';
    import 'reflect-metadata';
    import 'zone.js';
    @NgModule({
      imports: [
        ServerModule,
        AppModule
      ],
      bootstrap: [
        AppComponent
      ],
      providers: [
        {provide: APP_BASE_HREF, useValue: '/'}
      ]
    })
    export class AppServerModule {
    }
    


    src/uni/server-uni.ts

    import 'zone.js/dist/zone-node';
    import 'zone.js';
    import 'reflect-metadata';
    import { enableProdMode } from '@angular/core';
    import { AppServerModuleNgFactory } from  '../../aot/src/uni/app.server.ngfactory';
    import * as express from 'express';
    import { ngUniversalEngine } from './universal-engine';
    enableProdMode();
    const server = express();
    // set our angular engine as the handler for html files, so it will be used to render them.
    server.engine('html', ngUniversalEngine({
        bootstrap: [AppServerModuleNgFactory]
    }));
    // set default view directory
    server.set('views', 'src');
    // handle requests for routes in the app.  ngExpressEngine does the rendering.
    server.get(['/', '/dashboard', '/heroes', '/detail/:id'], (req:any, res:any) => {
        res.render('index.html', {req});
    });
    // handle requests for static files
    server.get(['/*.js', '/*.css'], (req:any, res:any, next:any) => {
        let fileName: string = req.originalUrl;
        console.log(fileName);
        let root = fileName.startsWith('/node_modules/') ? '.' : 'src';
        res.sendFile(fileName, { root: root }, function (err:any) {
            if (err) {
                next(err);
            }
        });
    });
    // start the server
    server.listen(3200, () => {
        console.log('listening on port 3200...');
    });
    

    src/uni/universal-engine.ts

    import * as fs from 'fs';
    import { renderModuleFactory } from '@angular/platform-server';
    const templateCache = {}; // cache for page templates
    const outputCache = {};   // cache for rendered pages
    export function ngUniversalEngine(setupOptions: any) {
      return function (filePath: string, options: { req: Request }, callback: (err: Error, html: string) => void) {
        let url: string = options.req.url;
        let html: string = outputCache[url];
        if (html) {
          // return already-built page for this url
          console.log('from cache: ' + url);
          callback(null, html);
          return;
        }
        console.log('building: ' + url);
        if (!templateCache[filePath]) {
          let file = fs.readFileSync(filePath);
          templateCache[filePath] = file.toString();
        }
        // render the page via angular platform-server
        let appModuleFactory = setupOptions.bootstrap[0];
        renderModuleFactory(appModuleFactory, {
          document: templateCache[filePath],
          url: url
        }).then(str => {
          outputCache[url] = str;
          callback(null, str);
        });
      };
    }
    

    在我假设位于根目录中的 tsconfig.ts 文件中添加以下配置

    {
        "compilerOptions": {
            "baseUrl": "",
            "declaration": false,
            "emitDecoratorMetadata": true,
            "experimentalDecorators": true,
            "lib": ["es2016", "dom"],
            "moduleResolution": "node",
            "outDir": "./dist/out-tsc",
            "sourceMap": true,
            "target": "es5",
            "module": "commonjs",
            "types": ["node"],
            "typeRoots": [
                "node_modules/@types"
            ]
        },
        "files": [
            "src/uni/app.server.ts",
            "src/uni/server-uni.ts"
        ],
        "angularCompilerOptions": {
            "genDir": "aot",
            "entryModule": "./src/app/app.module#AppModule",
            "skipMetadataEmit": true
        },
        "exclude": [
            "test.ts",
            "**/*.spec.ts"
        ]
    }
    

    在根目录中添加您的 webpack.config.uni.js

    const ngtools = require('@ngtools/webpack');
    const webpack = require('webpack');
    const path = require('path');
    const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin");
    module.exports = {
        devtool: 'source-map',
        entry: {
            main: ['./src/uni/app.server.ts', './src/uni/server-uni.ts']
        },
        resolve: {
            extensions: ['.ts', '.js']
        },
        target: 'node',
        output: {
            path: path.join(__dirname, "dist"),
            filename: 'server.js'
        },
        plugins: [
            new ngtools.AotPlugin({
                tsConfigPath: './tsconfig.json'
            })
        ],
        module: {
            rules: [
                {
                    test: /\.(scss|html|png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                    use: 'raw-loader'
                },
                { test: /\.ts$/,  loader: require.resolve('@ngtools/webpack') },
                {
                    test: /\.(png|jpg|woff|woff2|eot|ttf|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                    loader: 'url?limit=512&&name=[path][name].[ext]?[hash]'
                },
                { test: /\.scss$/, use: [{
                    loader: "style-loader" // creates style nodes from JS strings
                }, {
                    loader: "css-loader" // translates CSS into CommonJS
                }, {
                    loader: "sass-loader" // compiles Sass to CSS
                }] }
            ]
        }
    }
    

    package.json 文件中添加以下脚本:

    "ngc-build": "ngc -p ./tsconfig.json", // To generate ngFactory file
    "build:uni": "webpack --config webpack.config.uni.js",
    "serve:uni": "node dist/server.js",
    

    我们应该记住一些事情:

    • windowdocumentnavigator 和其他浏览器类型 - 在服务器上不存在 - 因此使用它们或任何使用它们的库(例如 jQuery)将不起作用。如果您确实需要其中的某些功能,您确实可以在 link 中提供一些选项。

    【讨论】:

      【解决方案3】:

      Angular Universal 仅用于 Angular 2.x。 Angular 4.x 需要使用平台服务器。示例如下:

      对于角度 2.X.X:

      AngularClass 的种子项目使用 express/universal

      https://github.com/angular/universal-starter

      对于角度 4.X.X 使用 Angular 平台服务器

      https://github.com/ng-seed/universal

      还有一些其他的例子:

      【讨论】:

        【解决方案4】:

        您可以在this blog 上找到有关使用 Webpack 进行服务器端渲染的 Angular 4 教程。

        特点:

        • 它基于 Angular 4 构建
        • 它不依赖于 Angular CLI
        • 它基于 Webpack 构建
        • 博客分三个阶段提供分步说明:
          • 第 1 阶段:在 Docker 容器上运行服务器端渲染的 Hello World 页面(为了您的方便,我提供了一个预安装的 Docker 映像,但说明应该适用于您自己的 Angular 环境)
          • 第 2 阶段:在主页上创建新的功能链接
          • 第 3 阶段(可选):通过 RESTful API 动态插入 WordPress 博客 POST

        可以在 Docker 主机上轻松查看最终结果,如下所示:

        (dockerhost)$ docker run -it -p 8002:8000 oveits/angular_hello_world:centos bash
        (container)# git clone https://github.com/oveits/ng-universal-demo
        (container)# cd ng-universal-demo
        (container)# npm i
        (container)# npm run start
        

        我选择了上面的 8002 端口,因为我已经在 8000 和 8001 端口上运行了其他示例;如果 docker 主机在 Virtualbox 上运行,您可能需要从 Virtualbox 主机的 8002 到 Virtualbox VM 的 8002 的端口映射。

        在浏览器上,导航至http://localhost:8002/blog。您将看到从 Wordpress API 下载的博客文章的内容。使用右键单击->查看源代码,您将看到 HTML 内容。这表明这是一个服务器端呈现的页面。

        PS:和你试用过的教程一样,教程是基于Git project that originally has been created by Rob Wormald,但是使用this fork by FrozenPandaz,我发现了一个升级到Angular 4并且与Webpack配合得更好的版本(见附录the blog 的详细信息)。

        【讨论】:

          猜你喜欢
          • 2021-11-01
          • 2017-04-27
          • 2021-06-11
          • 2021-10-20
          • 1970-01-01
          • 2018-09-23
          • 2018-01-14
          • 2016-10-01
          • 1970-01-01
          相关资源
          最近更新 更多