我正在尝试在渐进式Web应用程序上创建“添加到主屏幕”按钮,如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;
});
});
我遇到的问题是,如果用户已经将Web应用程序安装到其主屏幕上,并且我无法确定自己的安装按钮(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()
这样工作,它返回一个Promise而不是使用浏览器事件来尝试确定何时准备就绪。
在任何情况下,在显示自己的主屏幕安装按钮之前,是否有任何想法可以检查用户是否已安装到主屏幕?
编辑:正如Mathias所说,在显示按钮之前检查事件就足够了。我认为我遇到的问题是使用本地主机的结果,该本地主机甚至在安装后似乎仍不断触发beforeinstallprompt
事件,这不是预期的行为。托管代码解决了该问题。
答案 0 :(得分:8)
回答原始问题。在最新版本的Chrome中,您可以使用window.navigator.getInstalledRelatedApps()
。它返回一个承诺,其中包含您的Web应用在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'
时,您再次隐藏了按钮。
答案 1 :(得分:6)
也许在拦截自动弹出窗口之前不显示按钮?
或
在代码中,检查窗口是否为独立窗口
如果是这样,则无需显示按钮
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
答案 2 :(得分:3)
<!-- start with hidden button -->
<button id="install" style="display:none;">install</button>
// 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!");
});
答案 3 :(得分:2)
我看不出这是正确的答案,因为这基本上是在检查用户是否已经在使用该应用程序,但是我们要避免的行为是“当用户在网络上并尝试安装该应用程序时再次告诉他他已经在自己的设备中安装了该应用”。对我来说,这不是解决这个问题的答案。
我们可以做的是:
1.当用户单击安装但设备上已安装该应用程序时
在这种情况下,将触发beforeinstallprompt
事件不会,因此该事件将返回null。我们将结果存储在全局变量中,当结果为null时,我们向用户显示他已经安装了该应用程序。
2.当用户单击安装但在其设备上没有该应用程序时
在这种情况下,将触发beforeinstallprompt
事件 ,因此该事件将返回访问权限以显示提示。
我们可以将结果存储在全局变量中,如果它不是NULL(不会),因为如果用户在设备上没有该应用,则会触发beforeinstallprompt
,因此我们向用户。
我怀疑我的解决方案是否也很好,但我认为问题和正确答案没有共同之处
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>
答案 4 :(得分:0)
这里有一个简单的功能告诉你,这个应用程序是在浏览器中打开还是在 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';
}
答案 5 :(得分:-3)
我正在寻找类似的东西,简单的解决方案是在manifest.web
清单文件(将manifest.json
重命名为manifest.webm
)中传递的范围,
我们需要从清单文件中删除作用域,现在,如果我们已将PWA添加到主屏幕,并且有人单击默认情况下打开的PWA触发器之外的链接。
网站:CSRF