
在现代前端开发中,浏览器原生API为我们提供了强大的功能,让我们能够在不依赖第三方库的情况下实现复杂的功能。本文将深入探讨几个实用的浏览器原生API,包括Clipboard API、Notification API等,通过实际案例帮助你掌握这些API的使用技巧。
Clipboard API提供了对系统剪贴板的异步访问,让我们能够读取和写入文本、图片等各种数据类型。这个API在现代浏览器中得到了广泛支持,是实现复制粘贴功能的现代方案。
// 基础文本操作
navigator.clipboard.writeText('要复制的文本')
.then(() => console.log('文本已复制到剪贴板'))
.catch(err => console.error('复制失败:', err));
// 读取剪贴板文本
navigator.clipboard.readText()
.then(text => console.log('剪贴板内容:', text))
.catch(err => console.error('读取失败:', err));<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能复制功能</title>
<style>
.copy-container {
max-width: 600px;
margin: 50px auto;
padding: 20px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.copy-item {
display: flex;
align-items: center;
margin: 15px 0;
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border-left: 4px solid #007bff;
}
.copy-text {
flex: 1;
margin-right: 15px;
font-size: 14px;
color: #333;
}
.copy-btn {
padding: 8px 16px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
.copy-btn:hover {
background: #0056b3;
transform: translateY(-1px);
}
.copy-btn.copied {
background: #28a745;
}
.status-message {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
background: #28a745;
color: white;
border-radius: 4px;
opacity: 0;
transform: translateY(-20px);
transition: all 0.3s ease;
z-index: 1000;
}
.status-message.show {
opacity: 1;
transform: translateY(0);
}
</style>
</head>
<body>
<div class="copy-container">
<h1>智能复制功能演示</h1>
<p>点击下方按钮复制不同的内容到剪贴板</p>
<div class="copy-item">
<div class="copy-text">https://github.com/awesome-project</div>
<button class="copy-btn" onclick="copyText(this, 'https://github.com/awesome-project')">复制链接</button>
</div>
<div class="copy-item">
<div class="copy-text">const message = 'Hello, World!';</div>
<button class="copy-btn" onclick="copyText(this, \"const message = 'Hello, World!';\")">复制代码</button>
</div>
<div class="copy-item">
<div class="copy-text">contact@example.com</div>
<button class="copy-btn" onclick="copyText(this, 'contact@example.com')">复制邮箱</button>
</div>
</div>
<div id="statusMessage" class="status-message"></div>
<script>
async function copyText(button, text) {
try {
// 使用现代的 Clipboard API
await navigator.clipboard.writeText(text);
// 视觉反馈
button.classList.add('copied');
button.textContent = '已复制!';
// 显示状态消息
showStatusMessage('内容已复制到剪贴板');
// 3秒后恢复按钮状态
setTimeout(() => {
button.classList.remove('copied');
button.textContent = button.getAttribute('data-original-text') || '复制';
}, 3000);
} catch (err) {
console.error('复制失败:', err);
showStatusMessage('复制失败,请重试', 'error');
// 降级方案:使用传统的复制方法
fallbackCopyText(text);
}
}
function showStatusMessage(message, type = 'success') {
const statusEl = document.getElementById('statusMessage');
statusEl.textContent = message;
statusEl.style.background = type === 'error' ? '#dc3545' : '#28a745';
statusEl.classList.add('show');
setTimeout(() => {
statusEl.classList.remove('show');
}, 3000);
}
// 降级复制方案
function fallbackCopyText(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showStatusMessage('内容已复制到剪贴板');
} catch (err) {
showStatusMessage('复制失败,请手动复制', 'error');
}
document.body.removeChild(textArea);
}
// 为按钮保存原始文本
document.querySelectorAll('.copy-btn').forEach(btn => {
btn.setAttribute('data-original-text', btn.textContent);
});
</script>
</body>
</html>// 复制HTML内容
async function copyHTML() {
const htmlContent = `
<div style="color: blue; font-weight: bold;">
这是复制的HTML内容
</div>
`;
try {
// 创建Blob对象
const blob = new Blob([htmlContent], { type: 'text/html' });
// 创建ClipboardItem
const clipboardItem = new ClipboardItem({
'text/html': blob,
'text/plain': new Blob(['纯文本内容'], { type: 'text/plain' })
});
await navigator.clipboard.write([clipboardItem]);
console.log('富文本已复制');
} catch (err) {
console.error('复制富文本失败:', err);
}
}// 检查剪贴板权限
async function checkClipboardPermission() {
try {
const result = await navigator.permissions.query({ name: 'clipboard-read' });
console.log('剪贴板读取权限:', result.state); // granted, denied, prompt
if (result.state === 'granted') {
// 有权限,可以直接读取
const text = await navigator.clipboard.readText();
console.log('剪贴板内容:', text);
} else if (result.state === 'prompt') {
// 需要用户授权
console.log('需要用户授权剪贴板访问');
}
} catch (err) {
console.error('权限检查失败:', err);
}
}Notification API允许网页向用户显示系统通知,即使用户没有打开你的网站。这是提升用户体验和增加用户参与度的强大工具。
// 请求通知权限
async function requestNotificationPermission() {
if (!('Notification' in window)) {
console.log('此浏览器不支持通知');
return;
}
const permission = await Notification.requestPermission();
console.log('通知权限:', permission); // granted, denied, default
return permission;
}
// 发送简单通知
function sendBasicNotification() {
if (Notification.permission === 'granted') {
new Notification('你好!', {
body: '这是一条测试通知',
icon: '/path/to/icon.png'
});
}
}<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>智能通知系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.notification-container {
background: white;
padding: 40px;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
max-width: 500px;
width: 90%;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
font-size: 28px;
}
.permission-section {
margin-bottom: 30px;
padding: 20px;
background: #f8f9fa;
border-radius: 10px;
text-align: center;
}
.permission-status {
display: inline-block;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
margin-top: 10px;
}
.permission-status.granted {
background: #d4edda;
color: #155724;
}
.permission-status.denied {
background: #f8d7da;
color: #721c24;
}
.permission-status.default {
background: #fff3cd;
color: #856404;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
margin: 10px 5px;
font-weight: 500;
}
.btn-primary {
background: #007bff;
color: white;
}
.btn-success {
background: #28a745;
color: white;
}
.btn-warning {
background: #ffc107;
color: #212529;
}
.btn-info {
background: #17a2b8;
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.notification-form {
margin-top: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: 500;
}
input, textarea, select {
width: 100%;
padding: 12px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
input:focus, textarea:focus, select:focus {
outline: none;
border-color: #007bff;
}
.notification-log {
margin-top: 30px;
max-height: 200px;
overflow-y: auto;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
}
.log-item {
padding: 8px 12px;
margin: 5px 0;
background: white;
border-radius: 4px;
border-left: 4px solid #007bff;
font-size: 14px;
}
</style>
</head>
<body>
<div class="notification-container">
<h1>🚀 智能通知系统</h1>
<div class="permission-section">
<h3>通知权限状态</h3>
<div id="permissionStatus" class="permission-status default">未检查</div>
<button class="btn btn-primary" onclick="checkPermission()">检查权限</button>
<button class="btn btn-warning" onclick="requestPermission()">请求权限</button>
</div>
<div class="notification-form">
<h3>发送自定义通知</h3>
<div class="form-group">
<label for="notificationTitle">通知标题:</label>
<input type="text" id="notificationTitle" value="测试通知" placeholder="输入通知标题">
</div>
<div class="form-group">
<label for="notificationBody">通知内容:</label>
<textarea id="notificationBody" rows="3" placeholder="输入通知内容">这是一条测试通知消息</textarea>
</div>
<div class="form-group">
<label for="notificationIcon">图标URL (可选):</label>
<input type="url" id="notificationIcon" placeholder="https://example.com/icon.png">
</div>
<div class="form-group">
<label for="notificationTag">标签 (可选):</label>
<input type="text" id="notificationTag" placeholder="通知标签,用于替换相同标签的通知">
</div>
<div class="form-group">
<label for="notificationTimeout">自动关闭时间 (秒):</label>
<select id="notificationTimeout">
<option value="0">不自动关闭</option>
<option value="3">3秒</option>
<option value="5" selected>5秒</option>
<option value="10">10秒</option>
</select>
</div>
<button class="btn btn-success" onclick="sendCustomNotification()">发送通知</button>
<button class="btn btn-info" onclick="sendAdvancedNotification()">发送高级通知</button>
</div>
<div class="notification-log">
<h3>通知日志</h3>
<div id="notificationLog"></div>
</div>
</div>
<script>
let notificationCounter = 0;
// 检查当前权限状态
function checkPermission() {
if (!('Notification' in window)) {
updatePermissionStatus('unsupported');
return;
}
updatePermissionStatus(Notification.permission);
}
// 更新权限状态显示
function updatePermissionStatus(status) {
const statusEl = document.getElementById('permissionStatus');
statusEl.className = `permission-status ${status}`;
switch(status) {
case 'granted':
statusEl.textContent = '✅ 已授权';
break;
case 'denied':
statusEl.textContent = '❌ 已拒绝';
break;
case 'default':
statusEl.textContent = '⏳ 默认状态';
break;
case 'unsupported':
statusEl.textContent = '⚠️ 浏览器不支持';
break;
}
}
// 请求通知权限
async function requestPermission() {
if (!('Notification' in window)) {
addLog('浏览器不支持通知功能');
return;
}
try {
const permission = await Notification.requestPermission();
updatePermissionStatus(permission);
addLog(`权限请求结果: ${permission}`);
} catch (error) {
addLog(`权限请求失败: ${error.message}`);
}
}
// 发送自定义通知
function sendCustomNotification() {
if (Notification.permission !== 'granted') {
addLog('没有通知权限,请先请求权限');
return;
}
const title = document.getElementById('notificationTitle').value || '默认标题';
const body = document.getElementById('notificationBody').value || '默认内容';
const icon = document.getElementById('notificationIcon').value;
const tag = document.getElementById('notificationTag').value;
const timeout = parseInt(document.getElementById('notificationTimeout').value);
const options = {
body: body,
icon: icon || undefined,
tag: tag || undefined,
timestamp: Date.now()
};
try {
const notification = new Notification(title, options);
notificationCounter++;
notification.onclick = function() {
addLog(`通知 #${notificationCounter} 被点击`);
window.focus();
notification.close();
};
notification.onshow = function() {
addLog(`通知 #${notificationCounter} 已显示`);
};
notification.onclose = function() {
addLog(`通知 #${notificationCounter} 已关闭`);
};
notification.onerror = function() {
addLog(`通知 #${notificationCounter} 显示失败`);
};
// 自动关闭
if (timeout > 0) {
setTimeout(() => {
notification.close();
}, timeout * 1000);
}
addLog(`通知 #${notificationCounter} 发送成功`);
} catch (error) {
addLog(`通知发送失败: ${error.message}`);
}
}
// 发送高级通知(使用Service Worker)
async function sendAdvancedNotification() {
if (Notification.permission !== 'granted') {
addLog('没有通知权限,请先请求权限');
return;
}
// 检查是否支持Service Worker
if (!('serviceWorker' in navigator)) {
addLog('浏览器不支持Service Worker,使用基础通知');
sendCustomNotification();
return;
}
try {
const registration = await navigator.serviceWorker.register('data:text/javascript,' + encodeURIComponent(`
self.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(
clients.openWindow('https://example.com')
);
});
`));
const title = document.getElementById('notificationTitle').value || '高级通知';
const options = {
body: '这是通过Service Worker发送的高级通知',
icon: '/favicon.ico',
badge: '/favicon.ico',
vibrate: [200, 100, 200],
actions: [
{ action: 'view', title: '查看详情' },
{ action: 'close', title: '关闭' }
],
data: {
url: 'https://example.com',
timestamp: Date.now()
}
};
await registration.showNotification(title, options);
addLog('高级通知发送成功');
} catch (error) {
addLog(`高级通知发送失败: ${error.message}`);
// 降级到基础通知
sendCustomNotification();
}
}
// 添加日志
function addLog(message) {
const logEl = document.getElementById('notificationLog');
const logItem = document.createElement('div');
logItem.className = 'log-item';
const time = new Date().toLocaleTimeString();
logItem.textContent = `[${time}] ${message}`;
logEl.insertBefore(logItem, logEl.firstChild);
// 限制日志数量
while (logEl.children.length > 20) {
logEl.removeChild(logEl.lastChild);
}
}
// 页面加载时检查权限
window.onload = function() {
checkPermission();
addLog('页面加载完成');
};
</script>
</body>
</html>// 通知管理器类
class NotificationManager {
constructor() {
this.notifications = new Map();
this.maxNotifications = 5;
}
async send(options) {
if (Notification.permission !== 'granted') {
throw new Error('没有通知权限');
}
// 限制通知数量
if (this.notifications.size >= this.maxNotifications) {
const oldestKey = this.notifications.keys().next().value;
this.notifications.get(oldestKey).close();
this.notifications.delete(oldestKey);
}
const notification = new Notification(options.title, {
body: options.body,
icon: options.icon,
tag: options.tag || Date.now().toString(),
requireInteraction: options.requireInteraction || false
});
const id = options.tag || Date.now().toString();
this.notifications.set(id, notification);
// 监听事件
notification.onclick = () => {
options.onClick?.();
this.close(id);
};
notification.onclose = () => {
this.notifications.delete(id);
options.onClose?.();
};
// 自动关闭
if (options.autoClose) {
setTimeout(() => this.close(id), options.autoClose);
}
return id;
}
close(id) {
const notification = this.notifications.get(id);
if (notification) {
notification.close();
this.notifications.delete(id);
}
}
closeAll() {
this.notifications.forEach(notification => notification.close());
this.notifications.clear();
}
}
// 使用示例
const notificationManager = new NotificationManager();
// 发送重要通知
notificationManager.send({
title: '重要提醒',
body: '您有一个重要会议即将开始',
icon: '/important-icon.png',
requireInteraction: true,
autoClose: 10000,
onClick: () => {
window.focus();
// 打开相关页面
}
});// 获取用户位置
function getUserLocation() {
if (!navigator.geolocation) {
console.log('浏览器不支持地理定位');
return;
}
const options = {
enableHighAccuracy: true, // 高精度
timeout: 5000, // 5秒超时
maximumAge: 0 // 不使用缓存
};
navigator.geolocation.getCurrentPosition(
(position) => {
console.log('纬度:', position.coords.latitude);
console.log('经度:', position.coords.longitude);
console.log('精度:', position.coords.accuracy);
console.log('海拔:', position.coords.altitude);
console.log('速度:', position.coords.speed);
},
(error) => {
console.error('获取位置失败:', error.message);
switch(error.code) {
case error.PERMISSION_DENIED:
console.log('用户拒绝了地理定位请求');
break;
case error.POSITION_UNAVAILABLE:
console.log('位置信息不可用');
break;
case error.TIMEOUT:
console.log('获取位置超时');
break;
}
},
options
);
}
// 持续监听位置变化
const watchId = navigator.geolocation.watchPosition(
(position) => {
console.log('位置更新:', position.coords.latitude, position.coords.longitude);
},
(error) => {
console.error('位置监听错误:', error);
}
);
// 停止监听
navigator.geolocation.clearWatch(watchId);// 获取电池信息(部分浏览器支持)
async function getBatteryInfo() {
if (!navigator.getBattery) {
console.log('浏览器不支持电池API');
return;
}
try {
const battery = await navigator.getBattery();
console.log('电池电量:', battery.level * 100 + '%');
console.log('是否充电:', battery.charging);
console.log('充电时间:', battery.chargingTime + '秒');
console.log('剩余时间:', battery.dischargingTime + '秒');
// 监听电池状态变化
battery.addEventListener('levelchange', () => {
console.log('电量变化:', battery.level * 100 + '%');
});
battery.addEventListener('chargingchange', () => {
console.log('充电状态变化:', battery.charging);
});
battery.addEventListener('chargingtimechange', () => {
console.log('充电时间变化:', battery.chargingTime);
});
battery.addEventListener('dischargingtimechange', () => {
console.log('剩余时间变化:', battery.dischargingTime);
});
} catch (error) {
console.error('获取电池信息失败:', error);
}
}// 获取网络信息
function getNetworkInfo() {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (!connection) {
console.log('浏览器不支持网络信息API');
return;
}
console.log('网络类型:', connection.type); // bluetooth, cellular, ethernet, wifi, wimax, other, none
console.log('有效网络类型:', connection.effectiveType); // slow-2g, 2g, 3g, 4g
console.log('下行速度:', connection.downlink + 'Mb/s');
console.log('往返时间:', connection.rtt + 'ms');
console.log('节省数据模式:', connection.saveData);
// 监听网络变化
connection.addEventListener('change', () => {
console.log('网络状态变化');
console.log('新的网络类型:', connection.type);
console.log('新的有效类型:', connection.effectiveType);
});
}
// 根据网络状态调整应用行为
function adaptToNetworkConditions() {
const connection = navigator.connection;
if (connection) {
if (connection.effectiveType === 'slow-2g' || connection.effectiveType === '2g') {
// 慢速网络,降低图片质量,禁用自动播放
console.log('检测到慢速网络,启用省流模式');
enableDataSavingMode();
} else if (connection.saveData) {
// 用户启用了节省数据模式
console.log('用户启用节省数据模式');
enableDataSavingMode();
}
}
}
function enableDataSavingMode() {
// 禁用自动播放视频
// 使用低质量图片
// 延迟加载非关键资源
console.log('已启用省流模式');
}// 检测页面可见性变化
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
console.log('页面隐藏,暂停非必要操作');
// 暂停视频播放
// 停止轮询
// 减少网络请求
} else {
console.log('页面可见,恢复正常操作');
// 恢复视频播放
// 重新开始轮询
// 恢复网络请求
}
});
// 实际应用示例
class PageVisibilityManager {
constructor() {
this.isVisible = !document.hidden;
this.listeners = new Map();
document.addEventListener('visibilitychange', () => {
this.isVisible = !document.hidden;
this.notifyListeners();
});
}
onVisibilityChange(callback) {
const id = Date.now().toString();
this.listeners.set(id, callback);
return id;
}
removeListener(id) {
this.listeners.delete(id);
}
notifyListeners() {
this.listeners.forEach(callback => {
callback(this.isVisible);
});
}
// 在页面可见时执行操作
whenVisible(callback) {
if (this.isVisible) {
callback();
} else {
const id = this.onVisibilityChange((visible) => {
if (visible) {
callback();
this.removeListener(id);
}
});
}
}
}
// 使用示例
const visibilityManager = new PageVisibilityManager();
// 监听可见性变化
visibilityManager.onVisibilityChange((isVisible) => {
console.log('页面可见性:', isVisible);
});
// 只在页面可见时获取新数据
visibilityManager.whenVisible(() => {
console.log('页面可见,获取最新数据');
fetchLatestData();
});
function fetchLatestData() {
console.log('获取最新数据...');
}// 进入全屏模式
async function enterFullscreen(element = document.documentElement) {
try {
if (element.requestFullscreen) {
await element.requestFullscreen();
} else if (element.webkitRequestFullscreen) {
await element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) {
await element.msRequestFullscreen();
}
console.log('已进入全屏模式');
} catch (error) {
console.error('进入全屏失败:', error);
}
}
// 退出全屏模式
async function exitFullscreen() {
try {
if (document.exitFullscreen) {
await document.exitFullscreen();
} else if (document.webkitExitFullscreen) {
await document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
await document.msExitFullscreen();
}
console.log('已退出全屏模式');
} catch (error) {
console.error('退出全屏失败:', error);
}
}
// 检测全屏状态变化
document.addEventListener('fullscreenchange', () => {
if (document.fullscreenElement) {
console.log('当前处于全屏模式');
} else {
console.log('当前处于窗口模式');
}
});
// 全屏API实用类
class FullscreenManager {
constructor() {
this.isFullscreen = this.checkFullscreen();
this.originalElement = null;
document.addEventListener('fullscreenchange', () => {
this.isFullscreen = this.checkFullscreen();
});
}
checkFullscreen() {
return !!(document.fullscreenElement ||
document.webkitFullscreenElement ||
document.msFullscreenElement);
}
async enter(element = document.documentElement) {
this.originalElement = element;
try {
const requestMethod = element.requestFullscreen ||
element.webkitRequestFullscreen ||
element.msRequestFullscreen;
if (requestMethod) {
await requestMethod.call(element);
return true;
}
} catch (error) {
console.error('进入全屏失败:', error);
}
return false;
}
async exit() {
try {
const exitMethod = document.exitFullscreen ||
document.webkitExitFullscreen ||
document.msExitFullscreen;
if (exitMethod) {
await exitMethod.call(document);
return true;
}
} catch (error) {
console.error('退出全屏失败:', error);
}
return false;
}
toggle(element = document.documentElement) {
if (this.isFullscreen) {
return this.exit();
} else {
return this.enter(element);
}
}
// 全屏时锁定方向(移动端)
async lockOrientation(orientation) {
if (screen.orientation && screen.orientation.lock) {
try {
await screen.orientation.lock(orientation);
console.log('屏幕方向已锁定:', orientation);
} catch (error) {
console.error('锁定屏幕方向失败:', error);
}
}
}
// 解锁屏幕方向
unlockOrientation() {
if (screen.orientation && screen.orientation.unlock) {
screen.orientation.unlock();
console.log('屏幕方向已解锁');
}
}
}
// 使用示例
const fullscreenManager = new FullscreenManager();
// 全屏切换
document.getElementById('fullscreenBtn')?.addEventListener('click', () => {
fullscreenManager.toggle();
});
// 全屏观看视频
function fullscreenVideo(videoElement) {
fullscreenManager.enter(videoElement).then(success => {
if (success && screen.orientation) {
// 横屏显示(移动端)
fullscreenManager.lockOrientation('landscape');
}
});
}// 通用特性检测函数
function checkFeatureSupport(feature) {
const features = {
clipboard: () => 'clipboard' in navigator,
notification: () => 'Notification' in window,
geolocation: () => 'geolocation' in navigator,
battery: () => 'getBattery' in navigator,
networkInfo: () => 'connection' in navigator,
pageVisibility: () => 'hidden' in document,
fullscreen: () => 'requestFullscreen' in document.documentElement ||
'webkitRequestFullscreen' in document.documentElement ||
'msRequestFullscreen' in document.documentElement
};
return features[feature] ? features[feature]() : false;
}
// 兼容性管理器
class CompatibilityManager {
constructor() {
this.supportedFeatures = this.detectFeatures();
this.polyfills = new Map();
}
detectFeatures() {
return {
clipboard: checkFeatureSupport('clipboard'),
notification: checkFeatureSupport('notification'),
geolocation: checkFeatureSupport('geolocation'),
battery: checkFeatureSupport('battery'),
networkInfo: checkFeatureSupport('networkInfo'),
pageVisibility: checkFeatureSupport('pageVisibility'),
fullscreen: checkFeatureSupport('fullscreen')
};
}
// 检查是否支持某个特性
isSupported(feature) {
return this.supportedFeatures[feature] || false;
}
// 获取支持的特性列表
getSupportedFeatures() {
return Object.keys(this.supportedFeatures)
.filter(feature => this.supportedFeatures[feature]);
}
// 注册降级方案
registerFallback(feature, fallbackFunction) {
this.polyfills.set(feature, fallbackFunction);
}
// 执行带降级的操作
async executeWithFallback(feature, primaryFunction, ...args) {
if (this.isSupported(feature)) {
try {
return await primaryFunction(...args);
} catch (error) {
console.warn(`${feature} API 执行失败:`, error);
const fallback = this.polyfills.get(feature);
if (fallback) {
console.log(`使用 ${feature} 的降级方案`);
return await fallback(...args);
}
throw error;
}
} else {
const fallback = this.polyfills.get(feature);
if (fallback) {
console.log(`浏览器不支持 ${feature},使用降级方案`);
return await fallback(...args);
}
throw new Error(`浏览器不支持 ${feature},且无可用降级方案`);
}
}
}
// 创建兼容性管理器实例
const compatManager = new CompatibilityManager();
// 注册降级方案
compatManager.registerFallback('clipboard', {
writeText: async (text) => {
// 降级到传统的复制方法
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
return Promise.resolve();
} catch (error) {
return Promise.reject(error);
} finally {
document.body.removeChild(textArea);
}
}
});
// 使用示例
async function safeCopyText(text) {
try {
await compatManager.executeWithFallback(
'clipboard',
() => navigator.clipboard.writeText(text),
text
);
console.log('复制成功');
} catch (error) {
console.error('复制失败:', error);
}
}// 浏览器信息检测
class BrowserDetector {
constructor() {
this.ua = navigator.userAgent;
this.browser = this.detectBrowser();
this.version = this.detectVersion();
}
detectBrowser() {
if (this.ua.includes('Chrome') && !this.ua.includes('Edg')) return 'Chrome';
if (this.ua.includes('Safari') && !this.ua.includes('Chrome')) return 'Safari';
if (this.ua.includes('Firefox')) return 'Firefox';
if (this.ua.includes('Edg')) return 'Edge';
if (this.ua.includes('Trident') || this.ua.includes('MSIE')) return 'IE';
return 'Unknown';
}
detectVersion() {
const regex = new RegExp(`${this.browser}\\/([\\d.]+)`);
const match = this.ua.match(regex);
return match ? match[1] : 'Unknown';
}
// 检查是否支持特定API版本
supportsAPI(apiName, minVersion = null) {
const supportMap = {
'Chrome': {
clipboard: '63',
notification: '22',
geolocation: '5',
battery: '38',
networkInfo: '61',
pageVisibility: '33',
fullscreen: '71'
},
'Firefox': {
clipboard: '63',
notification: '22',
geolocation: '3.5',
battery: '43',
networkInfo: '62',
pageVisibility: '18',
fullscreen: '64'
},
'Safari': {
clipboard: '13.1',
notification: '7',
geolocation: '5',
battery: null, // 不支持
networkInfo: '11.1',
pageVisibility: '7',
fullscreen: '12'
},
'Edge': {
clipboard: '79',
notification: '14',
geolocation: '12',
battery: '79',
networkInfo: '79',
pageVisibility: '12',
fullscreen: '79'
}
};
const browserSupport = supportMap[this.browser];
if (!browserSupport || !browserSupport[apiName]) {
return false;
}
if (!minVersion) {
return true;
}
const supportedVersion = browserSupport[apiName];
const currentVersion = this.version;
return this.compareVersions(currentVersion, supportedVersion) >= 0;
}
compareVersions(v1, v2) {
const parts1 = v1.split('.').map(Number);
const parts2 = v2.split('.').map(Number);
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const part1 = parts1[i] || 0;
const part2 = parts2[i] || 0;
if (part1 > part2) return 1;
if (part1 < part2) return -1;
}
return 0;
}
}
// 使用示例
const browserDetector = new BrowserDetector();
console.log(`浏览器: ${browserDetector.browser} ${browserDetector.version}`);
// 检查特定API支持
if (browserDetector.supportsAPI('clipboard', '63')) {
console.log('支持Clipboard API');
} else {
console.log('不支持Clipboard API或版本过低');
}// API调用优化器
class APIOptimizer {
constructor() {
this.cache = new Map();
this.pendingRequests = new Map();
this.rateLimits = new Map();
}
// 带缓存的API调用
async cachedAPICall(key, apiFunction, ttl = 60000) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < ttl) {
console.log(`使用缓存的数据: ${key}`);
return cached.data;
}
// 检查是否有正在进行的请求
if (this.pendingRequests.has(key)) {
console.log(`等待进行中的请求: ${key}`);
return await this.pendingRequests.get(key);
}
// 创建新的请求
const requestPromise = apiFunction().then(data => {
this.cache.set(key, {
data: data,
timestamp: Date.now()
});
this.pendingRequests.delete(key);
return data;
}).catch(error => {
this.pendingRequests.delete(key);
throw error;
});
this.pendingRequests.set(key, requestPromise);
return await requestPromise;
}
// 限流控制
async rateLimitedAPICall(key, apiFunction, maxCalls = 10, timeWindow = 60000) {
const now = Date.now();
const rateLimit = this.rateLimits.get(key) || {
calls: [],
maxCalls: maxCalls,
timeWindow: timeWindow
};
// 清理过期的调用记录
rateLimit.calls = rateLimit.calls.filter(timestamp =>
now - timestamp < timeWindow
);
// 检查是否超过限制
if (rateLimit.calls.length >= maxCalls) {
const oldestCall = Math.min(...rateLimit.calls);
const waitTime = timeWindow - (now - oldestCall);
console.log(`达到速率限制,等待 ${waitTime}ms`);
await this.sleep(waitTime);
return await this.rateLimitedAPICall(key, apiFunction, maxCalls, timeWindow);
}
// 记录这次调用
rateLimit.calls.push(now);
this.rateLimits.set(key, rateLimit);
try {
return await apiFunction();
} catch (error) {
// 调用失败时移除记录
rateLimit.calls.pop();
throw error;
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 清理缓存
clearCache(key) {
if (key) {
this.cache.delete(key);
} else {
this.cache.clear();
}
}
}
// 使用示例
const apiOptimizer = new APIOptimizer();
// 缓存地理位置数据
async function getCachedLocation() {
return await apiOptimizer.cachedAPICall(
'user-location',
() => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => resolve(position.coords),
(error) => reject(error),
{ maximumAge: 300000 } // 5分钟缓存
);
});
},
300000 // 5分钟TTL
);
}
// 限流的通知发送
async function sendRateLimitedNotification(title, options) {
return await apiOptimizer.rateLimitedAPICall(
'notifications',
() => {
return new Promise((resolve, reject) => {
const notification = new Notification(title, options);
notification.onshow = () => resolve(notification);
notification.onerror = () => reject(new Error('通知显示失败'));
});
},
3, // 每分钟最多3个通知
60000
);
}// 安全配置管理器
class SecurityManager {
constructor() {
this.permissions = new Map();
this.securityPolicies = {
clipboard: {
requireHTTPS: true,
requireUserGesture: true,
maxTextLength: 10000
},
notification: {
requireHTTPS: true,
requireUserGesture: true,
maxNotificationsPerMinute: 10
},
geolocation: {
requireHTTPS: true,
requireUserConsent: true,
maxAccuracy: 1000 // 米
}
};
}
// 检查安全策略
checkSecurityPolicy(apiName, options = {}) {
const policy = this.securityPolicies[apiName];
if (!policy) {
return { allowed: true, reason: '无安全策略' };
}
// 检查HTTPS要求
if (policy.requireHTTPS && !this.isHTTPS()) {
return { allowed: false, reason: '需要HTTPS连接' };
}
// 检查用户手势要求
if (policy.requireUserGesture && !this.hasUserGesture()) {
return { allowed: false, reason: '需要用户交互' };
}
// API特定检查
switch (apiName) {
case 'clipboard':
if (options.text && options.text.length > policy.maxTextLength) {
return { allowed: false, reason: '文本长度超出限制' };
}
break;
case 'notification':
const recentCount = this.getRecentNotificationCount();
if (recentCount >= policy.maxNotificationsPerMinute) {
return { allowed: false, reason: '通知频率过高' };
}
break;
case 'geolocation':
if (options.enableHighAccuracy && policy.maxAccuracy) {
// 可以添加更精确的位置权限控制
}
break;
}
return { allowed: true, reason: '通过安全检査' };
}
isHTTPS() {
return location.protocol === 'https:' || location.hostname === 'localhost';
}
hasUserGesture() {
// 在实际应用中,这需要与事件系统结合
// 这里简化处理,实际使用时需要在用户交互事件中调用API
return true; // 假设总是有用户手势
}
getRecentNotificationCount() {
const now = Date.now();
const oneMinuteAgo = now - 60000;
if (!this.recentNotifications) {
this.recentNotifications = [];
}
// 清理过期记录
this.recentNotifications = this.recentNotifications.filter(
timestamp => timestamp > oneMinuteAgo
);
return this.recentNotifications.length;
}
recordNotification() {
if (!this.recentNotifications) {
this.recentNotifications = [];
}
this.recentNotifications.push(Date.now());
}
// 安全的API包装器
async secureAPICall(apiName, apiFunction, options = {}) {
const securityCheck = this.checkSecurityPolicy(apiName, options);
if (!securityCheck.allowed) {
throw new Error(`安全检査失败: ${securityCheck.reason}`);
}
try {
// 记录敏感操作
this.logSecurityEvent(apiName, 'attempt', options);
const result = await apiFunction();
// 记录成功操作
this.logSecurityEvent(apiName, 'success', options);
// 特殊处理
if (apiName === 'notification') {
this.recordNotification();
}
return result;
} catch (error) {
// 记录失败操作
this.logSecurityEvent(apiName, 'error', { ...options, error: error.message });
throw error;
}
}
logSecurityEvent(apiName, action, details) {
const event = {
timestamp: new Date().toISOString(),
api: apiName,
action: action,
details: details,
userAgent: navigator.userAgent.substring(0, 100), // 限制长度
url: window.location.href
};
// 在实际应用中,这可能发送到安全监控服务
console.log('安全事件:', JSON.stringify(event));
// 可以添加额外的安全监控逻辑
if (action === 'error') {
this.handleSecurityError(event);
}
}
handleSecurityError(event) {
// 处理安全错误,例如:
// - 限制用户访问
// - 增加额外的验证
// - 通知管理员
console.warn('安全错误处理:', event);
}
}
// 创建安全管理器实例
const securityManager = new SecurityManager();
// 安全的通知发送
async function sendSecureNotification(title, options) {
try {
return await securityManager.secureAPICall(
'notification',
() => {
return new Promise((resolve, reject) => {
const notification = new Notification(title, options);
notification.onshow = () => resolve(notification);
notification.onerror = () => reject(new Error('通知显示失败'));
});
},
options
);
} catch (error) {
console.error('安全的发送通知失败:', error);
throw error;
}
}
// 安全的剪贴板操作
async function secureClipboardOperation(operation, data) {
try {
if (operation === 'writeText') {
return await securityManager.secureAPICall(
'clipboard',
() => navigator.clipboard.writeText(data),
{ text: data }
);
} else if (operation === 'readText') {
return await securityManager.secureAPICall(
'clipboard',
() => navigator.clipboard.readText()
);
}
} catch (error) {
console.error('安全的剪贴板操作失败:', error);
throw error;
}
}// 智能个人助理类
class SmartPersonalAssistant {
constructor() {
this.config = {
enableNotifications: true,
enableLocation: true,
enableBatteryMonitor: true,
maxNotifications: 5,
locationUpdateInterval: 60000 // 1分钟
};
this.services = {
notification: new NotificationManager(),
location: new LocationManager(),
battery: new BatteryManager(),
network: new NetworkManager(),
clipboard: new ClipboardManager()
};
this.init();
}
async init() {
console.log('🚀 初始化智能个人助理...');
// 检查权限
await this.checkPermissions();
// 初始化各个服务
await this.initializeServices();
// 设置事件监听
this.setupEventListeners();
console.log('✅ 智能个人助理初始化完成');
}
async checkPermissions() {
const permissions = [
{ name: 'notifications', service: 'notification' },
{ name: 'geolocation', service: 'location' },
{ name: 'battery', service: 'battery' }
];
for (const permission of permissions) {
try {
const result = await this.requestPermission(permission.name);
console.log(`${permission.name} 权限:`, result);
if (result === 'granted') {
this.config[`enable${permission.service.charAt(0).toUpperCase() + permission.service.slice(1)}`] = true;
}
} catch (error) {
console.error(`获取 ${permission.name} 权限失败:`, error);
}
}
}
async requestPermission(permissionName) {
switch (permissionName) {
case 'notifications':
return await Notification.requestPermission();
case 'geolocation':
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition(
() => resolve('granted'),
(error) => {
switch (error.code) {
case error.PERMISSION_DENIED:
resolve('denied');
break;
case error.POSITION_UNAVAILABLE:
case error.TIMEOUT:
resolve('prompt');
break;
}
}
);
});
case 'battery':
if ('getBattery' in navigator) {
try {
await navigator.getBattery();
return 'granted';
} catch (error) {
return 'denied';
}
}
return 'unsupported';
default:
return 'unsupported';
}
}
async initializeServices() {
// 初始化通知服务
if (this.config.enableNotifications) {
await this.services.notification.init();
}
// 初始化位置服务
if (this.config.enableLocation) {
await this.services.location.init();
}
// 初始化电池监控
if (this.config.enableBatteryMonitor) {
await this.services.battery.init();
}
// 初始化网络监控
await this.services.network.init();
// 初始化剪贴板服务
await this.services.clipboard.init();
}
setupEventListeners() {
// 页面可见性变化
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
console.log('页面隐藏,暂停非必要服务');
this.pauseServices();
} else {
console.log('页面可见,恢复服务');
this.resumeServices();
}
});
// 网络状态变化
window.addEventListener('online', () => {
console.log('网络连接恢复');
this.handleNetworkChange(true);
});
window.addEventListener('offline', () => {
console.log('网络连接断开');
this.handleNetworkChange(false);
});
}
pauseServices() {
this.services.location.pause();
this.services.battery.pause();
}
resumeServices() {
this.services.location.resume();
this.services.battery.resume();
}
handleNetworkChange(isOnline) {
if (isOnline) {
// 网络恢复时的处理
this.services.notification.send({
title: '网络连接恢复',
body: '您的网络连接已恢复,所有服务正常运行',
icon: '/icons/online.png'
});
} else {
// 网络断开时的处理
this.services.notification.send({
title: '网络连接断开',
body: '检测到网络连接断开,部分功能可能受限',
icon: '/icons/offline.png'
});
}
}
// 智能提醒功能
async scheduleSmartReminder(time, message, options = {}) {
const reminder = {
id: Date.now().toString(),
time: time,
message: message,
options: options,
created: new Date()
};
// 设置定时器
const delay = new Date(time) - new Date();
if (delay > 0) {
setTimeout(() => {
this.triggerReminder(reminder);
}, delay);
console.log(`智能提醒已设置: ${message} (${new Date(time).toLocaleString()})`);
}
}
async triggerReminder(reminder) {
await this.services.notification.send({
title: '智能提醒',
body: reminder.message,
icon: reminder.options.icon || '/icons/reminder.png',
requireInteraction: true,
actions: [
{ action: 'complete', title: '完成' },
{ action: 'snooze', title: '稍后提醒' }
]
});
// 处理用户响应
if (reminder.options.onComplete) {
reminder.options.onComplete();
}
}
// 位置相关提醒
async setupLocationReminder(location, message, radius = 100) {
if (!this.config.enableLocation) {
console.warn('位置服务未启用');
return;
}
const reminder = {
id: Date.now().toString(),
location: location,
message: message,
radius: radius,
created: new Date()
};
this.services.location.addLocationReminder(reminder);
console.log(`位置提醒已设置: ${message} (半径: ${radius}米)`);
}
// 电池优化提醒
async checkBatteryOptimization() {
if (!this.config.enableBatteryMonitor) {
return;
}
const batteryInfo = await this.services.battery.getInfo();
if (batteryInfo.level < 0.2 && !batteryInfo.charging) {
await this.services.notification.send({
title: '电池电量低',
body: `电池电量仅剩 ${Math.round(batteryInfo.level * 100)}%,建议充电`,
icon: '/icons/battery-low.png',
requireInteraction: true
});
}
if (batteryInfo.charging && batteryInfo.level > 0.8) {
await this.services.notification.send({
title: '电池即将充满',
body: `电池电量已达到 ${Math.round(batteryInfo.level * 100)}%`,
icon: '/icons/battery-full.png'
});
}
}
// 智能复制助手
async setupSmartClipboard() {
if (!compatManager.isSupported('clipboard')) {
console.warn('剪贴板API不支持');
return;
}
// 监听剪贴板变化(需要用户交互触发)
document.addEventListener('click', async () => {
try {
const text = await secureClipboardOperation('readText');
// 智能分析剪贴板内容
if (this.isURL(text)) {
console.log('检测到URL:', text);
// 可以自动打开预览或保存书签
} else if (this.isEmail(text)) {
console.log('检测到邮箱:', text);
// 可以自动添加到联系人
} else if (this.isPhoneNumber(text)) {
console.log('检测到电话号码:', text);
// 可以自动格式化或保存
}
} catch (error) {
console.error('读取剪贴板失败:', error);
}
});
}
isURL(text) {
const urlRegex = /^(https?:\\/\\/)?[\\w\\-]+(\\.[\\w\\-]+)+[/#?]?.*$/;
return urlRegex.test(text);
}
isEmail(text) {
const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
return emailRegex.test(text);
}
isPhoneNumber(text) {
const phoneRegex = /^[\\+]?[1-9][\\d]{0,15}$/;
return phoneRegex.test(text.replace(/[\\s\\-()]/g, ''));
}
// 获取系统状态报告
async getSystemStatus() {
const status = {
timestamp: new Date().toISOString(),
permissions: {
notifications: Notification.permission,
geolocation: await this.getGeolocationPermission()
},
services: {}
};
// 获取各个服务状态
if (this.config.enableLocation) {
status.services.location = await this.services.location.getStatus();
}
if (this.config.enableBatteryMonitor) {
status.services.battery = await this.services.battery.getStatus();
}
status.services.network = this.services.network.getStatus();
return status;
}
async getGeolocationPermission() {
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition(
() => resolve('granted'),
(error) => {
if (error.code === error.PERMISSION_DENIED) {
resolve('denied');
} else {
resolve('prompt');
}
}
);
});
}
}
// 各个服务管理器的简化实现
class NotificationManager {
constructor() {
this.notifications = [];
this.maxNotifications = 5;
}
async init() {
if (Notification.permission === 'default') {
await Notification.requestPermission();
}
}
async send(options) {
if (Notification.permission !== 'granted') {
console.warn('没有通知权限');
return;
}
// 限制通知数量
if (this.notifications.length >= this.maxNotifications) {
const oldest = this.notifications.shift();
oldest.close();
}
const notification = new Notification(options.title, {
body: options.body,
icon: options.icon,
requireInteraction: options.requireInteraction || false
});
this.notifications.push(notification);
notification.onclose = () => {
const index = this.notifications.indexOf(notification);
if (index > -1) {
this.notifications.splice(index, 1);
}
};
}
}
class LocationManager {
constructor() {
this.watchId = null;
this.locationReminders = [];
}
async init() {
// 初始化位置服务
}
pause() {
if (this.watchId) {
navigator.geolocation.clearWatch(this.watchId);
this.watchId = null;
}
}
resume() {
if (!this.watchId) {
this.watchId = navigator.geolocation.watchPosition(
(position) => this.handlePositionUpdate(position),
(error) => console.error('位置更新失败:', error)
);
}
}
addLocationReminder(reminder) {
this.locationReminders.push(reminder);
}
handlePositionUpdate(position) {
// 检查位置提醒
this.locationReminders.forEach(reminder => {
const distance = this.calculateDistance(
position.coords,
reminder.location
);
if (distance <= reminder.radius) {
this.triggerLocationReminder(reminder);
}
});
}
calculateDistance(coords1, coords2) {
// 简化的距离计算
const R = 6371e3; // 地球半径(米)
const φ1 = coords1.latitude * Math.PI / 180;
const φ2 = coords2.latitude * Math.PI / 180;
const Δφ = (coords2.latitude - coords1.latitude) * Math.PI / 180;
const Δλ = (coords2.longitude - coords1.longitude) * Math.PI / 180;
const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
const d = R * c; // 距离(米)
return d;
}
triggerLocationReminder(reminder) {
// 触发位置提醒
if (reminder.notification) {
new Notification(reminder.notification.title, {
body: reminder.notification.body,
icon: reminder.notification.icon
});
}
if (reminder.callback) {
reminder.callback();
}
}
async getStatus() {
return {
active: this.watchId !== null,
reminders: this.locationReminders.length
};
}
}
class BatteryManager {
constructor() {
this.battery = null;
this.listeners = [];
}
async init() {
if (!navigator.getBattery) {
return;
}
this.battery = await navigator.getBattery();
this.setupListeners();
}
setupListeners() {
this.battery.addEventListener('levelchange', () => {
this.notifyListeners('levelchange', {
level: this.battery.level,
charging: this.battery.charging
});
});
this.battery.addEventListener('chargingchange', () => {
this.notifyListeners('chargingchange', {
level: this.battery.level,
charging: this.battery.charging
});
});
}
addListener(callback) {
this.listeners.push(callback);
}
notifyListeners(event, data) {
this.listeners.forEach(callback => callback(event, data));
}
getStatus() {
if (!this.battery) {
return { supported: false };
}
return {
supported: true,
level: this.battery.level,
charging: this.battery.charging,
chargingTime: this.battery.chargingTime,
dischargingTime: this.battery.dischargingTime
};
}
}
class NetworkManager {
constructor() {
this.connection = null;
}
init() {
this.connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (this.connection) {
this.connection.addEventListener('change', () => {
console.log('网络状态变化:', this.getStatus());
});
}
}
getStatus() {
if (!this.connection) {
return { supported: false };
}
return {
supported: true,
effectiveType: this.connection.effectiveType,
downlink: this.connection.downlink,
rtt: this.connection.rtt,
saveData: this.connection.saveData
};
}
}
// 初始化智能个人助理
async function initSmartAssistant() {
const assistant = new SmartPersonalAssistant({
enableLocation: true,
enableBatteryMonitor: true,
enableNetworkOptimization: true,
enableClipboardAssistant: true
});
await assistant.init();
// 添加一些示例提醒
assistant.addSmartReminder({
id: 'office-arrival',
condition: 'location',
location: { latitude: 40.7128, longitude: -74.0060 }, // 示例坐标
radius: 100, // 100米范围
message: '已到达办公地点,记得打卡!',
notification: {
title: '位置提醒',
body: '已到达办公地点,记得打卡!',
icon: '/icons/location.png'
}
});
console.log('智能个人助理已启动');
return assistant;
}
// 使用示例
// initSmartAssistant().then(assistant => {
// console.log('助手已就绪');
// });
export { SmartPersonalAssistant, initSmartAssistant };// 浏览器兼容性检测
class CompatibilityManager {
constructor() {
this.supportedFeatures = new Map();
this.init();
}
init() {
this.checkAllFeatures();
}
checkAllFeatures() {
// Clipboard API
this.supportedFeatures.set('clipboard',
'clipboard' in navigator &&
'writeText' in navigator.clipboard &&
'readText' in navigator.clipboard
);
// Notification API
this.supportedFeatures.set('notification',
'Notification' in window
);
// Geolocation API
this.supportedFeatures.set('geolocation',
'geolocation' in navigator
);
// Battery API
this.supportedFeatures.set('battery',
'getBattery' in navigator
);
// Network Information API
this.supportedFeatures.set('network',
'connection' in navigator ||
'mozConnection' in navigator ||
'webkitConnection' in navigator
);
// Page Visibility API
this.supportedFeatures.set('visibility',
'hidden' in document &&
'visibilityState' in document
);
// Fullscreen API
this.supportedFeatures.set('fullscreen',
'requestFullscreen' in document.documentElement ||
'webkitRequestFullscreen' in document.documentElement ||
'mozRequestFullScreen' in document.documentElement ||
'msRequestFullscreen' in document.documentElement
);
// Service Worker
this.supportedFeatures.set('serviceWorker',
'serviceWorker' in navigator
);
// HTTPS检测
this.supportedFeatures.set('https',
location.protocol === 'https:' ||
location.hostname === 'localhost'
);
}
isSupported(feature) {
return this.supportedFeatures.get(feature) || false;
}
getSupportedFeatures() {
const supported = {};
for (const [feature, supported] of this.supportedFeatures) {
if (supported) {
supported[feature] = true;
}
}
return supported;
}
getUnsupportedFeatures() {
const unsupported = {};
for (const [feature, supported] of this.supportedFeatures) {
if (!supported) {
unsupported[feature] = false;
}
}
return unsupported;
}
// 获取浏览器信息
getBrowserInfo() {
const ua = navigator.userAgent;
let browser = 'Unknown';
let version = 'Unknown';
if (ua.indexOf('Chrome') > -1) {
browser = 'Chrome';
version = ua.match(/Chrome\/(\d+)/)[1];
} else if (ua.indexOf('Firefox') > -1) {
browser = 'Firefox';
version = ua.match(/Firefox\/(\d+)/)[1];
} else if (ua.indexOf('Safari') > -1) {
browser = 'Safari';
version = ua.match(/Version\/(\d+)/)[1];
} else if (ua.indexOf('Edge') > -1) {
browser = 'Edge';
version = ua.match(/Edge\/(\d+)/)[1];
}
return { browser, version, userAgent: ua };
}
// 生成兼容性报告
generateReport() {
const report = {
timestamp: new Date().toISOString(),
browser: this.getBrowserInfo(),
features: {
supported: this.getSupportedFeatures(),
unsupported: this.getUnsupportedFeatures()
},
recommendations: this.getRecommendations()
};
return report;
}
getRecommendations() {
const recommendations = [];
const unsupported = this.getUnsupportedFeatures();
if (unsupported.clipboard) {
recommendations.push({
feature: 'Clipboard API',
recommendation: '使用 document.execCommand 作为降级方案',
priority: 'high'
});
}
if (unsupported.notification) {
recommendations.push({
feature: 'Notification API',
recommendation: '使用页面内通知或模态框替代',
priority: 'medium'
});
}
if (unsupported.https) {
recommendations.push({
feature: 'HTTPS',
recommendation: '某些API需要HTTPS环境,考虑升级到HTTPS',
priority: 'high'
});
}
return recommendations;
}
}
// 使用示例
const compatManager = new CompatibilityManager();
console.log('兼容性报告:', compatManager.generateReport());// API降级处理器
class FallbackHandler {
constructor(compatibilityManager) {
this.compat = compatibilityManager;
}
// 剪贴板降级方案
async copyText(text) {
if (this.compat.isSupported('clipboard')) {
try {
await navigator.clipboard.writeText(text);
return { success: true, method: 'modern' };
} catch (error) {
console.warn('现代剪贴板API失败,尝试降级方案');
}
}
// 降级方案
return this.fallbackCopyText(text);
}
fallbackCopyText(text) {
const textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.left = '-999999px';
textArea.style.top = '-999999px';
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
let success = false;
try {
success = document.execCommand('copy');
} catch (error) {
console.error('降级复制失败:', error);
}
document.body.removeChild(textArea);
return { success, method: 'fallback' };
}
// 通知降级方案
showNotification(title, options = {}) {
if (this.compat.isSupported('notification') && Notification.permission === 'granted') {
try {
return new Notification(title, options);
} catch (error) {
console.warn('现代通知API失败,使用降级方案');
}
}
// 降级方案:使用页面内通知
return this.fallbackNotification(title, options);
}
fallbackNotification(title, options) {
// 创建页面内通知元素
const notification = document.createElement('div');
notification.className = 'fallback-notification';
notification.innerHTML = `
<div class="notification-content">
<h4>${title}</h4>
${options.body ? `<p>${options.body}</p>` : ''}
<button onclick="this.parentElement.parentElement.remove()">关闭</button>
</div>
`;
// 添加样式
const style = document.createElement('style');
style.textContent = `
.fallback-notification {
position: fixed;
top: 20px;
right: 20px;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
padding: 16px;
max-width: 300px;
z-index: 10000;
animation: slideIn 0.3s ease;
}
.notification-content h4 {
margin: 0 0 8px 0;
color: #333;
}
.notification-content p {
margin: 0 0 12px 0;
color: #666;
}
.notification-content button {
background: #007bff;
color: white;
border: none;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
}
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
`;
if (!document.querySelector('.fallback-notification-styles')) {
style.className = 'fallback-notification-styles';
document.head.appendChild(style);
}
document.body.appendChild(notification);
// 自动关闭
if (options.timeout !== false) {
setTimeout(() => {
if (notification.parentElement) {
notification.remove();
}
}, options.timeout || 5000);
}
return notification;
}
// 地理位置降级方案
getCurrentPosition(options = {}) {
return new Promise((resolve, reject) => {
if (this.compat.isSupported('geolocation')) {
navigator.geolocation.getCurrentPosition(resolve, reject, options);
} else {
reject(new Error('Geolocation API 不支持'));
}
});
}
}
// 使用示例
const fallbackHandler = new FallbackHandler(compatManager);
// 使用降级方案复制文本
fallbackHandler.copyText('要复制的文本').then(result => {
console.log('复制结果:', result);
});
// 使用降级方案显示通知
fallbackHandler.showNotification('标题', { body: '内容' });// API调用优化器
class APIOptimizer {
constructor() {
this.cache = new Map();
this.rateLimiters = new Map();
this.pendingRequests = new Map();
}
// 缓存包装器
withCache(key, fetcher, ttl = 60000) {
const cached = this.cache.get(key);
if (cached && Date.now() - cached.timestamp < ttl) {
return Promise.resolve(cached.data);
}
return fetcher().then(data => {
this.cache.set(key, {
data,
timestamp: Date.now()
});
return data;
});
}
// 限流包装器
withRateLimit(key, limit, window, fn) {
const limiter = this.getRateLimiter(key, limit, window);
return new Promise((resolve, reject) => {
if (limiter.tryConsume()) {
fn().then(resolve).catch(reject);
} else {
reject(new Error('Rate limit exceeded'));
}
});
}
getRateLimiter(key, limit, window) {
if (!this.rateLimiters.has(key)) {
this.rateLimiters.set(key, new RateLimiter(limit, window));
}
return this.rateLimiters.get(key);
}
// 防抖包装器
debounce(key, delay, fn) {
clearTimeout(this.debounceTimers?.[key]);
if (!this.debounceTimers) {
this.debounceTimers = {};
}
return new Promise((resolve) => {
this.debounceTimers[key] = setTimeout(() => {
resolve(fn());
}, delay);
});
}
// 请求去重
dedupe(key, fn) {
if (this.pendingRequests.has(key)) {
return this.pendingRequests.get(key);
}
const promise = fn().finally(() => {
this.pendingRequests.delete(key);
});
this.pendingRequests.set(key, promise);
return promise;
}
// 清理缓存
clearCache(pattern) {
if (pattern) {
for (const key of this.cache.keys()) {
if (key.includes(pattern)) {
this.cache.delete(key);
}
}
} else {
this.cache.clear();
}
}
}
// 限流器类
class RateLimiter {
constructor(limit, window) {
this.limit = limit;
this.window = window;
this.tokens = limit;
this.lastRefill = Date.now();
}
tryConsume() {
this.refill();
if (this.tokens >= 1) {
this.tokens--;
return true;
}
return false;
}
refill() {
const now = Date.now();
const timePassed = now - this.lastRefill;
const tokensToAdd = Math.floor((timePassed / this.window) * this.limit);
if (tokensToAdd > 0) {
this.tokens = Math.min(this.limit, this.tokens + tokensToAdd);
this.lastRefill = now;
}
}
}// 安全配置管理器
class SecurityManager {
constructor() {
this.config = {
requireHTTPS: true,
requireUserGesture: true,
allowedOrigins: [],
blockedOrigins: [],
maxNotifications: 10,
maxLocationAccuracy: 1000, // 米
enableLogging: true
};
}
// 检查HTTPS
checkHTTPS() {
if (this.config.requireHTTPS &&
location.protocol !== 'https:' &&
location.hostname !== 'localhost') {
throw new Error('此功能需要HTTPS环境');
}
return true;
}
// 检查用户手势
checkUserGesture() {
if (this.config.requireUserGesture) {
// 检查当前事件是否是用户手势触发的
return this.isUserGesture();
}
return true;
}
isUserGesture() {
// 简化的用户手势检测
// 在实际应用中,这需要更复杂的检测逻辑
return true; // 假设当前调用是用户手势触发的
}
// 验证来源
validateOrigin(origin) {
if (this.config.blockedOrigins.includes(origin)) {
throw new Error('来源被阻止');
}
if (this.config.allowedOrigins.length > 0 &&
!this.config.allowedOrigins.includes(origin)) {
throw new Error('来源不在允许列表中');
}
return true;
}
// 权限验证包装器
async withPermissionCheck(permission, fn) {
try {
this.checkHTTPS();
this.checkUserGesture();
if (permission) {
const result = await navigator.permissions.query({ name: permission });
if (result.state === 'denied') {
throw new Error(`权限被拒绝: ${permission}`);
}
}
return await fn();
} catch (error) {
this.logSecurityEvent('permission_denied', { permission, error: error.message });
throw error;
}
}
// 安全包装器
secureExecute(feature, fn) {
return this.withPermissionCheck(null, async () => {
try {
const result = await fn();
this.logSecurityEvent('feature_used', { feature, success: true });
return result;
} catch (error) {
this.logSecurityEvent('feature_error', { feature, error: error.message });
throw error;
}
});
}
// 日志记录
logSecurityEvent(event, data) {
if (this.config.enableLogging) {
console.log(`[Security] ${event}:`, data);
// 可以发送到远程日志服务
this.sendToLogService(event, data);
}
}
sendToLogService(event, data) {
// 发送到远程日志服务的逻辑
// fetch('/api/security-logs', { method: 'POST', body: JSON.stringify({ event, data }) });
}
// 更新配置
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
}
// 使用示例
const securityManager = new SecurityManager();
// 安全地使用剪贴板API
async function secureClipboardOperation(operation, data) {
return securityManager.secureExecute('clipboard', async () => {
switch (operation) {
case 'writeText':
return await navigator.clipboard.writeText(data);
case 'readText':
return await navigator.clipboard.readText();
default:
throw new Error('不支持的操作');
}
});
}Clipboard API:
Notification API:
Geolocation API:
// 调试工具
class APIDebugger {
constructor() {
this.logs = [];
this.isEnabled = false;
}
enable() {
this.isEnabled = true;
console.log('API调试器已启用');
}
disable() {
this.isEnabled = false;
}
log(api, operation, data) {
if (!this.isEnabled) return;
const logEntry = {
timestamp: new Date().toISOString(),
api,
operation,
data,
userAgent: navigator.userAgent,
url: window.location.href
};
this.logs.push(logEntry);
console.log(`[${api}] ${operation}:`, data);
// 保持日志数量限制
if (this.logs.length > 100) {
this.logs.shift();
}
}
exportLogs() {
return JSON.stringify(this.logs, null, 2);
}
clearLogs() {
this.logs = [];
}
}
// 全局调试器实例
window.apiDebugger = new APIDebugger();浏览器原生API为前端开发提供了强大的功能,让我们能够创建更加丰富和交互性强的Web应用。通过本文的学习,你应该已经掌握了:
记住,使用这些API时要始终以用户体验为中心,合理使用权限,提供有价值的功能。随着Web技术的不断发展,浏览器原生API会变得越来越强大,为我们的应用开发提供更多可能性。