检查用户是否已在 Chrome 上将 PWA 安装到主屏幕?
check if user has already installed PWA to homescreen on Chrome?
我正在尝试在我的渐进式网络应用程序上创建一个 "Add To Home Screen" 按钮,如 Chrome's documentation 中所述。
我通常遵循规定的模式,我有一些隐藏按钮,当 Chrome 的 beforeinstallprompt
事件触发时显示。
一旦事件触发,我就捕获该事件,然后在单击我自己的安装按钮后使用该事件开始本机安装对话。示例代码如下:
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
});
btnAdd.addEventListener('click', (e) => {
// hide our user interface that shows our A2HS button
btnAdd.style.display = 'none';
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
} else {
console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
});
我 运行 遇到的问题是,如果用户已经在他们的主屏幕上安装了网络应用程序,我不想显示我的安装按钮 (btnAdd
),并且我无法弄清楚如何检查这种情况。
本来希望把上面的代码修改成如下:
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// If the user has not already installed...
deferredPrompt.userChoice
.then(choiceResult => {
if (choiceResult === undefined) {
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
}
});
});
如果用户已经安装,则不会显示安装按钮。但这似乎不起作用。看来,如果他们还没有做出选择,访问 userChoice
只会直接用本机对话提示用户。
我不太确定 beforeinstallevent
是如何工作的,所以这甚至可能不是一个好的策略。理想情况下,我希望这会像 navigator.serviceWorker.ready()
那样工作,其中 returns 是一个 Promise,而不是使用浏览器事件来尝试弄清楚什么时候准备好了。
无论如何,在显示我自己的主屏幕安装按钮之前,是否有关于如何检查用户是否已安装到主屏幕的想法?
编辑: 正如 Mathias 评论的那样,在显示按钮之前检查事件就足够了。我相信我遇到的问题是使用 localhost 的结果,它似乎在安装后不断触发 beforeinstallprompt
事件,这不是预期的行为。托管代码解决了这个问题。
也许,在拦截自动弹出窗口之前不要显示按钮?
或
在您的代码中,检查 window 是否独立
如果是,则无需显示按钮
if (window.matchMedia('(display-mode: standalone)').matches) {
// do things here
// set a variable to be used when calling something
// e.g. call Google Analytics to track standalone use
}
这里是我的示例测试人员
https://a2hs.glitch.me
HTML
<!-- start with hidden button -->
<button id="install" style="display:none;">install</button>
JAVASCRIPT
// variable store event
window.deferredPrompt = {};
// get button with id
const install_button = document.querySelector('#install');
// if the app can be installed emit beforeinstallprompt
window.addEventListener('beforeinstallprompt', e => {
// this event does not fire if the application is already installed
// then your button still hidden ;)
// show button with display:block;
install_button.style.display = 'block';
// prevent default event
e.preventDefault();
// store install avaliable event
window.deferredPrompt = e;
// wait for click install button by user
install_button.addEventListener('click', e => {
window.deferredPrompt.prompt();
window.deferredPrompt.userChoice.then(choiceResult => {
if (choiceResult.outcome === 'accepted') {
// user accept the prompt
// lets hidden button
install_button.style.display = 'none';
} else {
console.log('User dismissed the prompt');
}
window.deferredPrompt = null;
});
});
});
// if are standalone android OR safari
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
// hidden the button
install_button.style.display = 'none';
}
// do action when finished install
window.addEventListener('appinstalled', e => {
console.log("success app install!");
});
我不明白这怎么是正确答案,因为这基本上是检查用户是否已经使用该应用程序,但我们不想要的行为是 "When the user is on the web and tries to install the app again to tell him that he already has the app in his device"。在我看来,这不是解决这个问题的答案。
我们能做的是:
1. 当用户点击安装但他的设备上有应用程序时
在这种情况下,beforeinstallprompt
事件 不会被 触发,因此该事件将 return 为空。我们将结果存储在全局变量中,当结果为空时,我们向用户显示他已经安装了该应用程序。
2. 当用户点击安装但他的设备上没有应用程序时
在这种情况下,beforeinstallprompt
事件 将被触发 因此该事件将 return 访问以显示提示。
我们可以将结果存储在全局变量中,如果它不为 NULL(不会为 NULL),因为如果用户的设备上没有应用程序, beforeinstallprompt
将被触发,我们将 prompt() 显示给用户。
我怀疑我的解决方案是否也很好,但我认为问题和正确答案没有任何共同点
window.addEventListener("beforeinstallprompt", event => {
window.deferedPrompt = event;
});
handleButtonClick = () => {
const promptEvent = window.deferedPrompt;
if(!promptEvent){
// DO SOMETHING
}
//Show the add to home screen prompt
promptEvent.prompt()
promptEvent.userChoice.then((result: any) => {
// Reset the deferred prompt variable, since
// prompt() can only be called once.
window.deferedPrompt = null;.
});
}
<button onClick={handleButtonClick}>Install</button>
回答原问题。对于 Chrome 的最新版本,您可以使用 window.navigator.getInstalledRelatedApps()
。它 returns 一个承诺,其中包含一系列已安装的应用程序,您的网络应用程序在 manifest.json 中将其指定为相关。要启用此功能,您需要将 related_applications
字段添加到 manifest.json
"related_applications": [{
"platform": "webapp",
"url": "https://app.example.com/manifest.json"
}]
然后你可以像这样使用它:
//check if browser version supports the api
if ('getInstalledRelatedApps' in window.navigator) {
const relatedApps = await navigator.getInstalledRelatedApps();
relatedApps.forEach((app) => {
//if your PWA exists in the array it is installed
console.log(app.platform, app.url);
});
}
来源:API docs
现在您可以显示一些元素,具体取决于您的应用程序是否已安装。例如:您可以显示“打开应用程序”按钮并将用户重定向到 PWA。但是请记住,当用户已经在使用@Mathias 的回答并检查 (display-mode: standalone)
的应用程序中时禁用它
但是,关于您的用例。只有当 beforeinstallprompt
被拦截时,您才应该显示安装按钮。如果 PWA 已安装在设备上,浏览器不会触发此事件。当提示被触发并且 choiceResult.outcome === 'accepted'
你再次隐藏按钮。
这是告诉您的简单功能,此应用程序在浏览器或 pwa 中打开。
原始来源 link
function getPWADisplayMode() {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
if (document.referrer.startsWith('android-app://')) {
return 'twa';
} else if (navigator.standalone || isStandalone) {
return 'standalone';
}
return 'browser';
}
我正在尝试在我的渐进式网络应用程序上创建一个 "Add To Home Screen" 按钮,如 Chrome's documentation 中所述。
我通常遵循规定的模式,我有一些隐藏按钮,当 Chrome 的 beforeinstallprompt
事件触发时显示。
一旦事件触发,我就捕获该事件,然后在单击我自己的安装按钮后使用该事件开始本机安装对话。示例代码如下:
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
});
btnAdd.addEventListener('click', (e) => {
// hide our user interface that shows our A2HS button
btnAdd.style.display = 'none';
// Show the prompt
deferredPrompt.prompt();
// Wait for the user to respond to the prompt
deferredPrompt.userChoice
.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
console.log('User accepted the A2HS prompt');
} else {
console.log('User dismissed the A2HS prompt');
}
deferredPrompt = null;
});
});
我 运行 遇到的问题是,如果用户已经在他们的主屏幕上安装了网络应用程序,我不想显示我的安装按钮 (btnAdd
),并且我无法弄清楚如何检查这种情况。
本来希望把上面的代码修改成如下:
window.addEventListener('beforeinstallprompt', (e) => {
// Prevent Chrome 67 and earlier from automatically showing the prompt
e.preventDefault();
// Stash the event so it can be triggered later.
deferredPrompt = e;
// If the user has not already installed...
deferredPrompt.userChoice
.then(choiceResult => {
if (choiceResult === undefined) {
// Update UI notify the user they can add to home screen
btnAdd.style.display = 'block';
}
});
});
如果用户已经安装,则不会显示安装按钮。但这似乎不起作用。看来,如果他们还没有做出选择,访问 userChoice
只会直接用本机对话提示用户。
我不太确定 beforeinstallevent
是如何工作的,所以这甚至可能不是一个好的策略。理想情况下,我希望这会像 navigator.serviceWorker.ready()
那样工作,其中 returns 是一个 Promise,而不是使用浏览器事件来尝试弄清楚什么时候准备好了。
无论如何,在显示我自己的主屏幕安装按钮之前,是否有关于如何检查用户是否已安装到主屏幕的想法?
编辑: 正如 Mathias 评论的那样,在显示按钮之前检查事件就足够了。我相信我遇到的问题是使用 localhost 的结果,它似乎在安装后不断触发 beforeinstallprompt
事件,这不是预期的行为。托管代码解决了这个问题。
也许,在拦截自动弹出窗口之前不要显示按钮?
或
在您的代码中,检查 window 是否独立
如果是,则无需显示按钮
if (window.matchMedia('(display-mode: standalone)').matches) {
// do things here
// set a variable to be used when calling something
// e.g. call Google Analytics to track standalone use
}
这里是我的示例测试人员
https://a2hs.glitch.me
HTML
<!-- start with hidden button -->
<button id="install" style="display:none;">install</button>
JAVASCRIPT
// variable store event
window.deferredPrompt = {};
// get button with id
const install_button = document.querySelector('#install');
// if the app can be installed emit beforeinstallprompt
window.addEventListener('beforeinstallprompt', e => {
// this event does not fire if the application is already installed
// then your button still hidden ;)
// show button with display:block;
install_button.style.display = 'block';
// prevent default event
e.preventDefault();
// store install avaliable event
window.deferredPrompt = e;
// wait for click install button by user
install_button.addEventListener('click', e => {
window.deferredPrompt.prompt();
window.deferredPrompt.userChoice.then(choiceResult => {
if (choiceResult.outcome === 'accepted') {
// user accept the prompt
// lets hidden button
install_button.style.display = 'none';
} else {
console.log('User dismissed the prompt');
}
window.deferredPrompt = null;
});
});
});
// if are standalone android OR safari
if (window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone === true) {
// hidden the button
install_button.style.display = 'none';
}
// do action when finished install
window.addEventListener('appinstalled', e => {
console.log("success app install!");
});
我不明白这怎么是正确答案,因为这基本上是检查用户是否已经使用该应用程序,但我们不想要的行为是 "When the user is on the web and tries to install the app again to tell him that he already has the app in his device"。在我看来,这不是解决这个问题的答案。
我们能做的是:
1. 当用户点击安装但他的设备上有应用程序时
在这种情况下,beforeinstallprompt
事件 不会被 触发,因此该事件将 return 为空。我们将结果存储在全局变量中,当结果为空时,我们向用户显示他已经安装了该应用程序。
2. 当用户点击安装但他的设备上没有应用程序时
在这种情况下,beforeinstallprompt
事件 将被触发 因此该事件将 return 访问以显示提示。
我们可以将结果存储在全局变量中,如果它不为 NULL(不会为 NULL),因为如果用户的设备上没有应用程序, beforeinstallprompt
将被触发,我们将 prompt() 显示给用户。
我怀疑我的解决方案是否也很好,但我认为问题和正确答案没有任何共同点
window.addEventListener("beforeinstallprompt", event => {
window.deferedPrompt = event;
});
handleButtonClick = () => {
const promptEvent = window.deferedPrompt;
if(!promptEvent){
// DO SOMETHING
}
//Show the add to home screen prompt
promptEvent.prompt()
promptEvent.userChoice.then((result: any) => {
// Reset the deferred prompt variable, since
// prompt() can only be called once.
window.deferedPrompt = null;.
});
}
<button onClick={handleButtonClick}>Install</button>
回答原问题。对于 Chrome 的最新版本,您可以使用 window.navigator.getInstalledRelatedApps()
。它 returns 一个承诺,其中包含一系列已安装的应用程序,您的网络应用程序在 manifest.json 中将其指定为相关。要启用此功能,您需要将 related_applications
字段添加到 manifest.json
"related_applications": [{
"platform": "webapp",
"url": "https://app.example.com/manifest.json"
}]
然后你可以像这样使用它:
//check if browser version supports the api
if ('getInstalledRelatedApps' in window.navigator) {
const relatedApps = await navigator.getInstalledRelatedApps();
relatedApps.forEach((app) => {
//if your PWA exists in the array it is installed
console.log(app.platform, app.url);
});
}
来源:API docs
现在您可以显示一些元素,具体取决于您的应用程序是否已安装。例如:您可以显示“打开应用程序”按钮并将用户重定向到 PWA。但是请记住,当用户已经在使用@Mathias 的回答并检查 (display-mode: standalone)
但是,关于您的用例。只有当 beforeinstallprompt
被拦截时,您才应该显示安装按钮。如果 PWA 已安装在设备上,浏览器不会触发此事件。当提示被触发并且 choiceResult.outcome === 'accepted'
你再次隐藏按钮。
这是告诉您的简单功能,此应用程序在浏览器或 pwa 中打开。 原始来源 link
function getPWADisplayMode() {
const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
if (document.referrer.startsWith('android-app://')) {
return 'twa';
} else if (navigator.standalone || isStandalone) {
return 'standalone';
}
return 'browser';
}