Javascript 如何强制更新单页应用程序 (SPA) 页面?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34388614/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to force update Single Page Application (SPA) pages?
提问by Pahlevi Fikri Auliya
In fully server side based rendering (non Web 2.0), deploying server side code would directly update client side pages upon page reload. In contrast, in React based Single Page Application, even after React components were updated, there would be still some clients using old version of the components (they only get the new version upon browser reload, which should rarely happen) -> If the pages are fully SPA, it's possible that some clients only refresh the pages after a few hours.
在完全基于服务器端的渲染(非 Web 2.0)中,部署服务器端代码将在页面重新加载时直接更新客户端页面。相比之下,在基于 React 的单页应用程序中,即使更新了 React 组件,仍然会有一些客户端使用旧版本的组件(他们只会在浏览器重新加载时才获得新版本,这种情况应该很少发生) -> 如果页面完全是 SPA,有些客户可能只在几个小时后刷新页面。
What techniques should be employed to make sure the old components versions are not used anymore by any clients?
应该采用什么技术来确保任何客户端不再使用旧组件版本?
Update: the API doesn't changed, only React Component is updated with newer version.
更新:API 没有改变,只有 React 组件更新了新版本。
采纳答案by hazardous
You can have a React component make an ajax request to your server, when the application loads, to fetch "interface version". In the server API, you can maintain an incremental value for the client version. The React component can store this value on the client (cookie/local storage/etc). When it detects a change, it can invoke window.location.reload(true);
which should force the browser to discard client cacheand reload the SPA. Or better still, inform the end-user that a new version will be loaded and ask them if they wish to save the work and then reload etc. Depends on what you wanna do.
您可以让 React 组件在应用程序加载时向您的服务器发出 ajax 请求,以获取“接口版本”。在服务器 API 中,您可以为客户端版本维护一个增量值。React 组件可以将此值存储在客户端(cookie/本地存储等)。当它检测到更改时,它可以调用window.location.reload(true);
which 应该强制浏览器丢弃客户端缓存并重新加载 SPA。或者更好的是,通知最终用户将加载新版本并询问他们是否希望保存工作然后重新加载等。取决于您想要做什么。
回答by Andy
Similar to Steve Taylor's answer but instead of versioning API endpoints I would version the client app, in the following way.
与 Steve Taylor 的回答类似,但我将通过以下方式对客户端应用程序进行版本控制,而不是对 API 端点进行版本控制。
With each HTTP request send a custom header, such as:
每个 HTTP 请求都会发送一个自定义标头,例如:
X-Client-Version: 1.0.0
X-Client-Version: 1.0.0
The server would then be able to intercept such header and respond accordingly.
然后服务器将能够拦截此类标头并相应地做出响应。
If the server is aware that the client's version is stale, for example if the current version is 1.1.0
, respond with an HTTP status code that will be appropriately handled by the client, such as:
如果服务器知道客户端的版本是过时的,例如如果当前版本是1.1.0
,则以客户端将适当处理的 HTTP 状态代码响应,例如:
418 - I'm a Teapot
418 - I'm a Teapot
The client can then be programmed to react to such a response by refreshing the app with:
然后可以对客户端进行编程以通过以下方式刷新应用程序来对此类响应做出反应:
window.location.reload(true)
window.location.reload(true)
The underlying premise is that the server is aware of the latest client version.
底层前提是服务器知道最新的客户端版本。
EDIT:
编辑:
A similar answer is given here.
回答by LIIT
What techniques should be employed to make sure the old components versions are not used anymore by any clients?
应该采用什么技术来确保任何客户端不再使用旧组件版本?
today (2018), many front apps use service workers. With it, it's possible to manage your app lifecycle by several means.
今天(2018 年),很多前端应用都使用了Service Worker。有了它,可以通过多种方式管理您的应用程序生命周期。
Here is a first example, by using a ui notification, asking your visitors to refresh webpage in order to get latest application version.
这是第一个示例,通过使用 ui 通知,要求访问者刷新网页以获取最新的应用程序版本。
import * as SnackBar from 'node-snackbar';
// ....
// Service Worker
// https://github.com/GoogleChrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js
const offlineMsg = 'Vous êtes passé(e) en mode déconnecté.';
const onlineMsg = 'Vous êtes de nouveau connecté(e).';
const redundantMsg = 'SW : The installing service worker became redundant.';
const errorMsg = 'SW : Error during service worker registration : ';
const refreshMsg = 'Du nouveau contenu est disponible sur le site, vous pouvez y accéder en rafraichissant cette page.';
const availableMsg = 'SW : Content is now available offline.';
const close = 'Fermer';
const refresh = 'Rafra?chir';
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
function updateOnlineStatus() {
SnackBar.show({
text: navigator.onLine ? onlineMsg : offlineMsg,
backgroundColor: '#000000',
actionText: close,
});
}
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
navigator.serviceWorker.register('sw.js').then((reg) => {
reg.onupdatefound = () => {
const installingWorker = reg.installing;
installingWorker.onstatechange = () => {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
SnackBar.show({
text: refreshMsg,
backgroundColor: '#000000',
actionText: refresh,
onActionClick: () => { location.reload(); },
});
} else {
console.info(availableMsg);
}
break;
case 'redundant':
console.info(redundantMsg);
break;
default:
break;
}
};
};
}).catch((e) => {
console.error(errorMsg, e);
});
});
}
// ....
there's also an elegant way to check for upgrades in background and then silently upgrade app when user clicks an internal link. This method is presented on ach.codesand discussed on this threadas well
还有一种优雅的方法可以在后台检查升级,然后在用户单击内部链接时静默升级应用程序。该方法呈现在ach.codes和讨论在此线程以及
回答by Yan Takushevich
You can send app's version with every response from any endpoint of your API. So that when the app makes any API request you can easily check there's a new version and you need a hard reload. If the version in the API response is newer than the one stored in localStorage, set window.updateRequired = true
. And you can have the following react component that wraps react-router
's Link
:
您可以将应用程序的版本与来自 API 任何端点的每个响应一起发送。因此,当应用程序发出任何 API 请求时,您可以轻松检查是否有新版本,并且您需要重新加载。如果 API 响应中的版本比 localStorage 中存储的版本新,则设置window.updateRequired = true
. 您可以拥有以下包装react-router
's 的反应组件Link
:
import React from 'react';
import { Link, browserHistory } from 'react-router';
const CustomLink = ({ to, onClick, ...otherProps }) => (
<Link
to={to}
onClick={e => {
e.preventDefault();
if (window.updateRequired) return (window.location = to);
return browserHistory.push(to);
}}
{...otherProps}
/>
);
export default CustomLink;
And use it instead of react-router's Link
throughout the app. So whenever there's an update and the user navigates to another page, there will be a hard reload and the user will get the latest version of the app.
并Link
在整个应用程序中使用它而不是 react-router 。因此,每当有更新并且用户导航到另一个页面时,都会进行硬重新加载,用户将获得最新版本的应用程序。
Also you can show a popup saying: "There's an update, click [here] to enable it." if you have only one page or your users navigate very rarely. Or just reload the app without asking. It depends on you app and users.
您还可以显示一个弹出窗口:“有更新,单击 [此处] 以启用它。” 如果您只有一页或您的用户很少浏览。或者无需询问即可重新加载应用程序。这取决于您的应用程序和用户。
回答by magic
I know this is an old thread, and service workers are probably the best answer. But I have a simple approach that appears to work:
我知道这是一个旧线程,服务工作者可能是最好的答案。但我有一个简单的方法似乎有效:
I added a meta tag to my "index.html" file :
我在“index.html”文件中添加了一个元标记:
<meta name="version" content="0.0.3"/>
I then have a very simple php scrip in the same folder as the index.html that responds to a simple REST request. The PHP script parses the server copy of the index.html file, extracts the version number and returns it. In my SPA code, every time a new page is rendered I make an ajax call to the PHP script, extract the version from the local meta tag and compare the two. If different I trigger an alert to the user.
然后,我在与 index.html 相同的文件夹中有一个非常简单的 php 脚本,用于响应简单的 REST 请求。PHP 脚本解析 index.html 文件的服务器副本,提取版本号并返回它。在我的 SPA 代码中,每次呈现新页面时,我都会对 PHP 脚本进行 ajax 调用,从本地元标记中提取版本并比较两者。如果不同,我会向用户发出警报。
PHP script:
PHP脚本:
<?php
include_once('simplehtmldom_1_9/simple_html_dom.php');
header("Content-Type:application/json");
/*
blantly stolen from: https://shareurcodes.com/blog/creating%20a%20simple%20rest%20api%20in%20php
*/
if(!empty($_GET['name']))
{
$name=$_GET['name'];
$price = get_meta($name);
if(empty($price))
{
response(200,"META Not Found",NULL);
}
else
{
response(200,"META Found",$price);
}
}
else
{
response(400,"Invalid Request",NULL);
}
function response($status,$status_message,$data)
{
header("HTTP/1.1 ".$status);
$response['status']=$status;
$response['status_message']=$status_message;
$response['content']=$data;
$json_response = json_encode($response);
echo $json_response;
}
function get_meta($name)
{
$html = file_get_html('index.html');
foreach($html->find('meta') as $e){
if ( $e->name == $name){
return $e->content ;
}
}
}
回答by Kishore Barik
Yes in server side rendering if you need to update a small part of the page also you need to reload whole page. But in SPAs you update your stuffs using ajax, hence no need to reload the page. Seeing your problem I have some assumptions:
在服务器端渲染中是的,如果您需要更新页面的一小部分,还需要重新加载整个页面。但是在 SPA 中,您使用 ajax 更新您的内容,因此无需重新加载页面。看到你的问题,我有一些假设:
You see one of your component got updated but other components getting data from same API didn't update. Here comes Flux Architecture. where you have your data in store and your component listen to store's changes, whenever data in your store changes all your components listening to it's change will be updated (no scene of caching).
您会看到其中一个组件已更新,但从同一 API 获取数据的其他组件未更新。这里谈到通量架构。您将数据存储在存储中并且您的组件侦听存储的更改,每当存储中的数据发生更改时,所有侦听其更改的组件都将更新(没有缓存场景)。
Or
或者
You need to control your component to be updated automatically. For that
您需要控制组件自动更新。为了那个原因
- You can request your server for data in specific intervals
- Websocketscan help you updating component data from server.
- 您可以按特定时间间隔向您的服务器请求数据
- Websockets可以帮助您从服务器更新组件数据。