
前阵子我给一套 WPS JS 加载项做升级部署功能区按钮新增了两个结果产品同学过来说点 A 按钮打开的 Pane 里显示的是 B 的功能页面。我第一反应是入口页面路由写错了可开发环境点得好好的怎么一部署就串台。排查下来才发现问题根本不在我写的代码里而在加载项部署时的一份 manifest 配置文件上。这篇博文围绕 WPS 加载项部署后点击按钮打开的任务窗格Pane页面不对这个问题展开适合正在用 WPS JS 加载项做二次开发、或者准备把加载项发布到生产环境的开发者参考。我会先讲清楚 Pane 页面加载机制再拆解几类常见根因然后把我那次完整的排查链路还原一遍最后给出一份可以照做的部署前检查清单。1. 先说清楚问题现象Pane 页面不对到底是哪里不对标题里点击对应按钮打开的 Pane 对应的页面不对这个描述看起来简单但具体现象差别非常大对应的解法也完全不同。我在实际运维中至少见到过四类先列个表现象表现大概率根因点按钮 A打开的是另一个加载项的页面加载项 Id 冲突或 manifest 里 SourceLocation 指向了别家地址点任意按钮打开的是上一版本的页面WPS 本地缓存未清或服务器端 index.html 被缓存打开的是 localhost 调试页面部署时 SourceLocation 没从开发地址改回生产地址打开页面白屏或 404静态资源相对路径错误或前端路由 base 路径不对如果你遇到的页面不对是第一种也就是打开了一个你压根没在这个加载项里写过的页面那问题往往比代码层面更深一层。别急着改代码先确认它加载的 URL 是什么。1.1 任务窗格Pane不是独立窗口它是加载项网页的容器WPS 的 JS 加载项本质上是一个嵌入在 WPS 进程内的浏览器容器里运行的网页应用。你可以把它理解成 WPS 在功能区画了几个按钮点按钮后在文档右侧弹出一个任务窗格这个窗格里渲染的就是你部署的那套前端页面。这个容器并不是直接打开你写的某个 html 文件而是读取加载项注册信息里的入口地址然后把那个地址当成一个完整的网站加载进来。加载之后页面里的 JS 代码再通过 WPS 提供的 JS API 和当前文档交互。所以 Pane 页面最终显示什么内容取决于两件事第一manifest 配置的 SourceLocation 指向哪个入口第二这个入口页面内部的路由逻辑最终渲染了哪个功能视图。很多时候页面不对是这两层中的某一层出了问题而不是 WPS 随机抽风。1.2 按钮和 Pane 页面之间隔着一层映射很多刚接触加载项开发的同事容易有个误解以为按钮 A 直接对应 pageA.html按钮 B 直接对应 pageB.html。实际上 WPS 加载项不是这么设计的。功能区里每一个按钮的职责是唤起加载项而不是打开某个静态页面。唤起之后加载项入口页面会收到一个触发上下文里面带有按钮标识、文档上下文等信息。入口页面的脚本需要自己判断这个上下文然后决定展示哪个功能模块。也就是说按钮到最终页面之间有一层事件分发逻辑。这层映射一旦在部署过程被破坏比如页面引用的静态资源路径变了、入口页面 URL 被加上了额外参数、或者入口页面本身被服务器重定向到了另一个地址就会出现点了 A 按钮却看到 B 页面的诡异现象。要定位问题必须先把这层映射关系一条条拉直。2. 先从 manifest 下手SourceLocation 与页面路由决定 Pane 实际加载什么无论你用的是 WPS 提供的加载项开发工具还是自己手写的打包脚本最后都会生成一份 manifest 描述文件。这份文件是 WPS 识别加载项身份、能力、入口地址的唯一依据。2.1 SourceLocation 是 Pane 页面地址的总闸manifest 里最关键的一段大致长这样不同 WPS 版本的 schema 字段名可能有差异结构思路一致Plugin Idcom.example.myaddin/Id Version1.0.0.4/Version DefaultSettings SourceLocationhttps://myserver.com/myaddin/index.html/SourceLocation /DefaultSettings /PluginSourceLocation就是整个加载项的入口页面地址。WPS 在创建任务窗格时会直接向这个地址发起请求。也就是说无论你在功能区放了几个按钮Pane 一开始加载的一定是这个入口 URL而不是某个按钮专属的独立 html。如果这个地址指向了另一个加载项的页面比如https://myserver.com/other-addin/index.html那不管点哪个按钮弹出的自然都是别的加载项的内容。这个坑我在客户现场遇到过两次一次是复制了同事的 manifest 没改 SourceLocation另一次是部署脚本把 manifest 覆盖错了。2.2 入口页面里的路由分发决定最终显示内容入口页面加载成功后前端代码要根据按钮触发上下文来决定渲染哪一个功能视图。以常见的 hash 路由为例流程大概是// 伪代码根据触发来源切换到对应功能页 function handleButtonTrigger(actionId) { const pageMap { btn-order: #/order/list, btn-stock: #/stock/list, btn-export: #/export/start }; window.location.hash pageMap[actionId] || #/home; }开发环境下按钮 id 和路由参数的对应关系调试得好好的一部署就错最常见的原因是入口页面 URL 里拼接参数时部署环境把参数吞掉了或者服务器的重定向规则把 hash 给丢了。举例开发时访问http://localhost:3000/index.html#/order/list一切正常部署后用https://myserver.com/myaddin/index.html#/order/list访问如果 nginx 或者网关对 URL 里的 hash 做了清洗页面就只会回到默认首页。看起来是打开的页面不对实际是路由参数没传到位。提示排查这类问题最直接的办法是用浏览器手动打开 SourceLocation 对应的地址在地址栏里尝试不同的 hash/query 参数观察页面能否正确展示对应功能。这能把WPS 环境问题和前端路由问题快速分开。2.3 相对路径和路由 base 是部署后的隐形炸弹开发时项目通常直接跑在根路径下但部署到服务器后经常要放在子目录里比如https://myserver.com/myaddin/。如果你的前端资源引用写的是./js/app.js那没问题但如果你写的是/js/app.js浏览器会去请求https://myserver.com/js/app.js结果必然 404。页面白屏、样式丢失、入口能打开但签到别的页面很多都是这个原因。解决方案也很直接构建时把publicPath或base配置成部署子路径并且保证入口页面里所有的资源引用都是相对路径或者通过构建工具统一加前缀。另外如果前端框架用了 history 路由模式部署后刷新就会 404而 hash 路由模式则没这个问题。加载项这种场景我一般建议统一用 hash 路由省心。3. 部署后才出现的串页问题身份冲突和缓存是两大元凶开发环境只有你一家加载项门户干净怎么点都对。生产环境一堆加载项共存问题就开始冒头。我遇到的部署后 Pane 页面不对很大比例来自两个地方加载项身份重复和缓存残留。3.1 加载项 Id 重复导致互相覆盖每个加载项在 manifest 里都有一个Id。这个 Id 是 WPS 区分不同加载项的关键标识。如果两个项目恰好复制过同一个工程模板没改 Id 就直接部署WPS 会把它们当成同一个加载项处理后注册的那个往往会把前面注册的信息覆盖掉。结果就是你点击了加载项 A 的按钮WPS 通过按钮事件找到的却是加载项 B 的注册信息于是打开 B 的 SourceLocation显示 B 的页面。这个现象极具迷惑性因为加载项列表里两个名字都还在但底层身份已经乱了。规范做法是给每个加载项分配一个独立的 GUID并且把这个 GUID 写进 manifest 后尽量不要再改动。如果确实复用了旧工程部署前第一步就是检查Id是否全局唯一。3.2 WPS 本地缓存导致旧页面残留WPS 内置浏览器和普通浏览器一样会对加载项页面做缓存。部署了新版本后如果加载项入口没有变化URL 相同用户点开 Pane 看到的可能还是上一次缓存的页面。这种情况下页面不对不是别的加载项而是你自己的旧版本。排查和解决方法是关闭所有 WPS 进程。打开系统用户目录Win 下直接在资源管理器输入%APPDATA%找到 kingsoft 相关的加载项缓存目录把和该加载项 Id 有关的缓存文件清理掉。重启 WPS重新加载加载项。不同 WPS 版本的缓存目录位置不一样最快的办法是在缓存目录里按加载项 Id 或域名搜索找到后整个文件夹清掉。3.3 服务器端缓存和 Service Worker 的干扰除了 WPS 本地缓存服务器端也可能开了一层缓存。尤其是 index.html 这种入口文件如果 nginx 配了强缓存静态资源更新了但入口文件还指着旧版本最终 Pane 加载的就会是旧页面。另外如果前端项目里注册了 Service Worker部署后 Worker 更新不及时也会出现永远加载旧页面的情况。排查手段是在浏览器开发者工具里勾选 Disable cache同时确认部署服务器对index.html设置了合理的Cache-Control: no-cache。4. 一次真实排查从现象到根因只用了 20 分钟下面记录一次让我印象很深的排错过程。现象和标题描述得几乎一样生产环境点击 A 按钮打开的 Pane 显示的是 B 加载项的内容。4.1 现场还原点击 A 按钮打开的是 B 页面当时我维护两个加载项项目名分别叫 addin-a 和 addin-b两个前端工程结构几乎一样都部署在同一台内网服务器的不同目录下。发布完 addin-a 的新版本后同事测试反馈功能区里点 addin-a 的订单查询按钮右侧 Pane 直接显示了 addin-b 的库存管理界面。我第一反应是前端代码里路由映射写错了。但在本地把 addin-a 的源码跑起来点按钮跳转完全正常说明代码逻辑没问题。于是我把注意力转移到部署环节。4.2 排查链路先是按钮、再是 manifest、最后是部署脚本我按下面的顺序一步步排查第一步确认 addin-a 在 WPS 加载项管理器里的注册状态。右键 WPS 加载项查看 addin-a 的入口地址。结果发现入口地址写的是https://server/addin-b/index.html——这里就破案了一半。第二步打开服务器上 addin-a 的部署目录找到该目录下的 manifest.xml发现这份文件的内容居然是从 addin-b 的工程里复制过来的。也就是说部署脚本在拷贝文件时把 addin-b 的 manifest 覆盖到了 addin-a 目录里。第三步验证 addin-a 的页面本身是否正常。直接用浏览器访问https://server/addin-a/index.html页面能正常打开路由跳转也对说明前端资源没问题。第四步修正 addin-a 的 manifest 后重新注册加载项重启 WPS点击 A 按钮功能页面恢复正常。整个过程从头到尾不到二十分钟。真正的耗时点反而在第一步之前——我先查了一大圈前端代码浪费了几分钟。4.3 这次坑直接暴露出的两个部署隐患事后复盘这次问题能发生有两个隐患叠加一是 addin-a 和 addin-b 基于同一个模板工程创建的manifest 里 Id 一开始就是同一个二是部署脚本简单粗暴地把整个目录拷过去没有做覆盖前校验清单。如果当时发布流程里有一行检查待部署 manifest 的 Id 与目录名是否匹配这个问题根本不会暴露到生产环境。后面我把这个检查写进了自动化脚本并加了改动版本号提醒再没遇到过同类事故。5. 把部署后打开不对页面概率降到零的实操习惯排查问题只是救火更重要的是一套让火根本烧不起来的习惯。下面这些是我现在每发一个版本的固定动作推荐你直接抄。5.1 发布前检查清单每次发布 WPS JS 加载项之前我会在发布备注里过一遍下面这张清单- [ ] manifest 的 Id 是否唯一并和加载项名称匹配 - [ ] SourceLocation 是否指向生产环境的正确地址 - [ ] 前端路径 base/publicPath 是否匹配部署子目录 - [ ] 入口页面是否能用浏览器直接打开并走通核心功能 - [ ] 版本号是否已更新 - [ ] WPS 加载项管理器里注册信息是否已刷新 - [ ] 是否已清理 WPS 本地缓存并重启验证这套清单看起来简单但每一条背后都有真实事故支撑。尤其是版本号这一项很多人改了代码忘了涨版本号即使清了缓存WPS 也可能因为版本一致不重新拉取 manifest结果还是旧配置。5.2 内网部署加载项的注意点很多企业会在内网环境开发 WPS 加载项没有公网域名也没有 HTTPS 证书。此时 SourceLocation 一般写http://内网IP:8080/xxx/index.html。但要注意部分 WPS 版本对 http 源有安全策略限制加载阶段会被拦截或静默失败表现为 Pane 一直打不开或者打开后白屏。如果遇到这种情况优先尝试把入口服务放到本机回环地址验证比如http://localhost:8080/index.html能绕过大部分安全限制。真正要内网其他机器访问有条件就上内网 HTTPS没条件就排查 WPS 当前版本对非加密源的兼容性别在明明代码没问题的路上浪费时间。5.3 把浏览器开发者工具变成排查利器我能二十分钟定位那次问题很大程度靠的是直接在浏览器里打开 SourceLocation 验证。WPS 的 Pane 再神秘它加载的本质还是一个网页。你可以先把 SourceLocation 复制到浏览器里手动访问一遍如果浏览器里打开都不对是前端代码或静态资源问题如果浏览器里打开正常但在 WPS 里不对才需要继续查 manifest、Id、缓存、注册信息。另外加载项页面里如果有 console 输出浏览器开发者工具里可以直接看到。开发期我习惯让前端在入口页面打印一行版本号信息这样 WPS 里打开页面后取日志就能确认当前加载的到底是哪个版本、哪个环境省掉大量猜测。最后再分享一个小技巧我后来每次发布加载项之前都会先用一台闲置电脑把新的 manifest 注册进去点一遍按钮再收工。看起来多花五分钟但像这种页面不对的坑基本都能在发布前暴露。如果你也遇到类似问题照着上面的思路先排查 SourceLocation 和 Id十次里能解决八次。