检查用户是不是已经在 Chrome 的主屏幕上安装了 PWA?

Posted

技术标签:

【中文标题】检查用户是不是已经在 Chrome 的主屏幕上安装了 PWA?【英文标题】:check if user has already installed PWA to homescreen on Chrome?检查用户是否已经在 Chrome 的主屏幕上安装了 PWA? 【发布时间】:2019-01-15 01:55:40 【问题描述】:

我正在尝试在我的渐进式网络应用程序上创建一个“添加到主屏幕”按钮,如 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() 那样工作,它返回一个 Promise,而不是使用浏览器事件来尝试找出什么时候准备好。

无论如何,在我显示自己的主屏幕安装按钮之前,我有什么想法可以检查用户是否已安装到主屏幕?

编辑: 正如 Mathias 所说,在显示按钮之前检查事件就足够了。我相信我遇到的问题是使用 localhost 的结果,即使在安装之后,它似乎也会不断触发 beforeinstallprompt 事件,这不是预期的行为。托管代码解决了这个问题。

【问题讨论】:

等等。我刚刚重新阅读了您的问题。拦截提示后,您似乎已经只显示按钮,对吗?安装后,您应该不会再收到来自 Chrome 的拦截提示? 我相信这是正确的。我想我只是没有完全理解 API,我在 localhost 上进行测试,由于某种原因,即使在安装了应用程序之后,它似乎也会不断地触发 beforeinstallprompt 事件。一旦我托管了我的代码,beforeinstallprompt 在安装后停止触发,这解决了我的问题。 【参考方案1】:

也许,在拦截自动弹出窗口之前不显示按钮?

或 在您的代码中,检查窗口是否是独立的 如果是,则无需显示按钮

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

我的测试器的源代码https://github.com/ng-chicago/AddToHomeScreen

【讨论】:

我已经在显示弹出窗口之前检查事件,但我相信这是正确的解决方案。见我上面的评论。检查独立模式绝对是一个方便的技巧,但这意味着安装按钮仍会显示在安装后的浏览器视图(非独立)中。 此代码检测网站是否作为应用程序启动 - 它不检测用户是否安装了应用程序但正在从浏览器查看网站。 @AlexWalker 正确。浏览器中的网页无权查看设备上安装的应用程序。在大多数情况下,从已安装图标安装 PWA 的用户将使用该图标而不是 URL。所以这不是一个经常发生的问题(我认为) @AlexWalker 但是... 您可以检测 beforeinstallprompt 是否触发来告诉您用户是否尚未安装 PWA 并且正在使用浏览器版本。这适用于所有未拒绝​​安装提示的人。 没错,只要他们使用 Chrome 就足够了。【参考方案2】:

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!");
);

【讨论】:

在许多文章和博客中,您会写“完成安装后执行操作”。不幸的是,如果在安装开始时而不是在安装结束时触发,则事件“appinstalled”的名称不正确。【参考方案3】:

我看不出这是正确的答案,因为这基本上是检查用户是否已经使用该应用程序,但我们不希望出现的行为是“当用户在网络上并尝试安装应用程序时再次告诉他他的设备中已经有该应用程序”。在我看来,这不是解决这个问题的答案。

我们能做的是: 1.当用户点击安装但他的设备上有应用程序时 在这种情况下,beforeinstallprompt 事件 不会被 触发,因此该事件将返回 null。我们将结果存储在全局变量中,当结果为空时,我们向用户显示他已经安装了应用程序。 2.当用户点击安装但他的设备上没有应用程序时 在这种情况下,beforeinstallprompt 事件将被触发,因此该事件将返回访问以显示提示。 我们可以将结果存储在全局变量中,如果它不为 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>

【讨论】:

beforeinstallprompt 不适用于所有浏览器,目前仅适用于 chrome。【参考方案4】:

回答原始问题。使用最新版本的 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' 你再次隐藏按钮。

【讨论】:

这应该是公认的答案。写得好!我还尝试在桌面(Ubuntu 20.04 和 Chrome 84)上对其进行测试。我使用 devtools 来模仿移动设备。它会安装应用程序,但 getInstalledRelatedApps 未检测到它已安装。 页面上说它适用于Android:Chrome 84 或更高版本。因此,尽管模仿移动设备,但您的​​ 桌面 Chrome 可能不支持它。 我在桌面版 chrome 上试过你的答案,但没有用 @DLesjak 你能解释一下怎么做吗? “现在您可以根据您的应用是否安装显示一些元素。例如:您可以显示“打开应用”按钮并将用户重定向到 PWA。“ @RahulVyas 您应该检查getIntstalledRelatedApps(),在relatedApps 数组中遇到您的应用程序后,您可以更新您的用户界面并显示按钮。点击按钮后,您可以重定向用户,如window.open(window.location.href, "_blank"); Chrome 将打开应用程序。【参考方案5】:

这是一个简单的功能,告诉你,这个应用程序是在浏览器或 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';

【讨论】:

以上是关于检查用户是不是已经在 Chrome 的主屏幕上安装了 PWA?的主要内容,如果未能解决你的问题,请参考以下文章

即使用户已经登录,也会从 getAuthToken 屏幕登录 Chrome

检查用户是不是安装了 Chrome 扩展程序

如何检查元素是不是在屏幕上完全可见?

IAP 如何检查用户是不是已经试用(过去的任何时间)?

如何在网站上弹出添加到主屏幕 在移动浏览器中打开

使用 Inspect 检查页面时,Chrome 不显示屏幕的宽度和高度