首页 文章

Angular Service Worker - Rest API数据

提问于
浏览
2

我正在尝试在Angular 5项目中设置Progressive Web App . 我也使用Angular Universal进行服务器端渲染 .

我在从API缓存数据时遇到问题 . 我做了一个看起来像https://example.net/getContent/param_1/param_2/param_3的Rest API . 其中 param_1 是路由参数的页面名称, param_2 是一个lang url, 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');
  }
}

EDIT 在为Angular Universal设置服务器传输状态后,它在“缓存”选项卡中可见,但仍然无法脱机工作(带缓存选项卡的屏幕) .

Cache table

localForage似乎是最好的解决方案 . 如果有效,会发送答案 .

2 回答

  • 0

    哦,最后我找到了解决方案 . 谢谢你@nithalqb最好的主意 . ngforage-ng5工作正常!我添加到API 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);
                        })
                    }
                })
            }
        };
    }
    

    谢谢你的回答 .

  • 0

    @ patryk-panek你可以通过避免api路径中的通配符来解决缓存问题 . 例如

    "urls": [
      "https://example.net/getMenus",
      "https://example.net/getContent",
    ]
    

相关问题