How to deploy angular 4 universal app to firebase

2019-04-13 22:58发布

问题:

With the introduction to angular 4 of universal, I can't figure out how to deploy the app successfully to a firebase hosting. I followed the steps here https://github.com/angular/angular-cli/wiki/stories-universal-rendering

I can't figure out this part of it though:

"The bundle produced has a hash in the filename from webpack. When deploying this to a production server, you will need to ensure the correct bundle is required, either by renaming the file or passing the bundle name as an argument to your server."

Usually we just use ng build --prod

Then firebase deploy the dist directory.

With this universal inclusion, which folder do I deploy to firebase?

dist-server or dist?

回答1:

There is a youtube video from Google on this subject here: https://youtu.be/gxCu5TEmxXE

Basically, as far as I can tell at the moment there is no way of linking your functions folder with your 'dist' folder, so what we have to do is overwrite the firebase.json settings to serve your app as a function. This basically means that your function (expressJS code) is serving the app rather than the static files in dist/

After following the youtube video your firebase.json should be as follows:

{
  "hosting": {
    "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "function": "ssrapp"
      }
    ]
  },
  "functions": {
    "predeploy": "npm --prefix functions run build",
    "source": "functions"
  }
}

Then you have to copy your dist files into your function directory, so that the function can bootstrap your app. If your setup is correct then when people load your URL, the server function in /functions will then redirect them to the browser & server files that are hosted in the functions directory.

We actually have to delete the index.html file in the dist/ folder so that firebase doesn't accidently serve this instead of rerouting the traffic through the server you have configured as a firebase function:

index.ts (server in functions folder)

import * as functions from 'firebase-functions';
import * as angularUniversal from 'angular-universal-express-firebase';

export let ssrapp = angularUniversal.trigger({
	index: __dirname + '/browser/index.html',
	main: __dirname + '/server/main.bundle',
	enableProdMode: true,
	browserCacheExpiry: 1200,
	cdnCacheExpiry: 600
});

Regards your question about the hashing, basically your angular-cli should be building 2 apps, the normal one and the 'SSR' Server rendered app. An example configuration is here:

  "apps": [
    {
      "root": "src",
      "outDir": "dist/browser",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "styles.scss",
        "trexco.scss"
      ],
      "scripts": [
        "../node_modules/moment/min/moment.min.js"
      ],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    },
      {
        "platform": "server",
        "root": "src",
        "outDir": "dist/server",
        "assets": [
          "assets",
          "favicon.ico"
        ],
        "index": "index.html",
        "main": "main.server.ts",
        "test": "test.ts",
        "tsconfig": "tsconfig.server.json",
        "testTsconfig": "tsconfig.spec.json",
        "prefix": "app",
        "styles": [
          "styles.scss",
          "trexco.scss"
        ],
        "scripts": [],
        "environmentSource": "environments/environment.ts",
        "environments": {
          "dev": "environments/environment.ts",
          "prod": "environments/environment.prod.ts"
        }
      }

  ],

Because the function in our /functions folder should always link to the server bundle, we just need to make sure that when we compile the server app we do so without hashes, this is as simple as this in your package.json

  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "build:client-and-server-bundles": "ng build --prod && ng build --prod --app 1 --output-hashing=false",
    "build:prerender": "npm run build:client-and-server-bundles && npm run webpack:server && npm run generate:prerender",
    "build:ssr": "npm run build:client-and-server-bundles && npm run webpack:server",
    "generate:prerender": "cd dist && node prerender",
    "webpack:server": "webpack --config webpack.server.config.js --progress --colors",
    "serve:prerender": "cd dist/browser && http-server",
    "serve:ssr": "node dist/server"
  },

If you build the server app (app 1) with the argument --output-hashing=false then the output will always be main.bundle, meaning that your server function can always find the correct file without extra logic.