认识 HTML 新提案元素 :让权限请求更讲道理

摘要:是一个正在讨论中的 HTML 新元素。它想把“向用户申请权限”这件事,从 JavaScript 函数调用变成一个页面上真实可见的控件。目前这个提案还处于 WICG 草案阶段,经历过 Chrome 的 Origin Trial 测试,距离成为正式标准还有一段路

它是什么

<permission> 是一个正在讨论中的 HTML 新元素。它想把“向用户申请权限”这件事,从 JavaScript 函数调用变成一个页面上真实可见的控件。目前这个提案还处于 WICG 草案阶段,经历过 Chrome 的 Origin Trial 测试,距离成为正式标准还有一段路,现在还不能在生产环境里直接使用。


它能解决什么问题

以前要申请定位、摄像头、通知这些权限,基本套路是:页面上放一个按钮,用户点一下,背后执行 navigator.permissions 或者直接调用对应的权限接口,浏览器的弹窗就跳出来了。用户常常还没看清弹窗内容,手一抖就点了拒绝。拒绝之后,权限状态变成“已阻止”,网站再想引导用户去设置里改回来,过程非常折腾。

<permission> 的设计思路是:让权限入口变成页面上的一个标签,跟在按钮、输入框一样,用户点它,就是明确想要授权。浏览器能更清楚地知道,这次权限请求确实是用户真实意图触发的。WICG 草案和 Chrome 团队的说明都提到一个关键目标:减少骚扰式弹窗,把权限动作放回到使用场景里。


基础写法

最简的用法看起来就像个普通按钮:

<permission type="camera">打开摄像头</permission>
<permission type="microphone">打开麦克风</permission>

type 属性告诉浏览器要申请哪种权限,浏览器会根据这个值生成对应的授权流程。用户点击这个元素,本质上和以前点按钮触发权限接口类似,区别在于浏览器的处理方式可能更统一、更可预测。


实际项目中的稳妥写法

因为兼容性还没铺开,现在写业务代码,更好的做法是走“渐进增强”路线:支持 <permission> 的浏览器走新方案,不支持的继续用老办法兜底。下面是一个处理定位权限的示例:

<div class="permission-block">
  <!-- 新方案:permission 元素 -->
  <permission id="geoPermission" type="geolocation">
    允许获取当前位置
  </permission>

  <!-- 旧方案兜底按钮,默认隐藏 -->
  <button id="geoFallback" hidden>允许获取当前位置</button>

  <pre id="result"></pre>
</div>
const result = document.querySelector('#result');
const fallbackBtn = document.querySelector('#geoFallback');
const permissionEl = document.querySelector('#geoPermission');

function print(msg) {
  result.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`;
}

function readLocation() {
  navigator.geolocation.getCurrentPosition(
    function (pos) {
      const lat = pos.coords.latitude.toFixed(6);
      const lng = pos.coords.longitude.toFixed(6);
      print('定位成功:' + lat + ', ' + lng);
    },
    function (err) {
      print('定位失败:' + err.code + ' ' + err.message);
    },
    { enableHighAccuracy: true, timeout: 8000 }
  );
}

async function init() {
  try {
    // 先查当前权限状态,不要一上来就申请
    const status = await navigator.permissions.query({ name: 'geolocation' });
    print('当前权限状态:' + status.state);

    // 如果已经授权,直接定位
    if (status.state === 'granted') {
      readLocation();
      return;
    }

    // 检测浏览器是否不支持 permission 元素
    // 如果 permissionEl 不是真实的自定义元素实例,说明当前浏览器不认识它
    if (
      typeof customElements !== 'undefined' &&
      !customElements.get('permission') &&
      permissionEl instanceof HTMLElement
    ) {
      // 隐藏 permission 元素,换旧按钮出场
      permissionEl.hidden = true;
      fallbackBtn.hidden = false;
    }

    // 监听权限状态变化
    status.onchange = function () {
      print('权限状态变更:' + status.state);
      if (status.state === 'granted') {
        readLocation();
      }
    };
  } catch (e) {
    // 如果连 permissions.query 都不支持,直接切旧方案
    print('初始化失败:' + e.message);
    permissionEl.hidden = true;
    fallbackBtn.hidden = false;
  }
}

// 旧方案按钮的点击处理
fallbackBtn.addEventListener('click', function () {
  readLocation();
});

init();

上面这段代码做了几件事:

  1. 页面加载时先用 navigator.permissions.query() 查一下权限状态,而不是直接弹出请求。

  2. 如果权限已经是“已授权”,直接执行定位逻辑。

  3. 检测浏览器是否认识 <permission> 元素,不认识就把新元素藏起来,让兜底的旧按钮显示。

  4. 监听 status.onchange,当用户在别处改了权限后,页面能及时响应。


和 Permissions API 的关系

有一点容易混淆:<permission> 不是一个用来替代 Permissions API 的东西。它们分工不同。

Permissions API 负责让代码查询权限状态、监听变化。<permission> 负责给用户一个看得见、点得到的权限入口。两者是配合关系,不是替代关系。代码里该查状态还是要查,不该一加载页面就傻等着用户授权。


怎么用才真的有用

这个标签最大的价值不在于省掉几行 JS,而在于两件事:

第一,把权限入口嵌进真实的操作流程里。比如“开始导航”旁边放定位授权按钮,“加入会议”旁边放麦克风授权按钮。用户看到的就是“我要用这个功能,所以需要这个权限”,逻辑是通的。

第二,给“被拒绝后的恢复”留了一条更自然的路。以前用户拒绝过一次,权限状态变成“已阻止”,页面再想引导就非常费劲,用户得去浏览器设置里翻半天。Chrome 对这个提案反复提到的一点就是:用户之前拒绝过,页面里也应该有更清晰的方式让用户重新同意或管理权限,不用跑到设置页。


现在要不要用

如果问我现在的态度,我不会建议大家立刻大规模上。更好的做法是挑几个权限敏感、转化容易出问题的页面做小范围试验。把 <permission> 当成一个增强能力,而不是唯一入口。新标签再新,最后也得回到一个旧问题:用户到底知不知道你为什么需要这个权限。这个问题没讲清楚,换什么标签都帮不上忙。


小结

<permission> 是一个值得关注的提案,它代表了一种趋势:浏览器厂商试图把权限请求的控制权更多还给用户,减少出其不意的弹窗。但它目前还是草案,语法细节和兼容性都可能变。如果你对这个方向感兴趣,可以关注 WICG 的官方仓库或 Chrome 平台的更新日志。真正要落到业务里,记得保持渐进增强,用老方案兜底,别把全部希望押在一个还在演进中的东西上。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!