我正在尝试在Angular 5项目中设置Progressive Web App。我还使用Angular Universal进行服务器端渲染。
我在从API缓存数据时遇到问题。我做了一个看起来像https://example.net/getContent/param_1/param_2/param_3的Rest API。 param_1 是来自路线参数的网页名称, param_2 是一个网址, param_3 是一个lang代码。在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似乎是最好的解决方案。如果它能够正常工作,我们会发送答案。
答案 0 :(得分:0)
getAllPages
网站,我将返回所有网页列表。然后我将它插入到后台的IndexedDB中,就像那样:
private async saveData(url, data) {
if (data) {
for (let element of data) {
await this.ngf.getItem(element.urlPath).then(async res => {
if (!await res) {
await this.http.get(`${url}/getContent/${element.urlPath}/${element.languageCode}/0`).toPromise().then(async response => {
await this.ngf.setItem(element.urlPath, await response);
})
await this.http.get(`${url}/getLayout/${element.urlPath}/${element.languageCode}`).toPromise().then(async response => {
await this.ngf.setItem(`${element.urlPath}/layout`, await response);
})
}
})
}
};
}
感谢您的回答。
答案 1 :(得分:0)
@ patryk-panek您可以通过避免api路径中的通配符来解决缓存问题。例如
FB.ui({
method: 'feed',
name: 'Test Name',
link: "http://192.168.1.149/",
picture: 'http://www.fbrell.com/f8.jpg',
caption: 'An example caption',
}, function(response){
console.log(response);
})