是否可以强制浏览器对不同路径使用相同的缓存条目?

问题描述 投票:0回答:1

例如,同一域有 2 条路径:

some_domain.com/first
some_domain.com/second

这两个路径都返回相同的文件,比如说 -

contents.html
。是否可以以打开
some_domain.com/first
后不会执行
some_domain.com/second
加载的方式配置缓存 - 因此它只会重用为
some_domain.com/first
准备的缓存?

是否应该通过返回一些特殊的假设的

Cache-Key: key_value
标头来在服务器端完成?可以找到的最相关的事情是这个问题: 尽管有查询字符串,是否可以在浏览器中缓存文件?

更新 - 我可以稍微改一下。我正在使用 React 及其 Router。服务器配置为针对所有可能的路径返回

index.html
,然后允许客户端 JS 处理路由。假设,配置了一些缓存来防止服务器返回的所有内容过期。但这里有一个问题。一旦我打开
some_domain.com/first
,它就会被缓存,所以下次我访问同一路径时,它不会再次下载。但即使
some_domain.com/second
返回相同的
index.html
,打开
some_domain.com/first
后它也不会被缓存 - 我还需要打开第二个路径(即通过在浏览器中重新加载相应页面)至少一次以使其缓存(虽然这显然毫无意义,因为两条路线返回相同的
index.html
)。

path browser-cache cache-control
1个回答
0
投票

我已经设法解决了问题中描述的问题。如果很快,它是由服务工作人员检查每个

event.request.referrer
事件的
"fetch"
来完成的 - 在我的例子中,每次为 SPA 获取根
index.html
时,它总是没有定义。因此,每次没有引用值时,我都会对同一个
/
目的地执行刷新和缓存。

如果您对特定实现感兴趣,这里是 Ktor 完成的一个非常简单的服务器配置示例(这里的关键点是引入

"Last-Modified"
标头来处理缓存):

fun main() {
    embeddedServer(Netty) {
        install(Compression) {
            gzip()
        }
        install(ConditionalHeaders) // adds proper "Last-Modified" headers according to states of served files
        routing {
            singlePageApplication {
                react("build/dist/js/productionExecutable")
            }
        }
    }.start(wait = true)
}

这是我的 Service Worker 的代码,里面有注释:

// Configs.

/** Key to store HTTP responses. */
const RESPONSE_CACHE = "responses-v1";



// Fetching.

/** An entry point to handle all requests. */
const fetchStrategy = event => {
    const destination = getDestinationToBeProcessedByServiceWorker(event);
    if (destination) {
        event.respondWith(fetchModifiedWithFallbackToCached(destination));
    }
};

/**
 * Prepares a destination to be processed by the service worker 
 * or returns `null` if the provided `event` should not be handled by the service worker.
 */
const getDestinationToBeProcessedByServiceWorker = event => {
    // No referrer value implies an attempt to fetch the root `index.html`:
    // it can have different paths, but to avoid unnecessary requests for each of these paths,
    // only `/` will be used to fetch and cache its contents.
    if (!event.request.referrer) {
        return "/";
    }
    // All other types of requests will be processed by default browser means (including caching).
    return null;
};

/**
 * Tries to fetch a `destination` and cache it if it was modified,
 * returns its cached version if there are no updates.
 * 
 * It seems inevitable to perform at least one additional network request inside a service worker
 * for each response has been already fetched by the browser before the service worker's activation:
 * there is no straightforward way to share service worker caches with default browser ones
 * or get response data for some static request performed by the browser to fetch a resource.
 */
const fetchModifiedWithFallbackToCached = async destination => {
    const cachedResponse = await getCachedResponse(destination);
    const request        = createRequestWithProperCacheHeader(destination, cachedResponse);
    let networkResponse;
    try {
        networkResponse = await fetch(request);
    } catch (error) {
        if (!cachedResponse) {
            throw error; // there could be some other fallback instead of just propagating the error
        }
    }
    if (await cacheNetworkResponseIfModified(destination, networkResponse)) {
        return networkResponse;
    }
    return cachedResponse;
};

/**
 * If there is some `cachedResponse` available, 
 * creates a request with a header to fetch the `destination` only if it was modified.
 * 
 * Returns just a pure `destination` otherwise.
 */
const createRequestWithProperCacheHeader = (destination, cachedResponse) => {
    if (cachedResponse) {
        return new Request(destination, {
            headers: new Headers({
                "If-Modified-Since": cachedResponse.headers.get("Last-Modified")
            })
        });
    } else {
        return destination;
    }
};

/**
 * Caches a provided `networkResponse` only if its status implies that it was modified:
 * returns `true` in this case, `false` - otherwise.
 */
const cacheNetworkResponseIfModified = async (destination, networkResponse) => {
    if (networkResponse && networkResponse.ok) {
        await cacheResponse(destination, networkResponse.clone());
        return true;
    } else {
        return false;
    }
};



// Caching.

const getCachedResponse = async destination => { 
    return await (await openResponseCache()).match(destination);
};

const cacheResponse = async (destination, response) => {
    await (await openResponseCache()).put(destination, response);
};

const openResponseCache = async () => await caches.open(RESPONSE_CACHE);



// Service worker's setup.

// Seems like there is no reason to perform any caching during the installation phase,
// because methods like `Cache.add(...)` are still performing fetching under the hood
// and do not allow to avoid one more additional request per each resource expected to be cached
// after the browser has already fetched it.
self.addEventListener("fetch", fetchStrategy);

作为奖励,可以很容易地扩展此 Service Worker 以包含静态资源缓存,从而拥有一种 SPA 离线模式。

© www.soinside.com 2019 - 2024. All rights reserved.