我是 ember 和服务人员的初学者。我的目标是设置一个可以离线工作的简单 ember 应用程序。我基本上有一个可通过 API (GET/POST) 获得的元素列表。
当我在线时,一切都按预期工作。我可以获取列表并发布新项目。当我离线时,应用程序可以工作,但是一旦我重新上线,网络请求就不会执行。所有网络请求实际上都是在我离线时执行的(显然失败了)。我希望服务人员缓存网络请求并仅在我重新联机后执行它们。这是错的吗?
这里有一些关于我的设置的信息:
灰烬版本:
- 余烬-cli:2.13.1
- 节点:7.10.0
- 操作系统:达尔文 x64
Service Worker 附加组件(如 app/package.json 中所列):
"ember-service-worker": "^0.6.6",
"ember-service-worker-asset-cache": "^0.6.1",
"ember-service-worker-cache-fallback": "^0.6.1",
"ember-service-worker-index": "^0.6.1",
我可能还应该提到我在 1.1.3 版本中使用了 ember-django-adapter。
这是我的应用程序/ember-cli-build.js
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
'esw-cache-fallback': {
// RegExp patterns specifying which URLs to cache.
patterns: [
'http://localhost:8000/api/v1/(.*)',
],
// changing this version number will bust the cache
version: '1'
}
});
return app.toTree();
};
我的网络请求 (GET/POST) 转到http://localhost:8000/api/v1/properties/。
这是我的 app/adapters/applications.js
import DS from 'ember-data';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
export default DS.JSONAPIAdapter.extend(DataAdapterMixin, {
namespace: 'api/v1',
host: 'http://localhost:8000',
authorizer: 'authorizer:token',
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
buildURL: function(type, id, record) {
return this._super(type, id, record) + '/';
}
});
当我打开应用程序时,服务人员会注册:
(function () {
'use strict';
self.addEventListener('install', function installEventListenerCallback(event) {
return self.skipWaiting();
});
self.addEventListener('activate', function installEventListenerCallback(event) {
return self.clients.claim();
});
const FILES = ['assets/connect.css', 'assets/connect.js', 'assets/connect.map', 'assets/failed.png', 'assets/passed.png', 'assets/test-support.css', 'assets/test-support.js', 'assets/test-support.map', 'assets/tests.js', 'assets/tests.map', 'assets/vendor.css', 'assets/vendor.js', 'assets/vendor.map'];
const PREPEND = undefined;
const VERSION$1 = '1';
const REQUEST_MODE = 'cors';
/*
* Deletes all caches that start with the `prefix`, except for the
* cache defined by `currentCache`
*/
var cleanupCaches = (prefix, currentCache) => {
return caches.keys().then((cacheNames) => {
cacheNames.forEach((cacheName) => {
let isOwnCache = cacheName.indexOf(prefix) === 0;
let isNotCurrentCache = cacheName !== currentCache;
if (isOwnCache && isNotCurrentCache) {
caches.delete(cacheName);
}
});
});
};
const CACHE_KEY_PREFIX = 'esw-asset-cache';
const CACHE_NAME = `${CACHE_KEY_PREFIX}-${VERSION$1}`;
const CACHE_URLS = FILES.map((file) => {
return new URL(file, (PREPEND || self.location)).toString();
});
/*
* Removes all cached requests from the cache that aren't in the `CACHE_URLS`
* list.
*/
const PRUNE_CURRENT_CACHE = () => {
caches.open(CACHE_NAME).then((cache) => {
return cache.keys().then((keys) => {
keys.forEach((request) => {
if (CACHE_URLS.indexOf(request.url) === -1) {
cache.delete(request);
}
});
});
});
};
self.addEventListener('install', (event) => {
event.waitUntil(
caches
.open(CACHE_NAME)
.then((cache) => {
return Promise.all(CACHE_URLS.map((url) => {
let request = new Request(url, { mode: REQUEST_MODE });
return fetch(request)
.then((response) => {
if (response.status >= 400) {
throw new Error(`Request for ${url} failed with status ${response.statusText}`);
}
return cache.put(url, response);
})
.catch(function(error) {
console.error(`Not caching ${url} due to ${error}`);
});
}));
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(
Promise.all([
cleanupCaches(CACHE_KEY_PREFIX, CACHE_NAME),
PRUNE_CURRENT_CACHE()
])
);
});
self.addEventListener('fetch', (event) => {
let isGETRequest = event.request.method === 'GET';
let shouldRespond = CACHE_URLS.indexOf(event.request.url) !== -1;
if (isGETRequest && shouldRespond) {
event.respondWith(
caches.match(event.request, { cacheName: CACHE_NAME })
.then((response) => {
if (response) {
return response;
}
return fetch(event.request);
})
);
}
});
const VERSION$2 = '1';
const PATTERNS = ['http://localhost:8000/api/v1/(.*)'];
/**
* Create an absolute URL, allowing regex expressions to pass
*
* @param {string} url
* @param {string|object} baseUrl
* @public
*/
function createNormalizedUrl(url, baseUrl = self.location) {
return decodeURI(new URL(encodeURI(url), baseUrl).toString());
}
/**
* Create an (absolute) URL Regex from a given string
*
* @param {string} url
* @returns {RegExp}
* @public
*/
function createUrlRegEx(url) {
let normalized = createNormalizedUrl(url);
return new RegExp(`^${normalized}$`);
}
/**
* Check if given URL matches any pattern
*
* @param {string} url
* @param {array} patterns
* @returns {boolean}
* @public
*/
function urlMatchesAnyPattern(url, patterns) {
return !!patterns.find((pattern) => pattern.test(decodeURI(url)));
}
const CACHE_KEY_PREFIX$1 = 'esw-cache-fallback';
const CACHE_NAME$1 = `${CACHE_KEY_PREFIX$1}-${VERSION$2}`;
const PATTERN_REGEX = PATTERNS.map(createUrlRegEx);
self.addEventListener('fetch', (event) => {
let request = event.request;
if (request.method !== 'GET' || !/^https?/.test(request.url)) {
return;
}
if (urlMatchesAnyPattern(request.url, PATTERN_REGEX)) {
event.respondWith(
caches.open(CACHE_NAME$1).then((cache) => {
return fetch(request)
.then((response) => {
cache.put(request, response.clone());
return response;
})
.catch(() => caches.match(event.request));
})
);
}
});
self.addEventListener('activate', (event) => {
event.waitUntil(cleanupCaches(CACHE_KEY_PREFIX$1, CACHE_NAME$1));
});
const VERSION$3 = '1';
const INDEX_HTML_PATH = 'index.html';
const CACHE_KEY_PREFIX$2 = 'esw-index';
const CACHE_NAME$2 = `${CACHE_KEY_PREFIX$2}-${VERSION$3}`;
const INDEX_HTML_URL = new URL(INDEX_HTML_PATH, self.location).toString();
self.addEventListener('install', (event) => {
event.waitUntil(
fetch(INDEX_HTML_URL, { credentials: 'include' }).then((response) => {
return caches
.open(CACHE_NAME$2)
.then((cache) => cache.put(INDEX_HTML_URL, response));
})
);
});
self.addEventListener('activate', (event) => {
event.waitUntil(cleanupCaches(CACHE_KEY_PREFIX$2, CACHE_NAME$2));
});
self.addEventListener('fetch', (event) => {
let request = event.request;
let isGETRequest = request.method === 'GET';
let isHTMLRequest = request.headers.get('accept').indexOf('text/html') !== -1;
let isLocal = new URL(request.url).origin === location.origin
if (isGETRequest && isHTMLRequest && isLocal) {
event.respondWith(
caches.match(INDEX_HTML_URL, { cacheName: CACHE_NAME$2 })
);
}
});
}());
这是网络请求在 Chrome 中的显示方式:离线时的网络请求
我认为问题出在 ember-service-worker-cache-fallback 的配置中。但我不太确定。欢迎任何想法或指向工作示例的链接。到目前为止,我没有找到很多关于 ember-service-worker-cache-fallback 的信息。
谢谢!