ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

大计算别卡住页面:用 Web Worker 做本地文本统计 Demo,cpolar 给同事远程点验

大计算别卡住页面:用 Web Worker 做本地文本统计 Demo,cpolar 给同事远程点验 大计算别卡住页面用 Web Worker 做本地文本统计 Democpolar 给同事远程点验点击“开始统计”后页面上的按钮、进度条和状态文字还能不能正常响应这正是 Web Worker 最适合演示的场景把计算放到后台线程主线程继续负责页面交互。这篇不接上传、不读本机文件也不需要后端和数据库。我们用一段写死在页面里的虚构文本统计字符数、词频和关键词命中次数再用 cpolar 临时映射这个只读 Demo让同事用手机或另一台电脑点验。整个过程只开放本地静态页面验收结束后关闭隧道即可。1 Web Worker 到底解决什么问题浏览器页面里的 JavaScript 默认运行在主线程。主线程既要执行脚本也要处理点击、滚动、绘制和文字更新如果一段循环连续跑几秒页面就会出现按钮点不动、进度条不刷新甚至浏览器提示页面无响应。Web Worker 提供了一个独立的 JavaScript 执行上下文。页面通过postMessage()发送任务Worker 用postMessage()回传进度和结果二者不会共享 DOM。这个 Demo 里的分工很简单页面负责输入按钮和结果展示Worker 负责遍历文本。这里先用固定样例而不是把“读取任意文件”也塞进来。这样做不是偷懒而是为了把验收重点放在消息通信和页面响应上同时避免本机资料被带到公网地址。2 环境准备创建一个只读本地页面2.1 创建项目目录和三个文件准备一个空目录在其中创建index.html、worker.js和server.py。下面的命令只会创建目录和空文件不会接触其他路径mkdir -p ~/worker-text-demo cd ~/worker-text-demo touch index.html worker.js server.py把下面的页面代码写入index.html。页面中使用的是虚构的“星港开发周报”统计对象已经直接写在 HTML 里没有表单提交和网络请求。!doctype html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1 titleWorker 文本统计 Demo/title style :root { color-scheme: light; font-family: system-ui, sans-serif; } body { max-width: 760px; margin: 0 auto; padding: 24px; color: #172033; background: #f5f7fb; } .card { padding: 20px; border-radius: 16px; background: white; box-shadow: 0 8px 24px #17203318; } pre { white-space: pre-wrap; line-height: 1.7; padding: 14px; border-radius: 10px; background: #eef2f7; } button { border: 0; border-radius: 9px; padding: 10px 16px; margin-right: 8px; cursor: pointer; } #start { color: white; background: #2563eb; } #cancel { background: #e5e7eb; } progress { width: 100%; height: 18px; margin: 18px 0 8px; } .muted { color: #64748b; } .result { display: flex; gap: 12px; flex-wrap: wrap; } .result div { min-width: 130px; padding: 12px; border-radius: 10px; background: #eff6ff; } /style /head body div classcard h1Worker 文本统计 Demo/h1 p classmuted固定虚构文本 · 不上传 · 不保存/p pre idsource星港开发周报本周完成本地搜索组件的性能实验。 测试组把统计任务放进 Worker页面在计算时仍能响应按钮。 下一轮将继续观察进度反馈并整理可复用的前端示例。/pre button idstart开始统计/buttonbutton idcancel disabled取消任务/button progress idprogress value0 max100/progress p idstatus等待开始/p div idresult classresult/div /div script const start document.querySelector(#start); const cancel document.querySelector(#cancel); const progress document.querySelector(#progress); const status document.querySelector(#status); const result document.querySelector(#result); let worker; function showResult(data) { result.innerHTML div字符数b${data.characters}/b/div div词数b${data.words}/b/div div“Worker”次数b${data.workerHits}/b/div; } start.onclick () { worker?.terminate(); result.innerHTML ; progress.value 0; status.textContent 计算中页面仍可点击取消; start.disabled true; cancel.disabled false; worker new Worker(./worker.js); worker.onmessage ({ data }) { if (data.type progress) { progress.value data.value; status.textContent 计算进度 ${data.value}%; } else if (data.type done) { showResult(data); status.textContent 统计完成; start.disabled false; cancel.disabled true; worker.terminate(); worker null; } }; worker.postMessage({ text: document.querySelector(#source).textContent }); }; cancel.onclick () { worker?.terminate(); worker null; status.textContent 任务已取消可以重新开始; start.disabled false; cancel.disabled true; }; /script /body /html再把计算逻辑写入worker.jsself.onmessage ({ data }) { const text data.text; let index 0; let characters 0; let words 0; let workerHits 0; function step() { const end Math.min(index 2, text.length); for (; index end; index 1) { const char text[index]; characters 1; if (/\S/.test(char)) words 1; if (text.slice(index, index 6) Worker) workerHits 1; } self.postMessage({ type: progress, value: Math.round(index / text.length * 100) }); if (index text.length) setTimeout(step, 35); else self.postMessage({ type: done, characters, words, workerHits }); } step(); };代码把每轮处理量设成 2 个字符并用setTimeout让出执行权所以进度变化肉眼可见。真实项目中可以按数据量调大批次这里故意保留慢一点的演示节奏方便同事观察状态变化。words在这个中文 Demo 中表示非空白字符数量避免引入分词库后把主题带偏。2.2 用 Python 启动本地静态服务浏览器直接双击 HTML 时Worker 的加载会受到file://安全策略影响。用 Python 标准库启动静态服务更稳也不需要安装第三方包# server.py from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler HOST 127.0.0.1 PORT 8000 if __name__ __main__: server ThreadingHTTPServer((HOST, PORT), SimpleHTTPRequestHandler) print(fDemo running at http://{HOST}:{PORT}) server.serve_forever()在项目目录执行python3 server.py终端出现Demo running at http://127.0.0.1:8000后打开http://127.0.0.1:8000。点击“开始统计”进度条应从 0 走到 100%结果区显示三个数字统计期间点击“取消任务”状态应变成“任务已取消”。如果页面能打开但按钮没有反应先按浏览器开发者工具里的 Console 报错检查文件名尤其确认worker.js和index.html在同一目录。不要先怀疑网络因为此时链路还没有离开本机。3 看懂主线程与 Worker 的消息链路这段 Demo 最值得保留的不是样式而是三处边界new Worker(./worker.js)创建后台线程页面不把 DOM 传给 Worker。worker.postMessage({ text })发送普通字符串Worker 收到后开始计算。worker.onmessage只处理进度和最终结果页面更新集中在主线程完成。取消按钮调用terminate()直接结束当前 Worker。由于本例没有持久化和服务端任务取消后不会留下半成品数据重新点击开始时创建一个新的 Worker状态也会从头计算。如果把大循环直接写在start.onclick里页面会一直忙于计算进度更新要等循环结束才集中出现。把计算搬到 Worker 后主线程有机会处理点击和绘制这就是“计算不阻塞页面”的可见差异。4 用 cpolar 临时给同事点验本地确认通过后再做远程验收。cpolar 在这里负责把127.0.0.1:8000这个临时 Demo 映射成 HTTP 地址不负责账号系统、数据存储或业务接口。验收结束关闭命令即可不把它当成长期部署方案。4.1 安装并绑定 cpolar请从官方入口下载对应平台版本download下载 - cpolar 极点云官网。macOS 可按官方安装文档操作Mac OS安装cpolar内网穿透 - cpolar 极点云官网Linux 可参考linux安装cpolar内网穿透 - cpolar 极点云官网。完成安装后先让本机服务运行再检查 cpolar 的本地管理页面cpolar version curl -s http://127.0.0.1:9200需要手动绑定时登录 cpolar 后台进入“验证”页面获取 Authtoken再执行cpolar authtoken YOUR_AUTHTOKEN这里的YOUR_AUTHTOKEN只是命令参数说明实际执行时必须替换成后台复制的真实 token不要把 token 写进文章、截图或发给同事。4.2 创建临时 HTTP 隧道保持 Python 服务和 cpolar 在两个终端窗口运行在第二个窗口执行cpolar http 8000终端会显示一条公网访问地址。把这条地址发给验收同事并约定只检查这个演示页面。免费随机地址会变化本次只做短时验收不需要固定地址。验收时让同事完成三项检查页面能打开看到“固定虚构文本”和“不上传·不保存”提示。点击开始后进度条变化状态文字能更新取消按钮能响应。统计完成后结果区出现字符数、词数和Worker命中次数。如果本机访问正常、远程地址打不开先检查 cpolar 终端是否仍在运行以及命令中的端口是否为8000。如果远程页面打开但 Worker 报错检查浏览器是否把worker.js请求到了同一个公网地址不要把 Worker 改成读本机绝对路径。5 总结这个 Demo 已经完成了一条干净的验证链路页面使用虚构文本Worker 在后台线程分批统计主线程展示进度并支持取消本地服务只监听127.0.0.1:8000远程验收时才临时通过 cpolar 提供 HTTP 入口。用postMessage()传递任务和结果Worker 不直接操作 DOM。用terminate()实现取消重新开始时创建新的 Worker。用cpolar http 8000做短时点验验收完成后关闭隧道和本地服务。如果你要把它用于真实业务先补上输入规模限制、错误处理和浏览器兼容性检测再考虑更复杂的任务队列。只是在局域网里自己演示时cpolar 这一步可以跳过只有确实需要让异地同事点击验收才临时开启隧道范围更小也更容易收口。
返回列表