Angular 6 ssr页面加载太大

时间:2019-01-13 10:38:21

标签: node.js angular typescript browser angular-universal

我第一次对angular或我自己感到非常失望,因为我不知道这是angular问题还是我的问题。我有一个angular 6.0.0项目,它不是很大,但是也不小。我在项目中实现了angular universal ssr

没有ssr的结果: *提示我有惰性模块。 Main仅是我的第一页。

no-ssr

在heroku免费服务器上使用ssr的结果(但localhost上的main.js大小相同,而localhost上的加载时间为40ms):

Heroku server ssr

我认为这些数字太大了吗? SSR可以正常工作,但是load time太糟糕了-来自184ms to 2.50s.

详细的项目信息:

Package.json(提示:我正在使用heroku将我的网站部署到nodejs服务器):

{
....
"engines": {
    "node": "8.11.1",
    "npm": "6.5.0"
  },
  "scripts": {
    "ng": "ng",
    "start:heroku": "node dist/server",
    "start": "ng serve --aot",
    "build": "ng build",
    "lint": "ng lint Deals",
    "build:client-and-server-bundles": "ng build --prod --aot && ng run Deals:server:production",
    "build:prerender": "npm run build:client-and-server-bundles && npm run compile:server && npm run generate:prerender",
    "build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
    "compile:server": "tsc -p server.tsconfig.json",
    "generate:prerender": "cd dist && node prerender",
    "serve:prerender": "cd dist/browser && http-server",
    "serve:ssr": "node dist/server ",
    "postinstall": "npm run build:ssr"
  },
  "pre-commit": [],
  "private": true,
  "dependencies": {
    "@angular-devkit/build-angular": "0.6.0",
    "@angular/animations": "^6.1.10",
    "@angular/cdk": "^7.1.0",
    "@angular/cli": "6.0.0",
    "@angular/common": "^6.0.0",
    "@angular/compiler": "^6.0.0",
    "@angular/compiler-cli": "^6.0.0",
    "@angular/core": "^6.0.0",
    "@angular/fire": "^5.1.1",
    "@angular/forms": "^6.0.0",
    "@angular/http": "^6.0.0",
    "@angular/language-service": "^6.0.0",
    "@angular/material": "^7.1.0",
    "@angular/platform-browser": "^6.0.0",
    "@angular/platform-browser-dynamic": "^6.0.0",
    "@angular/platform-server": "^6.0.0",
    "@angular/pwa": "^0.11.4",
    "@angular/router": "^6.0.0",
    "@angular/service-worker": "^6.0.0",
    "@ngrx/core": "^1.2.0",
    "@ngrx/store-devtools": "^6.1.2",
    "@nguniversal/common": "^6.0.0",
    "@nguniversal/express-engine": "^6.0.0",
    "@nguniversal/module-map-ngfactory-loader": "^6.0.0",
    "@ngx-translate/core": "^10.0.2",
    "@ngx-translate/http-loader": "^4.0.0",
    "@types/node": "^8.0.30",
    "angular2-cookie": "^1.2.6",
    "angularfire2": "^5.1.1",
    "angulartics-clicky": "^1.0.0",
    "angulartics2": "^6.3.1",
    "codelyzer": "^4.0.2",
    "core-js": "^2.4.1",
    "express": "^4.15.2",
    "firebase": "^5.6.0",
    "hammerjs": "^2.0.8",
    "http-server": "^0.10.0",
    "ngx-page-scroll": "^5.0.1",
    "nodemailer": "^4.7.0",
    "npm": "^6.4.1",
    "pre-commit": "^1.2.2",
    "reflect-metadata": "^0.1.10",
    "rxjs": "^6.2.2",
    "ts-loader": "^4.2.0",
    "tslint": "^5.7.0",
    "typescript": "~2.7.2",
    "zone.js": "^0.8.26"
  },

呈现ssr的

Server.ts:

import 'zone.js/dist/zone-node';
import 'reflect-metadata';
import { enableProdMode } from '@angular/core';

// Express Engine
import { ngExpressEngine } from '@nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';

import * as express from 'express';
import { join } from 'path';

// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();

// Express server
const app = express();

const PORT = process.env.PORT || 4000;
const DIST_FOLDER = join(process.cwd(), 'dist');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./server/main');

// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
  bootstrap: AppServerModuleNgFactory,
  providers: [
    provideModuleMap(LAZY_MODULE_MAP),
    // In case you want to use an AppShell with SSR and Lazy loading
    // you'd need to uncomment the below. (see: https://github.com/angular/angular-cli/issues/9202)
  //   {
    //   provide: NgModuleFactoryLoader,
    //   useClass: ModuleMapNgFactoryLoader,
    //   deps: [
    //     Compiler,
    //     MODULE_MAP
    //   ],
    // },
  ]
}));

