Server side rendering using Angular4(Angular Universal)

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


This Angular Universal is only for Angular 2. If you want to start from scratch you can use this Angular 4 Universal Seed which has all features like:

  • Angular 4
  • WebPack
  • dev/prod modes
  • SCSS compilation
  • i18n, SEO, and TSLint/codelyzer
  • lazy loading, config, cache
  • Or if you already had Angular 4 project running you can Integrate Universal by doing following settings in your code:

    Install these packages:
    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

    Add this in your app.module.ts file

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

    Create following files

    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);
        });
      };
    }
    

    Add below configuration in your tsconfig.ts file which I assume located in root dir

    {
        "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"
        ]
    }
    

    Atlast your webpack.config.uni.js in root dir

    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
                }] }
            ]
        }
    }
    

    Add below scripts in you package.json file:

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

    There are certain things that we should keep in mind:

  • window , document , navigator , and other browser types - do not exist on the server - so using them, or any library that uses them (jQuery for example) will not work. You do have some options given in this link if you truly need some of this functionality.

  • Angular universal is only used for angular 2.x. Angular 4.x you need to use platform server.Examples are as follows:

    For angular 2.XX :

    AngularClass's seed project using express/universal

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

    For angular 4.XX Use angular platform server

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

    There are few other examples also:

  • https://github.com/hs950559/ng4-univ
  • https://github.com/designcourse/angular-seo learn step by step implementation here

  • The example that is mentioned in given tutorial is using the example mentioned in Angular Resources section. They have recently updated their docs and have not yet provided the detailed documentation to implement @angular/universal. This used to be the page you are looking for but it had some issues mentioned here. May be that's why they removed it and have decided to rewrite it.

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

    上一篇: 如何正确启动Angular Universal来运行服务器

    下一篇: 使用Angular4(Angular Universal)进行服务器端渲染