了解如何向 Expo 网站添加渐进式 Web 应用支持。
渐进式网络应用(简称 PWA)是一个可以安装在用户设备上并离线使用的网站。我们建议尽可能构建原生应用,因为它们具有最好的离线支持,但 PWA 对于桌面用户来说是一个不错的选择。
¥A progressive web app (or PWA for short) is a website that can be installed on the user's device and used offline. We recommend building native apps whenever possible as they have the best offline support, but PWAs are a great option for desktop users.
¥Favicons
Expo CLI 根据 app.json 中的 web.favicon
字段自动生成 favicon.ico 文件。
¥Expo CLI automatically generates the favicon.ico file based on the web.favicon
field in the app.json.
{
"web": {
"favicon": "./assets/favicon.png"
}
}
或者,你可以在 public 目录中创建 favicon.ico 文件来手动指定图标。
¥Alternatively, you can create a favicon.ico file in the public directory to manually specify the icon.
¥Manifest file
PWA 可以是 使用清单文件配置,描述应用的名称、图标和其他元数据。
¥PWAs can be configured with a manifest file that describes the app's name, icon, and other metadata.
1
在 public/manifest.json 中创建 PWA 清单:
¥Create a PWA manifest in public/manifest.json:
{
"short_name": "Expo App",
"name": "Expo Router Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
2
文件 logo192.png 和 logo512.png 是在用户设备上安装应用时将使用的图标。这些也应该添加到公共目录中。
¥The files logo192.png and logo512.png are the icons that will be used when the app is installed on the user's device. These should be added to the public directory too.
public
manifest.json
PWA Manifest
logo192.png
192x192 icon
logo512.png
512x512 icon
3
现在链接 HTML 文件中的清单。这里的方法取决于你网站的输出模式(app.json 中的 web.output
表示 - 默认为 single
)。
¥Now link the manifest in your HTML file. The method here depends on the output mode of your website (indicated in web.output
in the app.json––defaults to single
).
如果你使用的是单页应用,则可以通过首先在 public/index.html 中创建模板 HTML 来链接 HTML 文件中的清单:
¥If you're using a single-page app, you can link the manifest in your HTML file by first creating a template HTML in public/index.html:
-
npx expo customize public/index.html
然后将清单添加到 <head>
标记:
¥Then add the manifest to the <head>
tag:
<link rel="manifest" href="/manifest.json" />
如果你使用静态或服务器渲染,则可以在 app/+html.tsx 中动态创建 HTML 条目。在这里,我们将通过将 <link>
标签添加到 <head>
组件来链接清单:
¥If you're using static or server rendering, the HTML entry can be dynamically created in app/+html.tsx. Here we'll link the manifest by adding a <link>
tag to the <head>
component:
import { ScrollViewStyleReset } from 'expo-router/html';
import type { PropsWithChildren } from 'react';
// This file is web-only and used to configure the root HTML for every
// web page during static rendering.
// The contents of this function only run in Node.js environments and
// do not have access to the DOM or browser APIs.
export default function Root({ children }: PropsWithChildren) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
{/* Link the PWA manifest file. */}
<link rel="manifest" href="/manifest.json" />
{/*
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
*/}
<ScrollViewStyleReset />
{/* Add any additional <head> elements that you want globally available on web... */}
</head>
<body>{children}</body>
</html>
);
}
¥Service workers
Service Worker 主要用于为网站添加离线支持。Google 的 Workbox 是将 Service Worker 添加到网站的最佳方式。请遵循 使用 Workbox CLI 的指南,只要它引用 "构建脚本",请使用 npx expo export -p web
。
¥Service workers are primarily used to add offline support to websites. Google's Workbox is the best way to add service workers to a website. Follow the guide for using Workbox CLI, and wherever it refers to a "build script" use npx expo export -p web
instead.
警告:添加 Service Worker 时要小心,因为众所周知,它们会在网络上导致意外行为。如果你不小心发布了积极缓存你网站的 Service Worker,用户将无法轻松请求更新。为了获得最佳的离线移动体验,请使用 Expo 创建原生应用。与具有 Service Worker 的网站不同,原生应用可以通过应用商店进行更新,以清除缓存的体验。这类似于重置用户的原生浏览器(如果服务工作线程足够积极,他们可能必须这样做)。请参阅 为什么服务工作进程的表现不佳 了解更多信息。
¥warning: Be careful adding service workers as they are known to cause unexpected behavior on web. If you accidentally ship a service worker that aggressively caches your website, users cannot request updates easily. For the best offline mobile experience, create a native app with Expo. Unlike websites with service workers, native apps can be updated through the app store to clear the cached experience. This would be similar to resetting the user's native browser (which they may have to do if the service worker is aggressive enough). See why service workers are suboptimal for more information.
例如,以下是设置 Workbox 的可能流程:
¥For example, here's a possible flow for setting up Workbox:
1
使用以下命令创建一个新项目:
¥Create a new project with the following command:
-
npm create expo -t tabs my-app
-
cd my-app
2
现在在 HTML 文件中注册 Service Worker。这里的方法取决于你网站的输出模式(app.json 中的 web.output
表示 - 默认为 single
)。
¥Now register the service worker in the HTML file. The method here depends on the output mode of your website (indicated in web.output
in the app.json––defaults to single
).
接下来将 Service Worker 注册脚本添加到根 index.html。
¥Next add a service worker registration script to the root index.html.
首先在 public/index.html 中创建一个 HTML 模板(如果尚不存在):
¥First create a template HTML in public/index.html if one does not already exist:
-
npx expo customize public/index.html
然后在 <head>
标签中创建 Service Worker 注册脚本:
¥Then create the service worker registration script in the <head>
tag:
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('/sw.js')
.then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch(error => {
console.error('Service Worker registration failed:', error);
});
});
}
</script>
接下来,为应用创建一个根 HTML 文件并添加 Service Worker 注册脚本:
¥Next, create a root HTML file for the app and add the service worker registration script:
import { ScrollViewStyleReset } from 'expo-router/html';
import type { PropsWithChildren } from 'react';
// This file is web-only and used to configure the root HTML for every
// web page during static rendering.
// The contents of this function only run in Node.js environments and
// do not have access to the DOM or browser APIs.
export default function Root({ children }: PropsWithChildren) {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
{/* Bootstrap the service worker. */}
<script dangerouslySetInnerHTML={{ __html: sw }} />
{/*
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
*/}
<ScrollViewStyleReset />
{/* Add any additional <head> elements that you want globally available on web... */}
</head>
<body>{children}</body>
</html>
);
}
const sw = `
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').then(registration => {
console.log('Service Worker registered with scope:', registration.scope);
}).catch(error => {
console.error('Service Worker registration failed:', error);
});
});
}
`;
3
现在,在运行向导之前构建网站:
¥Now build the website before running the wizard:
-
npx expo export -p web
4
运行向导命令,选择 dist
作为应用的根目录,并选择其他所有内容的默认值...
¥Run the wizard command, selecting dist
as the root of the app, and the defaults for everything else...
-
npx workbox-cli wizard
? What is the root of your web app (that is which directory do you deploy)? dist/
? Which file types would you like to precache? js, html, ttf, ico, json
? Where would you like your service worker file to be saved? dist/sw.js
? Where would you like to save these configuration options? workbox-config.js
? Does your web app manifest include search parameter(s) in the 'start_url', other than 'utm_' or 'fbclid' (like '?source=pwa')? No
5
最后,运行 npx workbox-cli generateSW workbox-config.js
生成 Service Worker 配置。
¥Finally, run npx workbox-cli generateSW workbox-config.js
to generate the service worker config.
接下来,你可以在 package.json 中添加构建脚本,以按正确的顺序运行这两个脚本:
¥Going forward, you can add a build script in package.json to run both scripts in the correct order:
{
"scripts": {
"build:web": "expo export -p web && npx workbox-cli generateSW workbox-config.js"
}
}
6
如果你托管网站并使用 Chrome 进行访问,则可以通过转到 Chrome 开发工具中的应用 > Service Workers 来检查 Service Worker。
¥If you host your website and visit with Chrome, you can inspect the service worker by going to Application > Service Workers in the Chrome dev tools.