使用Angular4(Angular Universal)进行服务器端渲染

我正在开发一个Angular4 webpack项目,我想添加AngularUniversal来使服务器端的渲染成为可能。但是大多数教程都使用了角度cli.I想将Universal和webpack集成在一起。我试着在这个教程中没有运气。帮帮我。


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

  • 角4
  • 的WebPack
  • dev / prod模式
  • 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 /服务器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 /万向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)将不起作用。 如果您确实需要某些功能,您在此链接中提供了一些选项。

  • 角度通用仅用于角度2.x。 Angular 4.x您需要使用平台服务器。示例如下:

    对于角度2.XX:

    AngularClass的种子项目使用express / universal

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

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

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

    还有其他几个例子:

  • https://github.com/hs950559/ng4-univ
  • https://github.com/designcourse/angular-seo在这里学习一步一步的实施

  • 给出的教程中提到的示例使用的是Angular Resources部分中提到的示例。 他们最近更新了他们的文档,并且还没有提供详细的文档来实现@ angular / universal。 这曾经是你正在寻找的页面,但它在这里提到了一些问题。 可能这就是为什么他们删除它并决定重写它。

    链接地址: http://www.djcxy.com/p/5301.html

    上一篇: Server side rendering using Angular4(Angular Universal)

    下一篇: Angular Universal does not render all in server side