wps-js-demo深度解析:网页调用WPS的两种技术路线与实战坑点 📅 发布时间:2026/9/1 4:26:53 👁 浏览次数: 简介针对WPS插件开发场景这份演示程序基于Node.js环境展示了在网页中调用并操作本地WPS应用的具体实现适合具备JavaScript基础的开发者参考。资源共161个文件其中JavaScript与TypeScript脚本承担核心业务逻辑HTML页面完成前端展示JSON文件保存配置参数SVG资源用于图标呈现另有sample示例可直接运行体验MD与DOCX文档则提供使用说明与参考样例。整个压缩包仅1.52MB轻量易部署运行环境要求不高目前已有1114人下载学习。借助该演示开发者能够掌握WPS插件的基本工程结构、网页端与WPS程序的通信机制以及常用API的调用方式并据此扩展出在线打开、编辑、保存WPS文档等实际功能。虽然体积不大但示例覆盖了插件开发的主要环节代码组织与调用逻辑清晰无论刚入门的初学者还是进阶开发者都能从中获得良好的参考价值。 先别急着打开编辑器写代码我见过太多人拿到wps-js-demo就往里塞业务逻辑结果连插件跑不起来的原因都找不到。这个项目表面上是个JS Demo实际涉及两条完全不同的技术路线一条是在WPS客户端内部跑网页插件加载项另一条是从普通网页唤起并操作本机WPS程序。很多人把这两者混为一谈才导致后面处处碰壁。这篇文章我会从demo的目录结构、调用链路、核心API、常见坑四个方面把这两条路线一次讲透顺便把我实际调试时踩过的坑也一并交代清楚。1. 先搞清楚wps-js-demo到底在解决什么问题1.1 网页与WPS之间的最后一公里办公场景里有个很常见的需求业务系统里点一个按钮就能打开本机的WPS把服务器上的合同、审批单或者模板直接编辑起来。传统的做法是ActiveX控件或者本地装个代理程序但这两者一个被浏览器安全机制越收越紧另一个要单独维护部署。wps-js-demo这类项目解决的就是这个问题它用JavaScript作为统一入口把网页触发和WPS承载之间的通信链路打通让开发者不需要维护复杂的本地服务也能实现网页调用WPS打开文档、填充内容、触发保存等操作。1.2 demo的两种形态别搞混我在读这个demo源码的时候发现很多初学者最大的困惑是这个项目到底跑在哪答案是有两种形态。第一种是WPS加载项Add-in形态。WPS客户端本身内置了Chromium内核可以加载一个基于HTML/JavaScript的网页插件这个插件运行在WPS窗口内部通过wps.Office等命名空间直接操作当前打开的文档。这种形态下网页指的是插件UI本质上是WPS客户端的一部分。第二种是网页唤起本机客户端的形态。你在浏览器里打开一个业务系统页面点击按钮后通过自定义URL协议类似wps://open?pathxxx让系统唤起本机已经安装的WPS进程并带着参数打开指定文件。wps-js-demo这个项目有时候会同时包含这两种能力的示例如果分不清正在看哪部分代码后面所有逻辑都会乱套。我的建议是先确认demo里manifest.xml和wpsjs.config.json这两个文件的定位前者是加载项形态的入口后者是工程化编译配置看到它们基本就能判断项目框架属于哪种路线。2. demo项目结构与核心模块拆解2.1 标准目录结构长什么样以一版典型的wps-js-demo为例目录结构大致如下wps-js-demo/ ├── manifest.xml # 加载项注册清单声明插件名称、图标、入口页面 ├── wpsjs.config.json # wpsjs工程配置定义调试端口、编译输出路径 ├── package.json # npm脚本与依赖声明 ├── src/ │ ├── index.html # 插件主页面加载项形态下显示在任务窗格中 │ ├── index.js # 主逻辑入口 │ ├── api/ │ │ └── wps-handler.js # 封装WPS API调用的工具模块 │ └── utils/ │ └── protocol.js # 网页唤起客户端时生成协议URL的工具 ├── demo/ │ └── web-page.html # 纯浏览器页面演示从网页发起调用 └── README.md这种结构有一个好处把在WPS内跑的代码和在浏览器里跑的代码分开目录管理避免互相污染。src是加载项代码demo/web-page.html是外部网页调用示例。如果你拿到的demo不是这个结构也没关系认准manifest.xml和protocol相关文件就行。2.2 入口文件和manifest配置是命门加载项形态的入口是manifest.xml它负责告诉WPS我这个插件叫什么、图标在哪、任务窗格里加载哪个网页。一个最简化的manifest核心配置是这样?xml version1.0 encodingUTF-8? WpsAddIn DisplayName我的WPS插件/DisplayName Version1.0.0/Version TypeTaskPane/Type IconPathimages/icon.png/IconPath SourceLocationhttps://localhost:3000/index.html/SourceLocation /WpsAddIn别看字段不多SourceLocation这个值决定了WPS从哪加载你的插件页面。调试时它指向本地开发服务器的地址部署后要改成线上HTTPS地址。我当时在这个字段上卡了很久改了代码不生效最后发现是WPS缓存了旧的manifest清掉WPS缓存目录才恢复正常。如果你也遇到插件列表里有但页面一直是旧的这种问题优先排查manifest缓存。3. 网页调用WPS的完整链路3.1 加载项形态网页运行在WPS内部在加载项形态下调用链路是用户打开WPS文档任务窗格加载index.html页面里的JavaScript通过WPS注入的wps全局对象操作文档。代码看起来是这个样子// src/index.js $(function () { if (window.wps) { // 获取当前WPS应用实例 const app wps.WpsApplication(); // 读取当前文档的选中内容 const sel app.ActiveDocument.Selection; const text sel.Text; document.querySelector(#result).innerText text; } });这里的关键点在于window.wps不是你在浏览器里随便定义的对象而是WPS客户端环境里由宿主注入的桥接对象。所以这个JS文件只能在WPS加载项环境里运行不能直接在普通浏览器打开运行。很多初学者在Chrome里打开index.html看到wps is not defined就以为代码错了其实环境不对。3.2 网页唤起本机WPS的URL协议原理再看外部网页唤起本机客户端的形态。这种形态的底层是操作系统级的URL Protocol注册机制。程序安装时会在注册表里注册一个自定义协议例如wps浏览器遇到wps://开头的链接时会把这个请求转交给注册表对应程序处理。protocol.js里做的事情本质上是拼接并触发这种URL// demo/web-page.html 中的核心逻辑 function openWithWps(filePath) { // 构造协议URLencodeURIComponent处理中文和空格 const url wps://open?path encodeURIComponent(filePath); // 通过隐藏iframe触发协议不打断当前页面 const iframe document.createElement(iframe); iframe.style.display none; iframe.src url; document.body.appendChild(iframe); setTimeout(() { document.body.removeChild(iframe); }, 2000); }用隐藏iframe而不是直接window.location.href是为了防止页面在调用WPS时发生跳转影响用户当前操作。这个细节非常实用我建议直接照抄。3.3 一次完整调用路径整个流程可以归纳为五步业务页面点击按钮JavaScript拼接协议URL浏览器把协议请求交给操作系统操作系统根据注册表找到WPS主程序WPS收到参数后解析path并打开对应文档。期间如果WPS还没启动操作系统会先拉起进程再传参如果已经启动了需要WPS自身处理进程间通信把参数交给已有实例。这也能解释一个常见现象为什么WPS已经在运行的时候网页唤起打开文件比冷启动时慢。因为冷启动只是新开进程而进程已在运行时需要走一套IPC机制容易因为单实例锁、文件占用等问题失败。遇到第二次唤起没反应的大概率是WPS进程没释放锁。4. 核心JS操作与参数详解4.1 加载项API调用规范WPS加载项形态提供的API整体上模仿了Office JavaScript API的语义但又针对WPS做了裁剪。日常写demo最常用到的是下面这组对象/方法作用备注wps.WpsApplication()获取WPS应用实例所有操作的根入口Application.ActiveDocument当前活动文档文字/表格/演示通用Document.Save()保存文档可以接as参数另存Selection.Text读写选中区域文本表格场景下是单元格内容Application.CreateNewDocument()新建空白文档常用在初始化测试中Document.Sections.Item(index)按索引访问节处理页眉页脚时会用需要特别提醒的是WPS的JS API在不同组件文字、表格、演示上的支持程度不一样。比如Selection.Text在文字组件里很稳定但在表格组件里要改成Selection.Cells.Item(1,1).Text这种写法否则会报空引用。demo里如果只覆盖了文字场景你搬到表格里用之前最好先去官方API列表查一下当前版本的组件支持矩阵。4.2 一段能跑的完整示例我从wps-js-demo里简化出一段可以直接跑的代码功能是在插件页面点击按钮把当前文档选中内容替换为指定文本然后另存一份副本// src/index.js const wpsApp wps.WpsApplication(); const doc wpsApp.ActiveDocument; function replaceAndSave() { try { const sel doc.Selection; if (!sel.Text) { alert(请先选中一段文字); return; } sel.Text 【已替换】 sel.Text; // 另存为docx doc.SaveAs(C:\\temp\\output.docx, 12); // 12是wdFormatXMLDocument枚举值对应docx格式 console.log(保存成功); } catch (err) { console.error(调用WPS API出错了, err); alert(操作失败错误信息 err.message); } } document.querySelector(#btn-replace).addEventListener(click, replaceAndSave);SaveAs的第二个参数是格式枚举值这一点很多人忽略。12是docx其他常用值包括0对应doc、17对应pdf。如果传错枚举轻则保存出来的格式不对重则直接抛异常。建议每次都显式传格式参数不要依赖默认值。4.3 网页端调用的参数传递细节外部网页唤起形态下参数传递就是一个协议URL拼接的过程但实际项目里参数往往不止path一个还可能有action打开还是新建、password加密文档密码、readonly只读打开等。demo里utils/protocol.js通常会把参数对象序列化成query string// utils/protocol.js export function buildWpsUrl(params) { const base wps://action; const query Object.keys(params) .map(key ${encodeURIComponent(key)}${encodeURIComponent(params[key])}) .join(); return ${base}?${query}; }这里有一个特别值得注意的点协议URL的query string不一定要区分大小写但参数值一定要做URL编码。中文、空格、符号这几种字符如果不编码一进入WPS解析阶段就会出问题。我遇到过最典型的情况是文件路径里带结果WPS把路径截断只打开了前半段目录排查半天才意识到是编码问题。5. 常见问题与排查经验5.1 网页唤起没反应WPS完全不动优先检查三件事。第一协议是否被安全软件拦截很多企业安全策略会禁止浏览器自定义协议唤起本地程序。第二是否处于框架限制环境比如页面嵌在第三方App的WebView里WebView往往不支持协议跳转。第三WPS是否安装为MSI版本且注册表协议被清理。如果想快速验证是不是注册表问题可以在命令行里直接输入start wps://open?pathC:\test.docx如果命令行能唤起说明WPS和注册表正常问题出在浏览器或网页环境如果命令行也唤不起那就是WPS的协议注册出了故障重装WPS或者导入注册表修复文件能解决。5.2 加载项调试时页面白屏遇到白屏八成是SourceLocation指向的本地服务器没启动或者WPS无法访问localhost地址。这时候先确认命令终端里wpsjs debug是否真的跑起来了然后直接在Chrome里打开配置中的地址看页面能否正常渲染。如果Chrome渲染正常而WPS里白屏再考虑是不是WPS的调试端口被占用换个端口重试。另外提一句WPS加载项的调试比较特殊它不会像浏览器插件那样直接显示开发者工具。你需要在wpsjs.config.json里开启debug模式然后在WPS插件菜单里找到开发者工具入口才能打开控制台。很多新人对这一点没概念以为JavaScript报错就只能干瞪眼。5.3 Document对象拿不到API一直报错这个问题通常和事件时机有关。WPS加载项页面加载时WPS可能还没有把ActiveDocument准备妥当此时调用wps.WpsApplication().ActiveDocument拿到的对象是null。老实的做法是监听文档就绪事件wps.WpsApplication().ActivePresentation; // 或者用setTimeout轮询等待 function waitForDocument(callback) { const timer setInterval(() { const doc wps.WpsApplication().ActiveDocument; if (doc) { clearInterval(timer); callback(doc); } }, 200); }注意不同组件文档就绪事件的名称不一样文字组件是DocumentOpen、表格组件是WorkbookOpen。如果你拿到的demo只写了文字示例应对表格场景时一定要自己查事件名否则就会陷入轮询死循环。5.4 安全校验与白名单限制不管哪条路线最终都会遇到安全策略这道坎。网页唤起本机WPS不仅要把协议注册好还要确保站点的域名在这台机器上被浏览器允许发起协议跳转。企业内网环境里如果IE浏览器作为默认浏览器安全区设置里面允许ActiveX和允许打开本地程序两个选项经常是关闭状态这是很多内网系统调用WPS失败的隐性原因。加载项形态也有类似限制WPS在加载外部网页插件时会对证书和地址做校验。开发阶段用http://localhost没问题一旦部署到生产环境必须换成HTTPS地址否则插件会被拒绝加载。这一点我建议在项目初期就规划好别等到上线前才匆匆忙忙换证书到时候会有一堆混合内容、跨域问题等着你。6. 一些实际的建议如果你准备在自己的项目里复刻wps-js-demo我建议不要一上来就追求把所有功能都塞进插件页面。先做最小闭环把网页按钮点击唤起WPS打开固定路径文档跑通再考虑将业务数据传入文档模板。因为URL协议方式传复杂数据结构很受限一旦需要传JSON或大批量数据就说明你该用本地代理服务或加载项方式了而那条路线的改造量远超预期。另外WPS的JS API文档更新比较频繁不同版本的WPS客户端对API的支持程度也不完全一致。我吃过亏的是代码里调用了某个新版才有的API在客户的老版本WPS上直接白屏。稳妥的做法是在代码里做能力检测像浏览器特性检测那样判断某个API是否存在后再调用这样老版本至少能降级提示而不是静默崩溃。最后再分享一个调试技巧WPS加载项页面里的console.log在开发者工具控制台里是能看到的但它打印的对象结构跟浏览器里略有差异很多WPS对象打印出来显示的是Object没有可展开的属性。这个时候不要慌用JSON.stringify先序列化常见字段或者用Object.keys()查看对象上挂了哪些属性会比直接点开对象快得多。实践几次之后你会发现WPS JS插件开发和网页开发在思维上没有本质区别只要把环境差异和API边界摸清楚剩下的都是常规操作。本文还有配套的精品资源点击获取