app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'browser'));

// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });

// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser'), {
  maxAge: '1y'
}));

// All regular routes use the Universal engine
app.get('*', (req, res) => {
  res.render('index', { req });
});

// Start up the Node server
app.listen(PORT, () => {});

server.tsconfig.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "typeRoots": [
      "node_modules/@types"
    ],
    "lib": [
      "es2017",
      "dom"
    ]
  },
  "include": ["server.ts", "prerender.ts"]
}

1 个答案:

答案 0 :(得分:0)

也许您需要使用压缩? https://github.com/Angular-RU/angular-universal-starter/blob/6d1e980655a55b2e1651ac5d040aaff80bc8fe8c/server.ts#L45

server.ts

import 'zone.js/dist/zone-node';
import 'reflect-metadata';

const test = process.env['TEST'] === 'true';

const domino = require('domino');
const fs = require('fs');
const path = require('path');
const template = fs.readFileSync(path.join(__dirname, '.', 'dist', 'index.html')).toString();
const win = domino.createWindow(template);
const files = fs.readdirSync(`${process.cwd()}/dist-server`);

global['window'] = win;
Object.defineProperty(win.document.body.style, 'transform', {
  value: () => {
    return {
      enumerable: true,
      configurable: true,
    };
  },
});
global['document'] = win.document;
global['CSS'] = null;
// global['XMLHttpRequest'] = require('xmlhttprequest').XMLHttpRequest;
global['Prism'] = null;

import { enableProdMode } from '@angular/core';
import * as express from 'express';
import * as compression from 'compression';
import * as cookieparser from 'cookie-parser';
const { provideModuleMap } = require('@nguniversal/module-map-ngfactory-loader');

const mainFiles = files.filter((file) => file.startsWith('main'));
const hash = mainFiles[0].split('.')[1];
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require(`./dist-server/main.${hash}`);
import { ngExpressEngine } from '@nguniversal/express-engine';
import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens';
const PORT = process.env.PORT || 4000;
import { ROUTES } from './static.paths';
import { exit } from 'process';

enableProdMode();

const app = express();
app.use(compression());
app.use(cookieparser());

const redirectowww = false;
const redirectohttps = true;
const wwwredirecto = true;
app.use((req, res, next) => {
  // for domain/index.html
  if (req.url === '/index.html') {
    res.redirect(301, 'https://' + req.hostname);
  }

  // check if it is a secure (https) request
  // if not redirect to the equivalent https url
  if (
    redirectohttps &&
    req.headers['x-forwarded-proto'] !== 'https' &&
    req.hostname !== 'localhost'
  ) {
    // special for robots.txt
    if (req.url === '/robots.txt') {
      next();
      return;
    }
    res.redirect(301, 'https://' + req.hostname + req.url);
  }

  // www or not
  if (redirectowww && !req.hostname.startsWith('www.')) {
    res.redirect(301, 'https://www.' + req.hostname + req.url);
  }

  // www or not
  if (wwwredirecto && req.hostname.startsWith('www.')) {
    const host = req.hostname.slice(4, req.hostname.length);
    res.redirect(301, 'https://' + host + req.url);
  }

  // for test
  if (test && req.url === '/test/exit') {
    res.send('exit');
    exit(0);
    return;
  }

  next();
});

app.engine(
  'html',
  ngExpressEngine({
    bootstrap: AppServerModuleNgFactory,
    providers: [provideModuleMap(LAZY_MODULE_MAP)],
  }),
);

app.set('view engine', 'html');
app.set('views', 'src');

app.get('*.*', express.static(path.join(__dirname, '.', 'dist')));
app.get(ROUTES, express.static(path.join(__dirname, '.', 'static')));

app.get('*', (req, res) => {
  global['navigator'] = req['headers']['user-agent'];
  const http =
    req.headers['x-forwarded-proto'] === undefined ? 'http' : req.headers['x-forwarded-proto'];

  const url = req.originalUrl;
  // tslint:disable-next-line:no-console
  console.time(`GET: ${url}`);
  res.render(
    '../dist/index',
    {
      req: req,
      res: res,
      providers: [
        {
          provide: REQUEST,
          useValue: req,
        },
        {
          provide: RESPONSE,
          useValue: res,
        },
        {
          provide: 'ORIGIN_URL',
          useValue: `${http}://${req.headers.host}`,
        },
      ],
    },
    (err, html) => {
      if (!!err) {
        throw err;
      }

      // tslint:disable-next-line:no-console
      console.timeEnd(`GET: ${url}`);
      res.send(html);
    },
  );
});

app.listen(PORT, () => {
  console.log(`listening on http://localhost:${PORT}!`);
});