我正在尝试在我的Angular 5项目中设置渐进式Web应用程序。我还使用Angular Universal进行服务器端渲染。
我遇到了来自API的缓存数据问题。我创建了一个Rest API,它看起来像https://example.net/getContent/param_1/param_2/param_3。其中param_1是路由参数中的页面名称,param_2是一个语言URL,param_3是一个语言代码。在ngsw-config.json文件中,我这样做:
我认为它应该像"https://example.net/getMenus/anything/anything/anything/"一样缓存每个请求,但它没有。我无法离线运行应用程序,服务工作者在之前不会预加载所有页面数据。如何使其起作用?如何预加载所有页面的所有api调用?也许动态api调用是造成问题的原因?
这是我从SW和示例组件中的代码。
我遇到了来自API的缓存数据问题。我创建了一个Rest API,它看起来像https://example.net/getContent/param_1/param_2/param_3。其中param_1是路由参数中的页面名称,param_2是一个语言URL,param_3是一个语言代码。在ngsw-config.json文件中,我这样做:
"dataGroups": [{
"name": "api-performance",
"urls": [
"https://example.net/getMenus/**",
"https://example.net/getContent/**",
"https://example.net/getLayout/**",
"https://example.net/getFooter/**"
],
"cacheConfig": {
"maxSize": 10000,
"maxAge": "3d",
"strategy": "performance"
}
}]
我认为它应该像"https://example.net/getMenus/anything/anything/anything/"一样缓存每个请求,但它没有。我无法离线运行应用程序,服务工作者在之前不会预加载所有页面数据。如何使其起作用?如何预加载所有页面的所有api调用?也许动态api调用是造成问题的原因?
这是我从SW和示例组件中的代码。
app.module
// Core
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
import { ServiceWorkerModule } from '@angular/service-worker';
// Guards
import { AuthGuard } from './guards/auth.guard.service';
// Resolvers
import { LayoutResolver } from './resolvers/layout.resolver.service';
// Config
import { Config } from './config';
// Compontents
import { AppComponent } from './app.component';
import { ContainerComponent } from './container/container.component'
import { FooterComponent } from './footer/footer.component'
// Modules
import { MenuModule } from './menu/menu.module';
import { ContainerModule } from './container//container.module'
// Environment
import { environment } from '../environments/environment';
const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: ContainerComponent,
canActivate: [AuthGuard],
},
{
path: ':lang',
component: ContainerComponent,
resolve: { layout : LayoutResolver }
},
{
path : ':lang/:index',
component: ContainerComponent,
resolve: { layout : LayoutResolver }
}
];
@NgModule({
declarations: [
AppComponent,
FooterComponent
],
imports: [
RouterModule.forRoot(routes, {preloadingStrategy: PreloadAllModules}),
BrowserAnimationsModule,
BrowserModule.withServerTransition({ appId: 'main-app' }),
ServiceWorkerModule.register('/ngsw-worker.js', {enabled: environment.production}),
MenuModule,
ContainerModule
],
providers: [
AuthGuard,
Config,
LayoutResolver
],
bootstrap: [AppComponent]
})
export class AppModule { }
ngsw-config.json
{
"index": "/index.html",
"assetGroups": [{
"name": "app",
"installMode": "prefetch",
"resources": {
"files": [
"/index.html"
],
"versionedFiles": [
"/*.bundle.css",
"/*.bundle.js",
"/*.chunk.js"
]
}
}, {
"name": "assets",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/**",
"favicon.ico",
"**.png"
]
}
}],
"dataGroups": [{
"name": "api-performance",
"urls": [
"https://example.org/getMenus/**",
"https://example.org/getContent/**",
"https://example.org/getLayout/**",
"https://example.org/getFooter/**"
],
"cacheConfig": {
"maxSize": 10000,
"maxAge": "3d",
"strategy": "performance"
}
}]
}
.angular-cli.json
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "main-app",
"ejected": false
},
"apps": [
{
"root": "src",
"outDir": "dist/browser",
"assets": [
"assets",
"manifest.json",
"favicon.ico",
"robots.txt"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"serviceWorker": true,
"styles": [
"./assets/css/bootstrap.min.css",
"./assets/css/styles.less"
],
"scripts": [
"./assets/js/jquery-1.12.4.min.js",
"../node_modules/bootstrap/dist/js/bootstrap.min.js",
"./assets/js/functions.js"
],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/browser/environment.ts",
"prod": "environments/browser/environment.prod.ts"
}
},
{
"root": "src",
"outDir": "dist/server",
"assets": [
"assets",
"favicon.ico",
"robots.txt"
],
"platform": "server",
"index": "index.html",
"main": "main.server.ts",
"test": "test.ts",
"tsconfig": "tsconfig.server.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
"styles": [],
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/server/environment.ts",
"prod": "environments/server/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "less",
"component": {
}
}
}
例如一个组件:
news.component
import { Component } from '@angular/core';
import { ActivatedRoute } from "@angular/router";
import { Config } from "../../config";
import { ServerService } from "../../services/server.service";
import { SeoService } from "../../services/seo.service";
import { OnDestroy } from '@angular/core/src/metadata/lifecycle_hooks';
import { ISubscription } from 'rxjs/Subscription';
interface pageData {
banner: string;
data: any;
html: string;
text: string;
title: string;
}
@Component({
selector: 'app-news',
templateUrl: './news.component.html',
styleUrls: ['./news.component.less'],
providers : [Config, ServerService, SeoService],
})
export class NewsComponent implements OnDestroy {
subscription: ISubscription;
subscriptionHTTP: ISubscription;
URL: string;
langUrl: string;
active: string;
pageData: pageData;
headerText: Object;
constructor(private config: Config, private route: ActivatedRoute, private service: ServerService, private seo: SeoService) {
this.URL = this.config.impressURL;
this.langUrl = this.config.getLanguage();
this.subscription = this.route.params.subscribe( params => {
if(params.lang != this.langUrl) {
this.langUrl = params.lang;
}
let siteTitle = params.index;
if(typeof siteTitle != 'undefined') {
siteTitle = siteTitle.replace('.html', ' ');
siteTitle = siteTitle.replace(/-/g,' ');
}
this.subscriptionHTTP = this.service.getResponse(`${this.URL}/getContent/${params.index}/${this.langUrl}/0`).subscribe(
(response: any) => {
this.pageData = response;
this.seo.generateTags({
lang: this.langUrl,
title : siteTitle,
image : `${this.URL}/file/repository/${this.pageData.banner}`,
slug : params.index
})
}, (error) => {
console.log(error);
}
);
});
}
ngOnInit(): void {
}
ngOnDestroy() {
if(this.subscription) this.subscription.unsubscribe();
if(this.subscriptionHTTP) this.subscriptionHTTP.unsubscribe();
}
hideOnClick(element, target) {
element.parentNode.parentNode.classList.remove('in');
}
}
编辑 在设置了Angular Universal的服务器传输状态后,它会显示在缓存选项卡中,但仍无法离线使用(显示缓存选项卡的屏幕)。
localForage似乎是最好的解决方案。如果它有效,将发送答案。