// ==UserScript==
// @name 切换搜索引擎
// @description 在搜索引擎之间快速切换搜索内容
// @author Jack back
// @namespace search-engine-switcher
// @license GPL-3.0
// @include https://www.baidu.com/*
// @include *.bing.com/*
// @include /^https?://[a-z]+\.google\.[a-z,\.]+/.+$/
// @include https://www.zhihu.com/search*
// @include https://www.bilibili.com/search*
// @include https://www.xiaohongshu.com/search*
// @include https://www.youtube.com/results*
// @include https://metaso.cn/*
// @run-at document_body
// @version 1.1.3
// ==/UserScript==
(function () {
'use strict';
let sites = [
{
name: "百度",
host: "baidu.com",
link: "https://www.baidu.com/s",
key: "wd",
hide: false,
},
{
name: "必应",
host: "bing.com",
link: "https://bing.com/search",
key: "q",
hide: false,
},
{
name: "谷歌",
host: "google.com",
link: "https://www.google.com.hk/search",
key: "q",
hide: false,
},
{
name: "知乎",
host: "zhihu.com",
link: "https://www.zhihu.com/search",
key: "q",
hide: false,
},
{
name: "B站",
host: "bilibili.com",
link: "https://www.bilibili.com/search",
key: "keyword",
hide: false,
},
{
name: "小红书",
host: "xiaohongshu.com",
link: "https://www.xiaohongshu.com/search",
key: "keyword",
hide: false,
},
{
name: "YouTube",
host: "youtube.com",
link: "https://www.youtube.com/results",
key: "search_query",
hide: false,
},
{
name: "秘塔",
host: "metaso.cn",
link: "https://metaso.cn/",
key: "q",
hide: false,
},
{
name: "GitHub",
host: "github.com",
link: "https://github.com/search",
key: "q",
hide: false,
},
];
const css = `
/* 全局字体优化 */
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
font-family: -apple-system, BlinkMacSystemFont,
"Segoe UI", "PingFang SC", "Microsoft YaHei",
sans-serif;
}
.search-switcher {
position: fixed;
opacity: 0.12;
top: 50%;
transform: translateY(-50%);
left: -100px;
z-index: 9999999;
transition: all 800ms cubic-bezier(0.19, 1, 0.22, 1);
filter: drop-shadow(0 0 20px rgba(0, 0, 0, 0.2));
}
.search-switcher:hover {
left: 0;
opacity: 1;
filter: drop-shadow(0 0 30px rgba(0, 0, 0, 0.25));
}
.search-list {
display: flex;
flex-direction: column;
gap: 7px;
background: rgba(23, 23, 33, 0.92);
backdrop-filter: blur(20px) saturate(180%) brightness(95%);
-webkit-backdrop-filter: blur(20px) saturate(180%) brightness(95%);
border-radius: 0 18px 18px 0;
padding: 12px 10px;
box-shadow:
0 4px 24px -1px rgba(0, 0, 0, 0.25),
0 0 0 1px rgba(255, 255, 255, 0.1) inset,
0 0 0 1px rgba(255, 255, 255, 0.05);
width: 100px;
position: relative;
overflow: hidden;
}
.search-list::before {
content: '';
position: absolute;
inset: 0;
background:
linear-gradient(
135deg,
rgba(255, 255, 255, 0.03) 0%,
transparent 50%
),
radial-gradient(
circle at top right,
rgba(255, 255, 255, 0.12),
transparent 80%
);
z-index: 0;
}
.search-list a {
color: rgba(255, 255, 255, 0.85);
text-decoration: none;
padding: 9px 14px;
border-radius: 9px;
transition: all 500ms cubic-bezier(0.19, 1, 0.22, 1);
font-size: 13px;
font-weight: 600;
letter-spacing: 0.3px;
text-align: center;
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
position: relative;
overflow: hidden;
z-index: 1;
backdrop-filter: blur(4px);
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
.search-list a:hover {
color: #ffffff;
background: rgba(255, 255, 255, 0.1);
transform: translateX(3px) scale(1.02);
letter-spacing: 0.5px;
text-shadow: 0 2px 20px rgba(255, 255, 255, 0.4);
border-color: rgba(255, 255, 255, 0.2);
box-shadow:
0 4px 20px -2px rgba(0, 0, 0, 0.3),
0 0 0 1px rgba(255, 255, 255, 0.15) inset,
0 0 20px rgba(255, 255, 255, 0.06);
}
@keyframes glow {
0% {
box-shadow: 0 0 5px rgba(255, 255, 255, 0.1);
}
50% {
box-shadow: 0 0 20px rgba(255, 255, 255, 0.2);
}
100% {
box-shadow: 0 0 5px rgba(255, 255, 255, 0.1);
}
}
.search-switcher:hover .search-list {
animation: glow 2s infinite;
}
@media (prefers-reduced-motion) {
.search-switcher,
.search-list a {
transition: none;
}
}
@supports not (backdrop-filter: blur(12px)) {
.search-list {
background: rgba(28, 28, 35, 0.95);
}
}
/* 齿轮图标样式 */
.settings-gear {
width: 20px;
height: 20px;
padding: 4px;
margin: 4px auto 0;
cursor: pointer;
opacity: 0.6;
transition: all 0.3s ease;
}
.settings-gear:hover {
opacity: 1;
transform: rotate(45deg);
}
/* 弹窗样式优化 */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(8px) saturate(180%);
-webkit-backdrop-filter: blur(8px) saturate(180%);
display: none;
justify-content: center;
align-items: center;
z-index: 10000000;
animation: modalFadeIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
@keyframes modalFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background: linear-gradient(
135deg,
rgba(35, 35, 45, 0.85) 0%,
rgba(23, 23, 33, 0.9) 100%
);
backdrop-filter: blur(25px) saturate(180%);
padding: 32px;
border-radius: 24px;
min-width: 360px;
box-shadow:
0 20px 60px -10px rgba(0, 0, 0, 0.4),
0 0 0 1px rgba(255, 255, 255, 0.1) inset,
0 0 0 1px rgba(255, 255, 255, 0.05);
transform: scale(0.95);
animation: modalPop 0.6s cubic-bezier(0.19, 1, 0.22, 1) forwards;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.modal-buttons {
display: flex;
gap: 12px;
margin-bottom: 20px;
}
.modal-btn {
flex: 1;
padding: 13px 20px;
border: none;
border-radius: 12px;
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.08),
rgba(255, 255, 255, 0.03)
);
box-shadow:
0 2px 5px rgba(0, 0, 0, 0.1),
0 0 0 1px rgba(255, 255, 255, 0.06) inset;
color: white;
font-weight: 600;
cursor: pointer;
transition: all 500ms cubic-bezier(0.19, 1, 0.22, 1);
position: relative;
overflow: hidden;
backdrop-filter: blur(4px);
font-size: 13.5px;
letter-spacing: 0.3px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
}
.modal-btn:hover {
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.12),
rgba(255, 255, 255, 0.06)
);
transform: translateY(-2px);
box-shadow:
0 8px 25px -5px rgba(0, 0, 0, 0.3),
0 0 0 1px rgba(255, 255, 255, 0.1) inset;
}
.modal-btn:active {
transform: translateY(0);
}
.add-form {
display: flex;
flex-direction: column;
gap: 15px;
animation: formSlideIn 0.3s ease-out;
}
@keyframes formSlideIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.add-form > div:first-child {
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
margin-bottom: 5px;
font-weight: 550;
letter-spacing: 0.2px;
text-shadow: 0 0 1px rgba(255, 255, 255, 0.1);
}
/* 设置表单标签样式 */
.add-form > div {
color: #ffffff;
font-size: 16px;
margin-bottom: 5px;
font-weight: 700;
letter-spacing: 0.3px;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
.add-form input {
padding: 14px 18px;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
background: rgba(0, 0, 0, 0.2);
color: white;
font-size: 13.5px;
font-weight: 500;
letter-spacing: 0.3px;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
width: 100%;
backdrop-filter: blur(4px);
box-shadow:
0 2px 5px rgba(0, 0, 0, 0.1),
0 0 0 1px rgba(255, 255, 255, 0.05) inset;
}
.add-form input:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.2);
box-shadow:
0 0 0 3px rgba(255, 255, 255, 0.1),
0 0 30px rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.25);
}
.add-form input::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.delete-list {
max-height: 300px;
overflow-y: auto;
margin-top: 10px;
padding-right: 10px;
}
.delete-list::-webkit-scrollbar {
width: 6px;
}
.delete-list::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
border-radius: 3px;
}
.delete-list::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.2);
border-radius: 3px;
}
.delete-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px;
border-radius: 14px;
margin-bottom: 8px;
background: rgba(255, 255, 255, 0.04);
transition: all 500ms cubic-bezier(0.19, 1, 0.22, 1);
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
}
.delete-item:hover {
background: rgba(255, 255, 255, 0.06);
transform: translateX(3px);
border-color: rgba(255, 255, 255, 0.1);
box-shadow:
0 4px 15px rgba(0, 0, 0, 0.1),
0 0 0 1px rgba(255, 255, 255, 0.08) inset;
}
.delete-item span {
color: rgba(255, 255, 255, 0.9);
font-size: 13.5px;
font-weight: 500;
letter-spacing: 0.2px;
text-shadow: 0 0 1px rgba(255, 255, 255, 0.1);
}
.delete-btn {
padding: 7px 14px;
background: linear-gradient(
135deg,
rgba(255, 59, 48, 0.12),
rgba(255, 59, 48, 0.08)
);
color: #ff3b30;
border: 1px solid rgba(255, 59, 48, 0.15);
border-radius: 10px;
cursor: pointer;
font-size: 13px;
font-weight: 600;
letter-spacing: 0.2px;
transition: all 500ms cubic-bezier(0.19, 1, 0.22, 1);
backdrop-filter: blur(4px);
box-shadow:
0 2px 5px rgba(255, 59, 48, 0.1),
0 0 0 1px rgba(255, 59, 48, 0.05) inset;
}
.delete-btn:hover {
background: linear-gradient(
135deg,
rgba(255, 59, 48, 0.18),
rgba(255, 59, 48, 0.12)
);
transform: translateX(2px);
box-shadow:
0 4px 15px rgba(255, 59, 48, 0.15),
0 0 0 1px rgba(255, 59, 48, 0.1) inset;
}
#confirmAdd {
margin-top: 15px;
padding: 12px;
width: 100%;
background: rgba(255, 255, 255, 0.15);
color: white;
border: none;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
#confirmAdd:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(255, 255, 255, 0.4);
}
#confirmAdd:active {
transform: translateY(0);
}
.settings-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.settings-section h3 {
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
margin-bottom: 15px;
font-weight: 500;
}
`;
// 添加本地存储功能
function saveCustomSites() {
try {
localStorage.setItem('customSites', JSON.stringify(sites));
return true;
} catch (e) {
console.error('保存站点数据失败:', e);
return false;
}
}
function loadCustomSites() {
try {
const saved = localStorage.getItem('customSites');
if (saved) {
try {
const loadedSites = JSON.parse(saved);
if (Array.isArray(loadedSites) && loadedSites.length > 0) {
// 验证站点数据的有效性
const validSites = loadedSites.filter(site =>
site &&
typeof site === 'object' &&
site.name &&
site.host &&
site.link &&
site.key !== undefined
);
if (validSites.length > 0) {
sites = validSites;
console.log(`成功加载${validSites.length}个自定义站点`);
return true;
} else {
console.warn('加载的站点数据无效');
}
}
} catch (e) {
console.error('无法解析保存的站点数据', e);
}
}
return false;
} catch (e) {
console.error('读取本地存储失败:', e);
return false;
}
}
// 修改创建弹窗的HTML结构
function createSettingsModal() {
const modal = document.createElement('div');
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal-content">
<div class="modal-buttons">
<button class="modal-btn" id="addSiteBtn">添加站点</button>
<button class="modal-btn" id="deleteSiteBtn">管理站点</button>
<button class="modal-btn" id="closeModalBtn">关闭</button>
</div>
<div id="settingsContainer"></div>
</div>
`;
document.body.appendChild(modal);
// 绑定按钮事件
document.getElementById('addSiteBtn').addEventListener('click', () => {
const container = document.getElementById('settingsContainer');
container.innerHTML = `
<div class="add-form">
<div>搜索URL解析</div>
<input type="text" id="searchUrl" placeholder="输入完整搜索URL自动识别">
<button class="modal-btn" id="parseUrlBtn">解析URL</button>
<div style="margin-top:15px">站点名称</div>
<input type="text" id="siteName" placeholder="例如: 百度">
<div>站点域名</div>
<input type="text" id="siteHost" placeholder="例如: baidu.com">
<div>搜索链接</div>
<input type="text" id="siteLink" placeholder="例如: https://www.baidu.com/s">
<div>搜索参数</div>
<input type="text" id="siteKey" placeholder="例如: wd">
<button id="confirmAdd" class="modal-btn">添加</button>
</div>
`;
document.getElementById('confirmAdd').addEventListener('click', () => {
const name = document.getElementById('siteName').value;
const host = document.getElementById('siteHost').value;
const link = document.getElementById('siteLink').value;
const key = document.getElementById('siteKey').value;
if (name && host && link && key) {
addNewSite(name, host, link, key);
alert('添加成功');
container.innerHTML = '';
} else {
alert('请填写完整信息');
}
});
document.getElementById('parseUrlBtn').addEventListener('click', () => {
const url = document.getElementById('searchUrl').value;
if (url) {
const parsedData = parseSearchUrl(url);
if (parsedData) {
document.getElementById('siteName').value = parsedData.name || '';
document.getElementById('siteHost').value = parsedData.host || '';
document.getElementById('siteLink').value = parsedData.link || '';
document.getElementById('siteKey').value = parsedData.key || '';
} else {
alert('无法解析该URL,请确保是有效的搜索URL');
}
} else {
alert('请输入URL');
}
});
});
document.getElementById('deleteSiteBtn').addEventListener('click', () => {
const container = document.getElementById('settingsContainer');
showDeleteList(container);
});
document.getElementById('closeModalBtn').addEventListener('click', () => {
modal.style.display = 'none';
});
return modal;
}
function setup() {
// 添加CSS样式
const styleElement = document.createElement('style');
styleElement.textContent = css;
document.head.appendChild(styleElement);
// 创建搜索引擎切换器
const switcherDiv = document.createElement('div');
switcherDiv.className = 'search-switcher';
// 创建搜索引擎列表
const listDiv = document.createElement('div');
listDiv.className = 'search-list';
// 获取当前搜索词
const searchTerm = getCurrentSearchTerm();
// 填充搜索引擎列表
sites.filter(site => !site.hide).forEach(site => {
const link = document.createElement('a');
link.href = `${site.link}?${site.key}=${encodeURIComponent(searchTerm)}`;
link.textContent = site.name;
link.target = '_blank';
link.rel = 'noopener noreferrer';
listDiv.appendChild(link);
});
// 添加设置图标
const settingsIcon = document.createElement('div');
settingsIcon.className = 'settings-gear';
settingsIcon.innerHTML = `
<svg viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" />
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1Z" />
</svg>
`;
// 创建并设置模态框
const modal = createSettingsModal();
// 点击设置图标显示模态框
settingsIcon.addEventListener('click', () => {
showSettingsModal(modal);
});
listDiv.appendChild(settingsIcon);
switcherDiv.appendChild(listDiv);
document.body.appendChild(switcherDiv);
}
// 添加设置相关功能
function showSettingsModal(modal) {
modal.style.display = 'flex';
}
function addNewSite(name, host, link, key) {
// 输入验证
if (!name || typeof name !== 'string' || name.trim() === '') {
alert('站点名称不能为空');
return false;
}
if (!host || typeof host !== 'string' || host.trim() === '') {
alert('站点域名不能为空');
return false;
}
if (!link || typeof link !== 'string' || !link.startsWith('http')) {
alert('搜索链接必须是有效的URL');
return false;
}
if (!key || typeof key !== 'string' || key.trim() === '') {
alert('搜索参数不能为空');
return false;
}
// 检查是否已存在相同站点
const existingSite = sites.findIndex(s => s.host === host);
if (existingSite !== -1) {
const confirmReplace = confirm(`已存在域名为 ${host} 的站点,是否替换?`);
if (confirmReplace) {
sites[existingSite] = {
name: name.trim(),
host: host.trim(),
link: link.trim(),
key: key.trim(),
hide: false
};
saveCustomSites();
return true;
} else {
return false;
}
}
const newSite = {
name: name.trim(),
host: host.trim(),
link: link.trim(),
key: key.trim(),
hide: false
};
sites.push(newSite);
return saveCustomSites();
}
function showDeleteList(container) {
container.innerHTML = '<div class="delete-list"></div>';
const deleteList = container.querySelector('.delete-list');
sites.forEach((site, index) => {
const item = document.createElement('div');
item.className = 'delete-item';
item.innerHTML = `
<span>${site.name} (${site.host})</span>
<button class="delete-btn" data-index="${index}">移除</button>
`;
deleteList.appendChild(item);
});
// 添加删除事件监听器
const deleteButtons = container.querySelectorAll('.delete-btn');
deleteButtons.forEach(btn => {
btn.addEventListener('click', function() {
const index = parseInt(this.getAttribute('data-index'));
sites.splice(index, 1);
saveCustomSites();
showDeleteList(container); // 刷新删除列表
});
});
}
// 获取当前搜索关键词
function getCurrentSearchTerm() {
try {
const currentHost = window.location.hostname;
const site = sites.find(s => currentHost.includes(s.host));
if (!site) return '';
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(site.key) || '';
} catch (error) {
console.error('获取搜索关键词时出错:', error);
return '';
}
}
// 解析搜索URL的函数
function parseSearchUrl(url) {
if (!url || typeof url !== 'string') {
console.error('URL不是有效的字符串');
return null;
}
try {
const urlObj = new URL(url);
const searchParams = new URLSearchParams(urlObj.search);
// 提取域名作为host
const hostParts = urlObj.hostname.split('.');
let host = hostParts.length >= 2 ?
`${hostParts[hostParts.length-2]}.${hostParts[hostParts.length-1]}` :
urlObj.hostname;
// 尝试找出搜索参数
let searchKey = '';
let searchValue = '';
// 常见搜索参数名列表
const commonSearchParams = ['q', 'query', 'search', 'keyword', 'keywords', 'wd', 'kw', 'search_query', 'term', 'text'];
// 首先检查常见参数
for (const param of commonSearchParams) {
if (searchParams.has(param)) {
searchKey = param;
searchValue = searchParams.get(param);
break;
}
}
// 如果没有找到常见参数,尝试找第一个非空的参数
if (!searchKey) {
for (const [key, value] of searchParams.entries()) {
if (value && value.length > 0) {
searchKey = key;
searchValue = value;
break;
}
}
}
if (!searchKey) {
console.warn('无法在URL中找到搜索参数');
return null;
}
// 基础链接不包括查询参数
const baseLink = `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;
// 猜测网站名称
let siteName = hostParts[0];
if (hostParts.length > 2 && hostParts[0] !== 'www') {
siteName = hostParts[0].charAt(0).toUpperCase() + hostParts[0].slice(1);
} else if (hostParts.length > 2 && hostParts[0] === 'www') {
siteName = hostParts[1].charAt(0).toUpperCase() + hostParts[1].slice(1);
} else {
siteName = host.split('.')[0].charAt(0).toUpperCase() + host.split('.')[0].slice(1);
}
return {
name: siteName,
host: host,
link: baseLink,
key: searchKey
};
} catch (e) {
console.error('解析URL失败:', e);
return null;
}
}
// 初始化时加载自定义站点
loadCustomSites();
// 监听 pushState 和 replaceState 方法
const originalPushState = history.pushState;
const originalReplaceState = history.replaceState;
history.pushState = function() {
originalPushState.apply(this, arguments);
window.dispatchEvent(new Event('urlChange'));
};
history.replaceState = function() {
originalReplaceState.apply(this, arguments);
window.dispatchEvent(new Event('urlChange'));
};
// 监听 popstate 事件(用于处理浏览器后退和前进)
window.addEventListener('popstate', () => {
window.dispatchEvent(new Event('urlChange'));
});
// 自定义的 URL 变化事件处理函数
const handleUrlChange = () => {
// 移除旧的搜索切换器
const oldSwitcher = document.querySelector('.search-switcher');
if (oldSwitcher) {
oldSwitcher.remove();
}
// 如果当前页面是搜索页面,则重新创建搜索切换器
const currentHost = window.location.hostname;
const isSearchPage = sites.some(site =>
currentHost.includes(site.host) &&
window.location.search.includes(site.key)
);
if (isSearchPage) {
setTimeout(setup, 500); // 延迟执行以确保DOM已更新
}
};
// 监听自定义的 urlChange 事件
window.addEventListener('urlChange', handleUrlChange);
// 初始加载时也触发一次
handleUrlChange();
})();