运行时修改 Bootstrap 页面
应用程序 shell 模型 旨在通过优先加载网页的_重要_部分来加快网站访问速度,从而提升用户体验。关键点在于首次访问时交付显示用户界面所需的最小 HTML、CSS 和 JavaScript,并将其缓存以便后续访问时使用。
Vaadin Flow 中的 应用程序 Shell 也被称为 Bootstrap 页面,或 index.html。
修改应用程序 Shell
开发人员可以完全控制 index.html 的内容。可以通过多种方式进行修改。
在客户端,当内容为静态内容(如 <viewport> 标签)时,您可以编辑 frontend/index.html。在服务端,您可以进行需要动态服务端内容的更改,或者当更倾向于使用 Java 语法时。例如,可以通过启用 @PWA 内置功能,使应用程序支持安装。
对于 AppShellSettings API 所覆盖的情况,可以实现 AppShellConfigurator,或者通过注解的方式实现。对于需要高级修改文档结构的场景,可以配置 IndexHtmlRequestListener。
应用程序 Shell 模板
Vaadin servlet 使用 frontend/index.html 文件作为模板来生成 bootstrap 页面响应。Servlet 会处理模板,并注入以下信息:
-
<base href='./relative/to/root'>:Vaadin 根据当前请求路径计算到应用根路径的相对路径。这样可以确保视图模板中的相对链接能够正确运行。 -
打包后的脚本:Vaadin 会自动添加由
frontend/index.ts文件生成的打包优化脚本。它使用内置的 Vite 实例,作为模块打包工具,Vite 已包含在vaadin-maven-plugin中。因此,frontend/index.html模板无需显式包含index.ts脚本,或者index.js脚本。
|
Tip
|
frontend 目录路径可以通过在执行 vaadin-maven-plugin 的 Maven 目标 prepare-frontend 或 build-frontend 时,提供属性 vaadin.frontend.frontend.folder 来更改。
|
默认模板与入口点
如果 frontend 文件夹下缺少 index.html 或 index.ts 文件,vaadin-maven-plugin 会在 target 文件夹中自动生成对应的默认文件。在此情况下,应用程序仅使用服务端路由。您可以通过将这些文件移入 frontend 文件夹中来对其进行控制。默认情况下,这些文件类似如下:
Source code
默认 index.html
index.html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body, #outlet {
height: 100vh;
width: 100vw;
margin: 0;
}
</style>
<!-- index.ts is included here automatically (either by the dev server or during the build) -->
</head>
<body>
<!-- vaadin-router in index.ts needs an outlet for displaying the views -->
<div id="outlet"></div>
</body>
</html>Source code
// import Vaadin client-router to handle client-side and server-side navigation
import { Router } from '@vaadin/router';
// import Flow module to enable navigation to Vaadin server-side views
import { Flow } from '@vaadin/flow-frontend';
const {serverSideRoutes} = new Flow({
imports: () => import('../target/frontend/generated-flow-imports')
});
const routes = [
// for client-side, place routes below
// for server-side, the next magic line sends all unmatched routes:
...serverSideRoutes // IMPORTANT: this must be the last entry in the array
];
// The Vaadin router needs an outlet in the index.html page to display views
export const router = new Router(document.querySelector('#outlet'));
router.setRoutes(routes);这里是自定义应用程序 shell 的最佳位置,例如添加统计分析标签。
Source code
index.html
...
<head>
<title>My App</title>
</head>
<body>
...
<!-- Google Analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','http://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXX-Y', 'auto');
ga('send', 'my-app-bootstrap');
</script>
</body>运行时自定义应用程序 Shell
您可以使用几种方法在运行时自定义应用程序 shell。它们会在以下各小节中介绍。
AppShellConfigurator 接口
在 Java 代码中,如需在引导过程中添加动态内容,应该使用 AppShellConfigurator 标记接口,而不是直接编辑 index.html 文件。
整个 Vaadin 应用必须只有一个应用程序 shell,因此不能有多个类实现 AppShellConfigurator。在多模块应用中,实现 AppShellConfigurator 的类不能放在启动该应用的 JAR 或 WAR 文件所依赖的 JAR 文件内。
|
Note
|
AppShellConfigurator 替代了已废弃的 PageConfigurator 接口。
|
AppShellConfigurator.configurePage() 方法
通过重写 configurePage() 方法,并通过调用以下 AppShellSettings 方法,可以向 index.html 模板添加内容:
-
AppShellSettings.setViewport()设置 viewport 值。将替换index.html模板中的 viewport。 -
AppShellSettings.setPageTitle()设置初始页面标题。将替换模板中的 title 标签。 -
AppShellSettings.setBodySize()配置 body 的宽度和高度。 -
AppShellSettings.addMetaTag()向 head 添加 Meta 标签。 -
AppShellSettings.addInlineFromFile()包含资源文件内容。 -
AppShellSettings.addInlineWithContents()添加任意内容。 -
AppShellSettings.addLink()向 head 部分添加 link。 -
AppShellSettings.addFavIcon()配置 favicon。 -
AppShellSettings.getLoadingIndicatorConfiguration()在使用传统引导方式时配置加载指示器。此方法已弃用:详见代码示例后的说明。 -
AppShellSettings.getReconnectDialogConfiguration()在使用传统引导方式时配置重连对话框。此方法已弃用:详见代码示例后的说明。 -
AppShellSettings.getPushConfiguration()在使用传统引导方式时自定义推送机制。此方法已弃用:详见代码示例后的说明。
Source code
Java
public class AppShell implements AppShellConfigurator {
@Override
public void configurePage(AppShellSettings settings) {
settings.setViewport("width=device-width, initial-scale=1");
settings.setPageTitle("A Cool Vaadin App");
settings.setBodySize("100vw", "100vh");
settings.addMetaTag("author", "bunny");
settings.addFavIcon("icon", "icons/icon-192.png", "192x192");
settings.addLink("shortcut icon", "icons/favicon.ico");
settings.addInlineFromFile(
TargetElement.BODY,
Position.APPEND,
"custom.html",
Wrapping.AUTOMATIC);
settings.addInlineWithContents(Position.PREPEND,
"console.log(\"foo\");", Wrapping.JAVASCRIPT);
}
}Source code
ServiceListener.java
Java 注解
Vaadin 提供了一组注解来修改应用程序 shell。
-
@Viewport用于设置 viewport 值。 -
@PageTitle用于设置初始页面标题。 -
@BodySize用于配置 body 大小。 -
@Meta用于向 head 添加 meta 标签。 -
@Inline用于将资源文件内容包含到index.html中。 -
@PWA用于定义应用程序的 PWA 属性。 -
@Push用于配置服务端推送。
Source code
Java
@Viewport("width=device-width, initial-scale=1")
@PageTitle("A Cool Vaadin App")
@BodySize(height = "100vh", width = "100vw")
@Meta(name = "author", content = "bunny")
@Inline(wrapping = Wrapping.AUTOMATIC,
position = Position.APPEND,
target = TargetElement.BODY,
value = "custom.html")
@PWA(name = "Cool Vaadin App", shortName = "my-app")
@Push(value = PushMode.MANUAL, transport = Transport.WEBSOCKET)
public class AppShell implements AppShellConfigurator {
}在 AppShellConfigurator.configurePage() 中的修改优先于等效注解。注解不能涵盖通过重写 AppShellConfigurator.configurePage() 方法实现的所有场景。
服务端无效响应后强制页面重载
如果 XHR 响应无法解析为 JSON,Vaadin 会在响应文本的任意位置查找字符串 “Vaadin-Refresh”。如果找到了,Vaadin 会重载页面,而不是显示错误信息。通常这类响应由第三方服务器返回,此时您需要在该服务器返回的 HTML 页面的 Meta 标签中添加刷新令牌。
带 Meta 标签的 HTML 响应示例
Source code
XML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="refresh" content="Vaadin-Refresh">
<title>Vaadin App</title>
</head>
<body>
<h1>Invalid server response</h1>
</body>
</html>但有时候由于代理或防火墙超时,客户端会被分配一个新会话,而客户端对此并不知情。这种情况下,您需要在 Vaadin bootstrap 页面中添加刷新令牌。
Source code
Java
public class AppShell implements AppShellConfigurator {
@Override
public void configurePage(AppShellSettings settings) {
settings.addMetaTag("refresh", "Vaadin-Refresh");
}
}IndexHtmlRequestListener 接口
针对前文未覆盖的高级场景,可以通过 IndexHtmlRequestListener 修改内容。
实现的监听器应在 VaadinService 初始化时,通过 ServiceInitEvent 添加。关于如何使用 Vaadin ServiceInitListeners 的详细说明,请参见 ServiceInitListener 教程。
以下示例可动态更改 body 的 class:
Source code
Java
public class MyIndexHtmlRequestListener implements
IndexHtmlRequestListener {
@Override
public void modifyIndexHtmlResponse(
IndexHtmlResponse indexHtmlResponse) {
Document document = indexHtmlResponse.getDocument();
Element body = document.body();
body.classNames(computeBodyClassNames());
}
private Set<String> computeBodyClassNames() {
// Introduce some logic to dynamically change the body class
return Collections.singleton("my-className");
}
}这也可以通过名为 useDeprecatedV14Bootstrapping 的 servlet 容器部署属性实现。请注意,只有在前端构建工具使用 webpack 时才支持该选项,若应用使用 Vite(默认)则不支持。可通过对应的 feature flag 启用 webpack。详细了解启用办法请查阅 Hot Deploy & Live Reload 文档页面。
38A2B3F1-CC6B-45DF-8CB8-9DEF23BA53B0