前阵子我给一套 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> <Id>com.example.myaddin</Id> <Version>1.0.0.4</Version> <DefaultSettings> <SourceLocation>https://myserver.com/myaddin/index.html</SourceLocation> </DefaultSettings> </Plugin>SourceLocation就是整个加载项的入口页面地址。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,十次里能解决八次。