如何检测脚本文件是被加载执行还是在浏览器标签页打开查看?
可行方案:用Service Worker区分脚本加载场景并实现语法高亮渲染
完全可以实现你的需求,核心思路是通过Service Worker拦截JS文件的请求,根据请求的特征区分「直接在标签页打开查看」和「作为脚本加载执行」两种场景,再分别处理。
一、怎么区分两种场景?
浏览器在不同场景下请求JS文件时,会带上不同的请求特征,你可以在Service Worker的fetch事件里捕捉这些差异:
- 直接打开查看:请求的
mode为navigate,同时Accept请求头会包含text/html(因为浏览器期望把文件渲染成可阅读的页面)。 - 作为脚本执行:请求的
mode是no-cors或cors(取决于脚本跨域情况),Accept头会是application/javascript或包含module(ES模块的情况),且这类请求属于子资源加载,不是页面导航。
对应的判断代码示例:
self.addEventListener('fetch', (event) => { const req = event.request; // 先过滤出JS文件请求 if (req.url.match(/\.js(\?.*)?$/)) { // 判断是否是直接打开的场景 const isDirectView = req.mode === 'navigate' || req.headers.get('Accept')?.includes('text/html'); if (isDirectView) { // 转成带语法高亮的HTML返回 event.respondWith(renderHighlightedHtml(req)); } else { // 正常返回原始脚本,供执行使用 event.respondWith(fetch(req)); } } });
二、实现语法高亮的HTML渲染
当判断是直接打开的场景时,你需要:
- 获取原始JS文件的内容;
- 对代码进行语法高亮处理(可以自己写轻量的正则匹配,也可以嵌入小型的高亮逻辑,不用依赖外部库);
- 构造完整的HTML页面返回给浏览器。
示例实现:
async function renderHighlightedHtml(request) { // 拉取原始JS内容 const jsRes = await fetch(request); const jsCode = await jsRes.text(); // 简单的语法高亮处理(可根据需求扩展规则) const highlightedCode = jsCode // 高亮关键字 .replace(/(\bfunction\b|\bconst\b|\blet\b|\bvar\b|\bif\b|\belse\b|\breturn\b|\basync\b|\bawait\b)/g, '<span style="color: #0066cc;">$1</span>') // 高亮字符串 .replace(/(".*?"|'.*?')/g, '<span style="color: #009900;">$1</span>') // 高亮单行注释 .replace(/(\/\/.*)/g, '<span style="color: #999999;">$1</span>') // 高亮多行注释 .replace(/(\/\*[\s\S]*?\*\/)/g, '<span style="color: #999999;">$1</span>'); // 构造完整的HTML页面 const htmlPage = ` <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>${new URL(request.url).pathname.split('/').pop()}</title> <style> body { margin: 0; padding: 2rem; font-family: 'Consolas', 'Monaco', monospace; background: #f5f5f5; } pre { white-space: pre-wrap; line-height: 1.6; background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } </style> </head> <body> <pre>${highlightedCode}</pre> </body> </html> `; // 返回HTML响应 return new Response(htmlPage, { headers: { 'Content-Type': 'text/html; charset=utf-8' } }); }
三、需要注意的细节
- Service Worker作用域:注册Service Worker时,要确保作用域覆盖所有JS文件的路径,比如根目录下的JS文件,注册时用
navigator.serviceWorker.register('/sw.js', { scope: '/' })。 - 缓存逻辑:如果需要缓存JS文件,可在「作为脚本执行」的分支里加入缓存策略,但要注意更新机制,避免用户拿到过期的脚本。
- 兼容性:现代主流浏览器都支持Service Worker,若要兼容旧浏览器,可以在页面里做降级提示。
- ES模块适配:ES模块的请求
Accept头会包含module,同样会被归类到「执行」场景,直接返回原始内容即可,无需处理。
内容的提问来源于stack exchange,提问作者jcubic
相关产品推荐
相关产品推荐

