Angular Service Worker - Rest API数据

时间:2018-02-02 11:09:39

标签: angular rest api typescript service-worker

我正在尝试在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设置服务器传输状态后,它在“缓存”选项卡中可见,但仍然无法脱机工作(带缓存选项卡的屏幕)。

Cache table

localForage似乎是最好的解决方案。如果它能够正常工作,我们会发送答案。

2 个答案:

答案 0 :(得分: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);
                    })
                }
            })
        }
    };
}

感谢您的回答。

答案 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);
})