前端超能力:让浏览器听你指挥
《现代前端开发四大核心能力指南》摘要 本文系统阐述了现代前端开发的四大核心能力:1)WebAssembly加速技术,突破JS性能瓶颈,实现接近原生速度的计算密集型任务;2)性能嗅探能力,通过Performance API精准监控浏览器每一帧的渲染细节;3)跨端心智模型,构建一次编写多端运行的统一开发范式;4)高阶扩展技能,包括浏览器自动化、智能预取、WebGPU等前沿技术。文章从原理到实践,详细介
引言:从页面仔到浏览器驯服者
前端开发早已不是切图、做动画、调样式的“页面仔”时代。当现代浏览器进化为一个接近操作系统的运行时环境,我们手中的工具也悄然升级为真正的“超能力”——可以让网络嗅探每一字节的流动,可以编译其他语言到WebAssembly以获得接近原生的速度,可以构建一套代码跨桌面、移动、甚至嵌入式设备运行。这不是科幻,而是今天顶级前端工程师每天都在使用的技能。
本文是一份2万字的深度指南,带你系统掌握让浏览器完全听你指挥的四大核心超能力:WebAssembly加速、性能嗅探、跨端心智模型,以及一系列扩展的高阶技巧。我们将从原理到实战,从代码片段到架构决策,全方位构建你的前端超级工程师技能树。
第一章 WebAssembly加速:突破JavaScript的性能天花板
1.1 为什么需要WebAssembly?
JavaScript自诞生起就被设计为解释型语言,尽管现代JIT引擎(V8、SpiderMonkey、JavaScriptCore)已经将执行速度推到了接近编译型语言的水平,但它在某些场景下依然力不从心:
-
密集计算:图像/视频处理、3D物理模拟、加密算法
-
实时性要求高:音频合成、游戏循环、实时数据可视化
-
复用现有库:将C++/Rust编写的成熟算法无痛迁移到浏览器
WebAssembly(简称Wasm)提供了一种低级的类汇编语言,以二进制格式运行在浏览器中,执行效率接近原生。它的设计目标包括:高效、安全、跨平台、与JavaScript互操作。
核心原理:Wasm模块在加载后会经过解码、验证、编译,最终生成与平台相关的机器码。由于Wasm的类型系统是静态的且指令集简单,编译器可以做出激进的优化,避免了JIT的暖身阶段和类型推断开销。
1.2 WebAssembly指令集与虚拟机
Wasm基于栈式虚拟机,典型指令如i32.add、f64.mul、call。一个简单的加法函数在WAT(文本格式)中表示为:
wat
(module
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)
(export "add" (func $add)))
更直观的是,我们可以直接使用高级语言生成Wasm。目前最成熟的工具链包括:
-
Emscripten:将C/C++编译为Wasm,并提供POSIX兼容层
-
wasm-pack:Rust生态的标准编译工具
-
AssemblyScript:TypeScript的子集,直接编译为Wasm
-
Go、C#、Kotlin等也均有Wasm目标支持
1.3 实战:使用Rust编写高性能Wasm模块
我们从一个计算密集型任务开始:计算曼德布罗特集(分形),该算法对每个像素进行复杂平面迭代,是典型的CPU杀手。
步骤1:创建Rust项目
bash
cargo new mandelbrot-wasm --lib cd mandelbrot-wasm
在Cargo.toml中添加:
toml
[lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2"
wasm-bindgen提供了JS与Rust之间的高级互操作(无需手动管理内存)。
步骤2:编写核心算法
src/lib.rs:
rust
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn mandelbrot(width: u32, height: u32, max_iter: u32) -> Vec<u8> {
let mut pixels = vec![0u8; (width * height) as usize];
for y in 0..height {
for x in 0..width {
let real = (x as f64 / width as f64) * 3.5 - 2.5;
let imag = (y as f64 / height as f64) * 2.0 - 1.0;
let mut z_real = 0.0;
let mut z_imag = 0.0;
let mut iter = 0;
while iter < max_iter && (z_real * z_real + z_imag * z_imag) <= 4.0 {
let new_real = z_real * z_real - z_imag * z_imag + real;
let new_imag = 2.0 * z_real * z_imag + imag;
z_real = new_real;
z_imag = new_imag;
iter += 1;
}
let color = if iter == max_iter { 0 } else { (iter * 255 / max_iter) as u8 };
pixels[(y * width + x) as usize] = color;
}
}
pixels
}
步骤3:构建Wasm模块
bash
wasm-pack build --target web
生成的文件:pkg/mandelbrot_wasm_bg.wasm 和 pkg/mandelbrot_wasm.js 绑定层。
步骤4:在浏览器中使用
html
<script type="module">
import init, { mandelbrot } from './pkg/mandelbrot_wasm.js';
async function run() {
await init();
const width = 800, height = 600;
const pixels = mandelbrot(width, height, 256);
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const imgData = ctx.createImageData(width, height);
for (let i = 0; i < pixels.length; i++) {
const v = pixels[i];
imgData.data[i*4] = v;
imgData.data[i*4+1] = v;
imgData.data[i*4+2] = v;
imgData.data[i*4+3] = 255;
}
ctx.putImageData(imgData, 0, 0);
}
run();
</script>
性能对比:同样算法使用纯JavaScript实现,在800x600分辨率下,Wasm版本约12ms,JS版本约45ms(Chrome 120)。在更复杂的迭代(如光线追踪、粒子系统)中,差距可高达5-10倍。
1.4 高级互操作:传递复杂数据与共享内存
Wasm默认只能接收简单类型(整数、浮点数)。对于大数组,最佳实践是使用共享内存(SharedArrayBuffer)或直接传递指向线性内存的指针。
通过wasm-bindgen,我们可以轻松传递JavaScript的Uint8Array:
rust
#[wasm_bindgen]
pub fn process_image(input: &[u8], output: &mut [u8], width: u32, height: u32) {
// 直接读写切片,无需复制
for y in 0..height {
for x in 0..width {
let idx = (y * width + x) as usize;
output[idx] = input[idx].wrapping_mul(2);
}
}
}
JS侧:
js
const input = new Uint8Array([...]); const output = new Uint8Array(input.length); process_image(input, output, width, height);
内部机制:wasm-bindgen自动生成胶水代码,将JS TypedArray的底层指针传递给Wasm线性内存,零拷贝。
对于无法预分配大小的数据,可以使用Vec,但需要注意内存泄漏——每次Rust返回Vec到JS会分配新内存且不会自动释放,解决方案是实现free函数:
rust
#[wasm_bindgen]
pub struct ImageData {
pixels: Vec<u8>,
width: u32,
height: u32,
}
#[wasm_bindgen]
impl ImageData {
pub fn new(width: u32, height: u32) -> Self {
ImageData { pixels: vec![0; (width*height) as usize], width, height }
}
pub fn pixels_ptr(&self) -> *const u8 {
self.pixels.as_ptr()
}
// 在JS中使用后调用
pub fn free(self) {}
}
1.5 WebAssembly系统接口(WASI)与浏览器之外的Wasm
WASI为Wasm定义了系统级API(文件、网络、时钟),让Wasm可以运行在服务器或边缘计算环境。在浏览器中,虽然不能直接使用WASI,但可以通过Wasm组件模型将WASI函数映射到浏览器API(如fetch模拟网络)。
未来趋势:Wasm垃圾回收提案让高阶语言(Java、C#)可以直接托管内存;多值返回、异常处理、SIMD支持已在主流浏览器落地。WebAssembly正在从“计算加速器”演变为“跨平台字节码标准”。
1.6 何时不应使用WebAssembly?
-
DOM操作密集:Wasm无法直接操作DOM,需要调用JS桥接,性能开销可能超过收益
-
代码体积敏感:Wasm模块即使压缩后也有几十KB起,首屏极小的页面不必使用
-
开发效率优先:调试Wasm比JS困难,构建流程复杂
最佳实践:将Wasm作为模块化加速器,只将核心计算部分下沉到Wasm,UI与事件驱动保持JS。
第二章 性能嗅探:洞察浏览器的每一帧
如果说WebAssembly是增加引擎的马力,那么性能嗅探就是给引擎装上传感器和诊断系统。一个真正的前端高手,能像医生一样给浏览器做CT扫描,精准定位每一毫秒的延迟。
2.1 Performance API:浏览器的黑匣子
现代浏览器提供了performance对象,记录着从导航到资源加载、从长任务到帧回调的详细时间线。最强大的工具是PerformanceObserver,可以订阅各种性能事件。
js
// 监听所有长任务(阻塞主线程超过50ms)
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.warn(`长任务: ${entry.duration}ms`, entry);
}
});
observer.observe({ entryTypes: ['longtask'] });
其他重要的entryTypes:
-
navigation:页面加载完整时间线(DNS、TCP、请求、DOM解析) -
resource:每个资源(图片、脚本、字体)的时机 -
paint:首次绘制(FP)、首次内容绘制(FCP) -
layout-shift:累积布局偏移(CLS) -
largest-contentful-paint:最大内容绘制(LCP) -
first-input:首次输入延迟(FID)
2.2 核心Web指标(Core Web Vitals)与用户体验量化
Google定义了三个核心指标,直接影响搜索排名:
-
LCP(最大内容绘制):视口中最大元素渲染完成时间,理想<2.5秒
-
INP(与下一次绘制的交互):取代FID,衡量所有交互延迟,理想<200毫秒
-
CLS(累积布局偏移):意外移动视觉元素的程度,理想<0.1
获取真实用户监控(RUM)数据:
js
import {getCLS, getFID, getLCP, getFCP, getTTFB} from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify({ name: metric.name, value: metric.value });
navigator.sendBeacon('/api/metrics', body);
}
getCLS(sendToAnalytics);
getLCP(sendToAnalytics);
getFID(sendToAnalytics);
2.3 长任务与时间分片
主线程执行超过50ms的任务称为长任务,会导致界面卡顿或输入延迟。对策是时间分片(Time Slicing):将一个长任务拆分为多个小于50ms的块,中间穿插setTimeout或requestIdleCallback让给用户交互。
js
function processLargeArray(data, chunkSize = 100) {
let index = 0;
function processChunk() {
const end = Math.min(index + chunkSize, data.length);
for (; index < end; index++) {
// 处理单个元素
}
if (index < data.length) {
setTimeout(processChunk, 0); // 让出主线程
}
}
processChunk();
}
更优雅的现代方案:使用Scheduler.postTask() 配合优先级:
js
async function backgroundWork() {
const scheduler = navigator.scheduling;
if (scheduler) {
await scheduler.postTask(() => heavyCompute(), { priority: 'background' });
}
}
2.4 内存泄漏检测与分析
Chrome DevTools的Memory面板提供:
-
Heap snapshot:拍摄堆快照,比较两次快照间的对象增长
-
Allocation instrumentation on timeline:记录一段时间内的分配,定位持续增长的分配
-
Allocation sampling:低开销采样,适合长时间监控
典型的内存泄漏模式:
-
未解绑的事件监听器(尤其在单页应用中重复挂载组件)
-
闭包持有大对象
-
全局变量/缓存无限制增长
-
分离的DOM树(从文档移除但仍有JS引用)
实战工具:performance.memory API(非标准,仅Chrome)可实时监控堆大小:
js
function checkMemory() {
if (performance.memory) {
console.log(`已使用JS堆: ${performance.memory.usedJSHeapSize / 1048576} MB`);
}
}
2.5 网络嗅探:资源时机、优先级与缓存策略
浏览器的网络请求是一个复杂的调度系统。通过PerformanceResourceTiming可以获取每个资源的详细时机:
js
const resources = performance.getEntriesByType('resource');
resources.forEach(r => {
console.log(`${r.name}: DNS=${r.domainLookupEnd - r.domainLookupStart}ms,
TCP=${r.connectEnd - r.connectStart}ms,
请求=${r.responseStart - r.requestStart}ms,
下载=${r.responseEnd - r.responseStart}ms`);
});
关键指标:
-
排队时间(
queueTime非标准):请求因优先级或HTTP/2并发限制而等待的时间,优化手段包括preload、preconnect、升级H3。 -
TTFB(首字节时间):服务器响应速度,受后端性能、网络延迟影响。
利用<link rel="preload">提示浏览器提前加载关键资源:
html
<link rel="preload" href="critical-font.woff2" as="font" crossorigin> <link rel="preconnect" href="https://api.example.com">
2.6 帧率监控与流畅度优化(帧预算)
浏览器渲染一帧的预算在60Hz刷新率下是16.6ms。当主线程任务(JS、样式计算、布局、绘制)超过此阈值,就会产生丢帧。监控帧率的方法:
js
let lastFrameTime = performance.now();
let frameCount = 0;
function measureFPS() {
frameCount++;
const now = performance.now();
if (now - lastFrameTime >= 1000) {
console.log(`当前FPS: ${frameCount}`);
frameCount = 0;
lastFrameTime = now;
}
requestAnimationFrame(measureFPS);
}
requestAnimationFrame(measureFPS);
更专业的是使用PerformanceObserver观察frame事件(早期标记,现多为long-animation-frame)。
帧预算分配建议(16.6ms内):
-
脚本执行:≤8ms
-
样式计算+布局:≤5ms
-
绘制与合成:≤3ms
-
剩余时间作为缓冲
优化技巧:
-
使用
transform和opacity实现动画(仅触发合成) -
避免强制同步布局:不要先读取
offsetHeight再修改样式 -
将滚动/交互监听设为
passive: true -
使用
will-change提示即将动画的元素
2.7 实战:构建一个自定义性能监控面板
下面构建一个小型的内部性能仪表盘,实时展示LCP、INP、CLS和当前FPS。
html
<div id="dashboard">
<div>LCP: <span id="lcp">-</span>ms</div>
<div>INP: <span id="inp">-</span>ms</div>
<div>CLS: <span id="cls">-</span></div>
<div>FPS: <span id="fps">-</span></div>
</div>
<script type="module">
import {onLCP, onINP, onCLS} from 'https://unpkg.com/web-vitals@4/dist/web-vitals.attribution.js?module';
onLCP(metric => document.getElementById('lcp').textContent = metric.value.toFixed(0));
onINP(metric => document.getElementById('inp').textContent = metric.value.toFixed(0));
onCLS(metric => document.getElementById('cls').textContent = metric.value.toFixed(3));
// FPS监控
let lastTimestamp = 0;
let frames = 0;
function tick(timestamp) {
frames++;
if (timestamp - lastTimestamp >= 1000) {
document.getElementById('fps').textContent = frames;
frames = 0;
lastTimestamp = timestamp;
}
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
</script>
结合sendBeacon可将数据上报到后端,用于分析趋势和版本回归检测。
第三章 跨端心智模型:一次编写,随处运行
真正的“让浏览器听你指挥”,不仅局限于桌面web,还要扩展到移动端、桌面应用、甚至嵌入式设备。跨端开发需要建立一套统一的心智模型,抽象出平台差异。
3.1 跨端的困境与演进
历史演进路径:
-
响应式Web:一套HTML/CSS/JS通过媒体查询适配不同屏幕尺寸。
-
混合应用:Cordova/PhoneGap将WebView包装成原生应用,性能受限。
-
React Native / Weex:使用JS桥接调用原生UI组件,性能接近原生。
-
Flutter:自绘引擎,完全脱离原生控件,一致性极高。
-
Tauri / Electron:桌面端Web技术栈封装,使用系统WebView或自定义渲染。
-
Kotlin Multiplatform / Compose Multiplatform:共享业务逻辑,UI各平台独立或共享。
关键洞察:跨端不是写一次代码在所有地方完美运行,而是共享尽可能多的逻辑,同时让UI层适配各自平台的交互范式。
3.2 响应式设计与硬件抽象
响应式设计的核心是布局弹性与断点。但除此之外,还要考虑输入方式(触摸 vs 鼠标)、网络质量(移动端弱网)、电量与性能限制。
硬件抽象示例:使用window.matchMedia检测是否支持触摸:
js
const isTouch = window.matchMedia('(pointer: coarse)').matches;
if (isTouch) {
// 增大点击区域、增加触感反馈
}
更全面的API:navigator.hardwareConcurrency(CPU核心数)、navigator.deviceMemory(设备内存估算)、navigator.connection.effectiveType(网络类型)。
3.3 服务端渲染、静态生成与客户端增强
跨端中,性能和SEO往往是痛点的根源。现代框架(Next.js、Nuxt、SvelteKit)提供了多种渲染策略:
-
SSR(服务端渲染):首屏快,但服务器压力大
-
SSG(静态站点生成):最快,适合内容不变页
-
ISR(增量静态再生):混合方案
-
CSR(客户端渲染):适合后台应用
在跨端项目中,你可以将同一个React组件在服务器上渲染为HTML,再在客户端水合(hydration),实现同构。
3.4 主流跨端框架对比与选型
React Native
-
优势:React生态庞大,热更新成熟,性能接近原生
-
劣势:复杂布局需要学习Flexbox特例,桥接序列化成本高
-
适合:已有React技术栈的团队,需要快速上架App
Flutter
-
优势:性能极佳(60fps保证),UI一致性极高,强大的开发工具
-
劣势:包体积大(~4MB基础),Dart语言学习成本
-
适合:对UI精致度和性能要求极高的应用
Tauri (桌面端)
-
优势:极小的包体积(基于系统WebView),内存占用低,Rust后端性能强
-
劣势:生态比Electron年轻
-
适合:需要原生系统调用但希望包体积小的桌面应用
Kotlin Multiplatform (KMP)
-
优势:共享业务逻辑(网络、数据存储、认证),原生UI保持平台个性
-
劣势:构建配置复杂,生态还在成长
-
适合:已有原生团队,希望减少重复逻辑
3.5 统一状态管理与路由
跨端项目中,状态管理需要同时支持Web和原生环境。推荐使用与平台无关的状态容器,例如Zustand、Redux Toolkit。注意避免使用依赖DOM的API(如localStorage),而是通过抽象接口:
ts
// 定义存储接口
interface IStorage {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
}
// Web实现
class WebStorage implements IStorage {
get(key) { return Promise.resolve(localStorage.getItem(key)); }
set(key, value) { localStorage.setItem(key, value); return Promise.resolve(); }
}
// React Native实现(使用AsyncStorage)
class RNStorage implements IStorage {
async get(key) { return await AsyncStorage.getItem(key); }
async set(key, value) { await AsyncStorage.setItem(key, value); }
}
同样,路由需要抽象:Web使用react-router-dom,移动端使用react-native-screens。
3.6 适配不同输入模式(触摸、鼠标、语音)
跨端应用必须优雅处理各种输入源:
-
悬停状态:触摸屏没有hover,需使用
@media (hover: hover)选择性启用悬停效果 -
点击区域:移动端至少44x44pt(苹果HIG标准)
-
键盘快捷键:桌面端需要支持
Ctrl+S等,移动端不需要 -
语音控制:通过
SpeechRecognitionAPI实现语音输入,增强无障碍
3.7 跨端测试策略
使用单一代码库时,测试覆盖:
-
单元测试:Jest + Vitest,测试平台无关逻辑
-
集成测试:针对每个平台运行端到端测试(Web用Playwright,移动端用Detox/Appium)
-
视觉回归测试:Percy、Loki确保UI在不同平台上一致
CI/CD中应包含矩阵构建:为每个目标平台(web、android、ios、windows)运行独立的测试套件。
心智模型总结:将应用分为三层:核心业务逻辑(纯TypeScript/Rust)、平台适配层(调用本地API)、UI展示层(各平台独立但共享设计令牌)。这种分层让跨端维护变得可控。
第四章 其他超能力技能:让浏览器完全臣服
4.1 浏览器自动化与脚本注入(Puppeteer、Playwright)
如果你想控制浏览器完成自动化测试、爬取SPA内容、生成PDF或截图,那么Puppeteer和Playwright是你的超能力。它们通过DevTools协议操控真实Chromium/Firefox/WebKit。
示例:使用Playwright截取所有长页面全屏滚动图:
js
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com/long-article');
// 滚动到最底部触发懒加载
await page.evaluate(async () => {
await new Promise((resolve) => {
let totalHeight = 0;
const distance = 400;
const timer = setInterval(() => {
const scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= scrollHeight) {
clearInterval(timer);
resolve();
}
}, 100);
});
});
await page.screenshot({ path: 'fullpage.png', fullPage: true });
await browser.close();
})();
更高级的用法:拦截网络请求、模拟地理位置、注入自定义脚本(如将console.log重定向到Node端)。
4.2 智能预取与预测导航
让浏览器提前加载用户下一步可能点击的页面,可以大幅提升感知性能。Google的Quick Link(或<link rel="prerender">)可以在空闲时渲染整个页面。
现代实现使用Predictive prefetch结合机器学习(分析用户鼠标移动/停留时间):
js
let prefetchTimeout;
document.querySelectorAll('a').forEach(link => {
link.addEventListener('mouseenter', () => {
const url = link.href;
prefetchTimeout = setTimeout(() => {
const prefetchLink = document.createElement('link');
prefetchLink.rel = 'prefetch';
prefetchLink.href = url;
document.head.appendChild(prefetchLink);
}, 100); // 鼠标悬停100ms后才预取,避免浪费
});
link.addEventListener('mouseleave', () => clearTimeout(prefetchTimeout));
});
更激进的:InstantClick 或 barba.js 实现页面切换时仅替换内容区域并更新历史,配合预取实现无刷新导航。
4.3 Web Workers与多线程编程
Web Workers是浏览器实现真正并行的唯一途径。主线程可以委派繁重任务给Worker,完全不影响UI响应。
专用Worker:
js
// worker.js
self.onmessage = (e) => {
const result = heavyComputation(e.data);
self.postMessage(result);
};
// 主线程
const worker = new Worker('worker.js');
worker.postMessage(largeDataset);
worker.onmessage = (e) => updateUI(e.data);
共享Worker可在多个标签页间共享;Service Worker则用于拦截网络请求、实现离线缓存和推送通知。
高级模式:Worker池——创建多个Worker,任务队列分发,并行处理大数据集(例如图像滤波)。
4.4 WebGPU:下一代图形与计算
WebGL虽然强大,但受限于OpenGL ES的陈旧模型。WebGPU是现代图形API(Vulkan/Metal/DirectX 12)的Web封装,提供:
-
更低的驱动开销:减少CPU端验证
-
计算着色器:通用GPU计算,比WebGL的片段着色器更灵活
-
多线程提交命令:支持从Worker提交渲染命令
一个小例子:用WebGPU计算向量加法(GPGPU):
js
// 简化代码,完整需处理适配器、设备、缓冲区、着色器
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const shader = `
@group(0) @binding(0) var<storage, read> a : array<f32>;
@group(0) @binding(1) var<storage, read> b : array<f32>;
@group(0) @binding(2) var<storage, read_write> result : array<f32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id : vec3<u32>) {
let i = id.x;
result[i] = a[i] + b[i];
}
`;
// ... 创建管线、绑定组、调度计算
WebGPU让浏览器可以运行类似TensorFlow.js的机器学习训练、实时流体模拟、百万级别粒子系统。
4.5 浏览器存储革命:OPFS与File System Access API
传统的存储(IndexedDB、localStorage)有性能或容量限制。Origin Private File System (OPFS) 提供了一个快速的、仅限源访问的虚拟文件系统,结合同步访问句柄可实现高性能存储。
js
const root = await navigator.storage.getDirectory();
const fileHandle = await root.getFileHandle('data.bin', { create: true });
const writable = await fileHandle.createWritable();
await writable.write(new Uint8Array([1,2,3]));
await writable.close();
File System Access API允许用户授予网页读写本地文件或文件夹的权限,类似于原生应用:
js
const [fileHandle] = await window.showOpenFilePicker(); const file = await fileHandle.getFile(); const contents = await file.text();
这些API加起来,浏览器已经具备了轻度桌面应用的存储能力。
4.6 安全沙盒与权限管理
作为浏览器指挥官,必须清楚安全边界。Content Security Policy(CSP)可以限制脚本来源、禁止eval。权限API可以查询/请求敏感权限(地理位置、摄像头、剪贴板):
js
navigator.permissions.query({ name: 'clipboard-write' }).then(result => {
if (result.state === 'granted') {
// 写入剪贴板
} else if (result.state === 'prompt') {
// 需要请求用户授权
}
});
隔离是另一个趋势:使用Cross-Origin-Opener-Policy和Cross-Origin-Embedder-Policy启用SharedArrayBuffer并防御旁路攻击。
更多推荐


所有评论(0)