希望那里有一些了不起的灵魂可以帮助我。
我有一个简单的 ANGULAR 16 应用程序,可以为搜索引擎生成 SEO 友好的页面,并且我的应用程序是用英语编写的并翻译为 es。
当我运行
build:ssr
时,会在“dist/appName/browser”文件夹内创建 2 个文件夹(en、es)。
这些文件夹在本地完美运行,当我查看每个页面的源代码时,我可以看到呈现给 SEO 的页面。
当我将这 2 个文件夹部署到 Firebase 托管时,该应用程序运行完美,但源代码似乎没有通用、没有 SEO 友好的页面。我究竟做错了什么? 我阅读了那里的每一个教程,但没有任何更新足够。
我遵循官方Firebase文档(https://firebase.google.com/docs/hosting/frameworks/angular),其中没有提到使用函数,并启用了webframeworks
firebase experiments:enable webframeworks
,运行firebase init hosting
,回答了问题如下并得到最终错误:
cd projects/appName firebase init hosting
Detected an existing Angular codebase in the current directory, should we use this? No
Do you want to use a web framework? (experimental) Yes
What folder would you like to use for your web application's root directory? dist/appName/browser
Could not determine the web framework in use.
Please choose the framework: Angular
Error: This command is not available when running the Angular CLI inside a workspace.
任何帮助将不胜感激,我是初学者,所以请善意:) 以下是我在该项目中拥有的更重要的文件:
角度.JSON
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"appName": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": { "style": "scss" }
},
"i18n": {
"sourceLocale": {
"baseHref": "/",
"code": "en"
},
"locales": {
"es": {
"translation": "src/i18n/messages.es.xlf",
"baseHref": "es/"
}
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"localize": true,
"outputPath": "dist/appName/browser",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": [ "zone.js" ],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/assets"
],
"styles": [
"src/assets/scss/styles.scss"
],
"scripts": []
},
"configurations": {
"production": {
"optimization": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "10mb",
"maximumError": "10mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true,
"localize": ["en"]
},
"es": { "localize": ["es"], "baseHref": "/es/" }
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "appName:build:production"
},
"development": {
"browserTarget": "appName:build:development"
},
"es": { "browserTarget": "appName:build:es" }
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "ng-extract-i18n-merge:ng-extract-i18n-merge",
"options": {
"browserTarget": "appName:build",
"format": "xlf",
"outputPath": "src/i18n",
"targetFiles": [
"messages.es.xlf"
]
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/assets"
],
"styles": [
"src/assets/scss/styles.scss"
],
"scripts": []
}
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/appName/server",
"main": "server.ts",
"tsConfig": "tsconfig.server.json",
"inlineStyleLanguage": "scss"
},
"configurations": {
"production": {
"outputHashing": "media"
},
"development": {
"optimization": false,
"sourceMap": true,
"extractLicenses": false,
"vendorChunk": true,
"buildOptimizer": false
}
},
"defaultConfiguration": "production"
},
"serve-ssr": {
"builder": "@nguniversal/builders:ssr-dev-server",
"configurations": {
"development": {
"browserTarget": "appName:build:development",
"serverTarget": "appName:server:development"
},
"production": {
"browserTarget": "appName:build:production",
"serverTarget": "appName:server:production"
}
},
"defaultConfiguration": "development"
},
"prerender": {
"builder": "@nguniversal/builders:prerender",
"options": {
"routes": [ "/" ]
},
"configurations": {
"production": {
"browserTarget": "appName:build:production",
"serverTarget": "appName:server:production"
},
"development": {
"browserTarget": "appName:build:development",
"serverTarget": "appName:server:development"
}
},
"defaultConfiguration": "production"
},
"lint": {
"builder": "@angular-eslint/builder:lint",
"options": {
"lintFilePatterns": [
"src/**/*.ts",
"src/**/*.html"
]
}
}
}
}
},
"cli": {
"schematicCollections": [ "@angular-eslint/schematics" ]
}
}
服务器.JS
import 'zone.js/node';
import { APP_BASE_HREF } from '@angular/common';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
import { LOCALE_ID } from '@angular/core';
// The Express app is exported so that it can be used by serverless Functions.
export function app(lang:string): express.Express {
const server = express();
const distFolder = join(process.cwd(), `dist/appName/browser/${lang}`);
const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/main/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
extraProviders: [{ provide: LOCALE_ID, useValue: lang}]
} as any));
server.set('view engine', 'html');
server.set('views', distFolder);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(distFolder, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
server.get('*', (req, res) => {
res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
});
return server;
}
function run(): void {
const port = process.env['PORT'] || 4000;
// Start up the Node server
const server = express();
const appEs = app('es');
const appEn = app('en');
server.use('/es', appEs);
server.use('/en', appEn);
server.use('', appEn);
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './src/main.server';
FIREBASE.JSON
{
"hosting": {
"target": "site",
"public": "dist/appName/browser",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "/es/**",
"destination": "/es/index.html"
},
{
"source": "**",
"destination": "en/index.html"
}
],
"i18n": {
"root": "/"
}
}
}
包.JSON
{
"name": "appName",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --open",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"dev:ssr": "ng run appName:serve-ssr",
"serve:ssr": "node dist/appName/server/main.js",
"build:ssr": "ng build && ng run appName:server",
"prerender": "ng run appName:prerender",
"build:universal": "npm run build:ssr && npm run serve:ssr",
"lint": "ng lint",
"start-es": "ng serve --configuration=es"
},
"private": true,
"dependencies": {
"@angular/animations": "^16.2.9",
"@angular/cdk": "^16.2.8",
"@angular/common": "^16.2.9",
"@angular/compiler": "^16.2.9",
"@angular/core": "^16.2.9",
"@angular/forms": "^16.2.9",
"@angular/material": "^16.2.8",
"@angular/platform-browser": "^16.2.9",
"@angular/platform-browser-dynamic": "^16.2.9",
"@angular/platform-server": "^16.2.9",
"@angular/router": "^16.2.9",
"@nguniversal/express-engine": "^16.2.0",
"express": "^4.15.2",
"rxjs": "~7.8.1",
"tslib": "^2.3.0",
"zone.js": "~0.13.3"
},
"devDependencies": {
"@angular-devkit/build-angular": "^16.2.6",
"@angular-eslint/builder": "16.2.0",
"@angular-eslint/eslint-plugin": "16.2.0",
"@angular-eslint/eslint-plugin-template": "16.2.0",
"@angular-eslint/schematics": "16.2.0",
"@angular-eslint/template-parser": "16.2.0",
"@angular/cli": "~16.2.6",
"@angular/compiler-cli": "^16.2.9",
"@angular/localize": "^16.2.9",
"@nguniversal/builders": "^16.2.0",
"@types/express": "^4.17.0",
"@types/jasmine": "~5.1.0",
"@types/node": "^20.8.4",
"@typescript-eslint/eslint-plugin": "^6.7.5",
"@typescript-eslint/parser": "^6.7.5",
"eslint": "^8.39.0",
"jasmine-core": "~5.1.1",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"ng-extract-i18n-merge": "^2.7.1",
"typescript": "~5.1.6"
}
}
好吧,Angular 17 解决了所有问题!一切都完美运行并部署到 Firebase 托管,无需使用生成 SRR 文件的函数。谢谢你,角!