
简介这是一份完整的 WPS 插件开发 Node.js 示例工程核心解决在网页中调用 WPS 客户端打开、编辑文档等需求。整个压缩包共 161 个文件体积约 1.52MB主体包含 18 个 JavaScript 脚本、6 个 TypeScript 类型文件、6 个 HTML 示例页面、5 个 JSON 配置和 3 个 Markdown 说明文档并附带 sample 演示案例、svg 图标、css 样式及 docx 文档等辅助材料便于对照实际效果学习包中还保留了完整的 Git 元数据含大量哈希对象、packed-refs、description、index 等可查看源码开发过程中的每次提交与分支演进。目前已有 1118 人学习下载。借助这份 demo开发者可以快速理解 WPS 网页插件从页面触发到本地文档操作的调用流程、接口封装方式与回调处理思路也能为后续基于 JS/TS 的二次开发提供可直接复用的工程骨架适合正在评估 WPS 集成方案的前端或 Node.js 工程师也适合想快速上手 WPS 插件开发的新手。1. wps-js-demo网页里调起 WPS 的完整可跑示例别再从零趟坑做 Web 前端的人大概都遇到过这种需求用户点一个按钮网页直接调用本机 WPS 打开指定文档甚至把编辑结果回传给 Web 端。听起来像“浏览器调本地桌面软件”的黑匣子但 wps-js-demo 这个项目把整条链路摊开了——它是一套基于 Node.js 开发的 WPS 插件示例工程提供了从网页发起调用、WPS 接收请求、JS 宏处理文档、再把结果返回网页的完整闭环。适合三类人一是要给 OA、ERP 加“网页打开本地 WPS 编辑文档”能力的后端/全栈工程师二是研究 WPS 二次开发但不想读那一大堆 API 文档的入门者三是需要给现有办公系统做国产化适配的技术负责人。我拆完这份资源后直接在一台 Windows 10 上跑通了最小示例下面把方案选型、工程配置、接口调用和踩过的坑一次性说透。2. 方案选型与运行链路先搞懂 WPS 插件到底怎么和网页通信2.1 为什么是 Node.js WPS JS 宏而不是 COM 或 HTTP 轮询WPS 二次开发主要有三条路VBA 宏、COM 组件类似 Office 的 ActiveX、以及 WPS 2019 之后主推的 JavaScript 宏JSA。wps-js-demo 选的是 JSA Node.js 的方案核心原因有两个。其一WPS 的 JavaScript 宏和浏览器里的 JavaScript 语法基本一致前端工程师上手几乎零成本不用去碰 VBA 那种看着像 1990 年的语法其二Node.js 在这里不是写插件本体而是起一个本地 HTTP 服务负责接收网页端的指令然后通过 WPS 的启动参数或本地通信机制唤起 WPS 并执行对应的 JS 宏。你可能要问为什么不用 Web 端直接跟 WPS 通信因为浏览器出于安全沙箱限制不可能直接往本机任意程序发指令。常见做法是网页端先调一个本地 Node.js 服务http://127.0.0.1:某个端口这个服务再去启动 WPS 并传参数。wps-js-demo 的工程结构就是这么设计的前端页面、Node 服务、WPS JS 宏三部分各司其职。2.2 完整调用链路网页 - Node - WPS - 回传从 wps-js-demo 源码里梳理出来的核心链路如下浏览器页面发起请求 - POST http://127.0.0.1:3000/open - Node.js 服务解析参数文件路径、操作类型 - 通过 child_process 启动 WPS 并传入文档路径 - WPS 加载文档并触发 JS 宏如打开文件、读取内容 - 宏执行完毕后将结果写入临时文件或通过 HTTP 回调 - Node 服务读取结果并返回给浏览器这条链路里有几个关键设计点。Node.js 服务要提前启动并监听固定端口WPS 的 JS 宏需要被放置到 WPS 的宏目录下或者通过命令行参数指定回传数据不能直接复用最初的 HTTP 请求因为 WPS 宏跑在独立的进程里等它执行完网页端的请求可能早就超时了。所以 wps-js-demo 里实际采用的是“回调文件 轮询”或“宏内发起新 HTTP 请求”的方式。我在复现时建议你先跑通最简单的“打开文档”场景看到 WPS 窗口弹出来并且文档加载成功再继续折腾数据回传。3. 从拉取到跑通Node 服务与 WPS 宏的目录配置、启动命令与参数说明3.1 工程目录结构先看懂每个文件夹是干嘛的wps-js-demo 的整个工程不复杂但目录里有几个是给 WPS 宏准备的、有几个是给 Node 服务用的第一次看容易混。下面是我整理后的文件清单路径作用需要改动吗server/Node.js HTTP 服务主目录包含app.js或index.js需按本机端口和 WPS 路径调整wps-macro/WPS JS 宏脚本存放目录.js后缀但语法走 WPS JSA核心要看的代码在这里public/前端演示页面HTML 原生 JS可改用于调整调用按钮和参数package.jsonNode 依赖描述主要用到 express 和 child_process基本不用动config/本地配置文件写着 WPS 安装路径、端口号、临时目录必须改否则启动直接报错首先你要打开config下的配置文件把wpsPath改成你自己机器上 WPS 的安装路径例如C:\Users\你的用户名\AppData\Local\Kingsoft\WPS Office\ksolaunch.exe或新版路径下的wps.exe。然后确认port字段默认值我建议直接用 3000如果被占用再换别的但换的同时前端页面里的请求地址也要同步改。3.2 启动 Node 服务三步让网页端和 WPS 接上头按下面步骤操作跑通最小环境# 第一步安装依赖 npm install # 第二步启动 Node 服务windows 下直接用 cmd node server/app.js # 第三步浏览器打开演示页 # 在 chrome 里访问 http://127.0.0.1:3000这三步背后分别做了什么npm install会按照package.json拉取 express 等依赖express 在这里用来定义/open和/call这些路由接口node server/app.js启动后服务会监听你配置的端口同时检查 WPS 主程序路径是否存在如果路径配错了它会在控制台直接报ENOENT错误访问http://127.0.0.1:3000后页面上的按钮会向服务端发送一个携带文件路径的 POST 请求。如果一切正常你点击页面上的打开按钮WPS 会弹出来并加载对应文档。这里有个小细节值得说明WPS 的启动参数与 Office 不同Office 常用/dde或自动化接口而 wps-js-demo 里用的是child_process.execFile拉起 WPS 可执行文件并拼接文件路径参数。核心 Node 代码如下// server/app.js 中调用 WPS 的关键片段 const { execFile } require(child_process); const config require(../config/default.json); function openWithWps(filePath) { // wpsPath 来自配置例如 ksolaunch.exe 的绝对路径 const wps config.wpsPath; // 通过 execFile 启动 WPS并传入目标文档路径 execFile(wps, [filePath], { windowsHide: false }, (error, stdout, stderr) { if (error) { console.error(启动 WPS 失败:, error); } else { console.log(WPS 已启动返回信息:, stdout); } }); }参数说明execFile的第二个参数[filePath]是传给 WPS 的命令行参数这里直接传文档路径WPS 收到后会用默认窗口打开windowsHide: false表示让 WPS 窗口正常显示如果你在无桌面环境下跑可以改成truestdout和stderr用于捕获 WPS 启动时的输出但 WPS 本身不太往标准输出打日志所以这个回调里更多是为了拿启动失败的错误信息。3.3 WPS JS 宏的放置位置放错目录等于白写wps-js-demo 拆开看最有价值的部分是wps-macro/下的那几段宏脚本它们是真正操作文档内容的逻辑。但光有脚本还不行关键要搞清楚 WPS 从哪里加载宏。WPS 的 JS 宏默认存放在用户目录下特定的 JSA 目录中比如C:\Users\你的用户名\AppData\Roaming\Kingsoft\Office\User\jsmacro\custom你需要把wps-macro/下的.js文件复制到这个目录。复制完成后重新打开 WPS 或者新建一个文档按Alt F11调出 WPS 的 JS 宏编辑器左侧的工程树里应该能看到你放进去的宏文件。如果在编辑器里看不到检查两个地方目录是不是jsmacro\custom注意是 custom 不是 global以及宏文件是不是以 UTF-8 无 BOM 编码保存。我遇到过同行把文件保存成带 BOM 的 UTF-8结果 WPS 编辑器里能打开但一执行就报语法错误去掉 BOM 后立刻正常。放好宏之后Node 服务要调用它常见做法是在启动 WPS 时加一个特殊参数。比如 wps-js-demo 里在调用时拼接了-jsmacro参数让 WPS 启动后自动执行指定宏。参数格式大致是ksolaunch.exe C:\test.docx -jsmacro 读取文档内容注意-jsmacro参数后面跟的是宏的名字而这个名字对应jsmacro/custom/目录下某个文件里定义的函数名或宏名。也就是说Node 端传文档路径WPS 打开文档后执行一个叫“读取文档内容”的宏宏跑完再把结果通过文件写入或 HTTP POST 回传给 Node。这个机制搞懂了wps-js-demo 的核心逻辑就算吃透了。4. 网页调用 WPS 的完整配置open 接口、回传方式与常见参数调整4.1 定义 HTTP 接口open、read、write 三类请求的分工wps-js-demo 的服务端本质上只做了三件事接收网页请求、解析参数里的文件路径和宏名称、执行 WPS 调用。我用一个简化的路由表来说明路由路径方法参数用途/openPOSTfilePath让 WPS 直接打开文档/readPOSTfilePath,range让 WPS 打开文档并读取指定区域内容/writePOSTfilePath,content让 WPS 打开文档并写入指定内容每条路由的实现套路都类似先从req.body中取出参数然后调用我们上面写的openWithWps函数只不过会在命令行参数上追加不同的宏名。比如/read路由最终拼接的命令类似于ksolaunch.exe D:\docs\合同.docx -jsmacro ReadRange这种设计的好处是把“网页请求”和“WPS 操作”彻底解耦。我后续在自己项目里扩展时只需要在 WPS 宏目录里加一个宏然后在 Node 端加一条路由转发就能让网页支持更多的文档操作。对应的 Node 路由代码如下app.post(/read, (req, res) { const { filePath, range } req.body; // 拼接 WPS 启动参数文档路径 宏名称 区域参数 const args [filePath, -jsmacro, ReadRange, range]; execFile(config.wpsPath, args, (error) { if (error) { return res.status(500).json({ code: 1, msg: error.message }); } res.json({ code: 0, msg: 读取指令已发出 }); }); });这里有个值得注意的设计决策Node 路由并没有等待 WPS 宏执行完再返回结果而是发出调用后立刻返回“指令已发出”。原因很简单——WPS 是一个 GUI 程序宏执行时长取决于文档大小和用户是否手动操作HTTP 请求挂着等它完全不符合实际。所以数据回传需要另想办法。4.2 宏执行结果的回传文件回调与 HTTP 回调二选一wps-js-demo 里宏回传数据用的是最实际的方案——写临时文件Node 端轮询读取。做法很像早期的 AJAX 轮询但在这里恰好够用。宏执行完关键内容后把结果序列化写入一个固定路径的 JSON 文件比如C:\temp\wps_result.jsonNode 服务收到“指令已发出”响应后每隔 500 毫秒去读这个文件一旦发现文件修改时间比调用时间晚就认为宏执行完毕把 JSON 内容解析后返回给网页。// Node 端轮询结果文件的代码 function waitForResult(pollMs 500, timeoutMs 15000) { return new Promise((resolve, reject) { const start Date.now(); const timer setInterval(() { const result checkResultFile(); // 读取结果JSON并解析 if (result) { clearInterval(timer); resolve(result); } else if (Date.now() - start timeoutMs) { clearInterval(timer); reject(new Error(等待 WPS 宏执行超时)); } }, pollMs); }); }参数说明pollMs设 500 毫秒比较均衡——太快会频繁读磁盘太慢用户会感觉卡timeoutMs建议 15 秒足够绝大多数文档操作完成但如果你的宏里要处理几十 MB 级别的文档把超时拉长到 30 秒更稳妥。checkResultFile内部会先读 JSON再比对文件的时间戳如果时间戳不满足条件就返回 null 继续等。另一种回传方式是让宏直接向 Node 服务发起 HTTP POST 请求——WPS 的 JSA 环境里可以用new ActiveXObject(MSXML2.XMLHTTP)或fetch发送请求。但这种方式的坑在于如果 Node 服务恰好重启、端口变化宏里写死的回调地址就失效了。我实际开发时更倾向于临时文件方式毕竟是纯本地回环不需要宏感知 Node 服务的生命周期。4.3 前端页面参数跨域与请求格式要注意wps-js-demo 自带的演示页面是放在同一个 Node 服务下的所以不存在跨域问题直接fetch(/open)就行。但如果你要在自己现有的 Vue/React 项目里接入一定会有跨域。解决很简单在 Node 服务里加 CORS 头app.use((req, res, next) { res.setHeader(Access-Control-Allow-Origin, *); res.setHeader(Access-Control-Allow-Methods, POST, GET, OPTIONS); res.setHeader(Access-Control-Allow-Headers, Content-Type); next(); });注意调试时的“预检请求”如果前端同时传了Content-Type: application/json浏览器会先发 OPTIONS 请求所以上面代码里Access-Control-Allow-Methods必须带上OPTIONS否则前端的 POST 被浏览器拦截控制台报的还是跨域错误真实原因其实是预检没过。如果只用 GET 方式传参这种问题能少一点但文件路径里带中文、空格时 URL 编码很痛苦所以还是用 POST JSON 更合适。5. 避坑与排查WPS 启动失败、宏找不到、回传超时等五个高频问题5.1 WPS 启动失败Node 报ENOENT现象点击网页按钮后Node 控制台输出Error: spawn ... ENOENTWPS 没有弹出来。原因config里配置的wpsPath不对。Windows 下 WPS 的安装路径有很多变体比如旧版装在C:\Users\xxx\AppData\Local\Kingsoft\WPS Office\下新版则可能在C:\Program Files\Kingsoft\WPS Office\下且真正拉起程序的文件名有ksolaunch.exe、wps.exe的区别。解决先在文件管理器里找到 WPS 的安装目录随便双击打开一个 docx用任务管理器找到进程对应的可执行文件名和完整路径然后把这个路径填进配置。另外建议把配置文件里的路径统一用双反斜杠转义例如wpsPath: C:\\Users\\admin\\AppData\\Local\\Kingsoft\\WPS Office\\win64\\ksolaunch.exe。5.2 WPS 启动了但文档没打开只弹了启动器现象WPS 窗口打开了主界面也出来了但目标文档没有加载页面停在空白启动页。原因执行execFile时传的是ksolaunch.exe它是 WPS 的启动器只负责找到正确的组件版本并不直接打开文档需要把文件参数传给ksolaunch.exe的特定调用方式或者干脆换成wps.exe。这在 wps-js-demo 里是个很容易被忽略的细节。解决我一般直接改用当前版本下实际的wps.exe完整路径不再经过启动器。注意wps.exe路径在 64 位系统下可能位于office6子目录里具体以你的机器实际文件结构为准。另外要确认目标文件路径存在WPS 对不存在的文件会静默忽略并显示空白页。5.3 宏能打开但报错“找不到宏 xxx”现象WPS 正常打开文档但弹出一个对话框提示找不到名为ReadRange的宏。原因宏文件没有复制对位置。前面我们说要放到jsmacro\custom目录但不同的 WPS 版本对自定义宏的目录有差异有的版本还要求宏文件名不带中文且文件内部要写function 宏名()的形式。另外-jsmacro参数后接的宏名必须与文件内部定义的函数名一致大小写也要一致。虽然名称为“js”但 wps 宏的匹配规则实际上区分大小写——这个和网页 JavaScript 的“忽略大小写习惯”完全相反我在这里吃过亏。解决在 WPS 里按AltF11打开宏编辑器逐个展开工程确认能看到目标宏文件。然后把宏文件的函数名改成和 Node 端参数完全一致的名称。如果仍然报错不用沉浸测试直接删掉宏文件重新复制一遍并确认编码是 UTF-8 无 BOM。注意部分 WPS 版本创建用户目录要等首次运行之后才有如果装了 WPS 但从没打开过任何文档jsmacro目录根本不存在先新建文件夹再放文件即可。5.4 宏执行成功但网页一直转圈等待现象WPS 里宏已经弹出了结果提示框但网页端一直处于等待状态最后超时。原因回传链路断裂。如果你的宏是弹对话框提示结果并没有把结果写入轮询的 JSON 文件Node 端checkResultFile永远拿不到新内容自然超时。还有一种常见情况临时文件路径权限不足宏写入时被系统拒绝写文件动作静默失败。解决把临时文件路径改到一个 WPS 和 Node 都有权限的目录比如C:\Users\你的用户名\wps_temp\result.json。这段路径要牢记因为你只配置一次后面所有宏都要往这里写。同时要在宏代码里加try...catch写失败时至少弹出一个错误提示方便定位。另外建议在 Node 端waitForResult里打日志打印当前轮询到的时间戳与文件时间戳这样可以立刻看出是文件没更新还是更新了没读到。5.5 Node 服务崩溃在 JSON 解析上现象WPS 宏正常写入了结果文件但 Node 服务在读文件时直接抛 JSON 解析异常进程退出。原因宏写入 JSON 文件时采用了非标准格式最典型的是宏里拼接字符串时用了单引号或者直接把弹窗内容alert的多行文本原样写入导致 JSON 里出现换行和未转义引号。解决在 WPS 宏里把结果序列化时用JSON.stringify不要手动拼接字符串。Node 端读文件时也要先用try...catch包住JSON.parse解析失败时记录原始文件内容方便对照。这一步我建议当默认习惯来做因为宏运行环境不可控任何一段手写拼接的 JSON 都可能翻车。6. 进阶用法从“打开文档”到“回填编辑结果”打通动态表格合并与 JS 宏协作跑通了基础打开流程之后你会发现这项目真正的价值在于把 WPS 的文档操作封装成了网页可调用的服务。我之前在做一个合同管理项目时业务方要求在网页上点一个按钮WPS 自动打开合同模板把弹窗里填的甲乙双方名称、金额、日期直接写入文档再把合同状态回传业务系统。下面这段思路就是从 wps-js-demo 扩展出来的——核心是让宏接收参数并操作文档。在宏目录里新建一个WriteContract.js内容大致如下// 设置函数名 WriteContract供 Node 端 -jsmacro 参数调用 function WriteContract(params) { // params 是 Node 端传过来的 JSON 字符串先解析 const data JSON.parse(params); // 获取当前活动表格 const sheet ActiveWorkbook.ActiveSheet; // 按单元格坐标写入合同双方名称 sheet.Range(B2).Value2 data.partyA; sheet.Range(B3).Value2 data.partyB; }对应 Node 端调用时把网页传来的表单字段序列化后拼到启动参数里ksolaunch.exe D:\contract.xlsx -jsmacro WriteContract {partyA:张三,partyB:李四}我在做动态表格合并时踩过一个典型坑WPS JS 宏的RangeAPI 默认操作的是表格区域但“合并单元格”和“跨列居中”在 JSA 里的调用方式不一样网页端用 JS 操作 DOM 的惯性思维套到宏上不通用。正确的合并方式是sheet.Range(A1:C1).Merge()不要用Cells.MergeCells不带区域参数在 WPS 的 JSA 环境里会直接报未定义错误。而动态创建的表格如果你事先不知道要合并到第几列就在宏里用data.mergeRange参数控制Node 端按业务规则算好传进来就好。另外如果你网页端本身用了比较多“忽略大小写”的 JS 字符串处理逻辑注意千万别把这个习惯带到写宏参数里。上面提到过宏名称匹配和 API 方法名的解析——我实际测试中 WPS JSA 区分大小写存在不一致的地方有些 API 别名不区分、但函数和自定义宏严格区分。所以保险的做法是宏文件内的function名称与 Node 端-jsmacro参数保持逐字符一致不要靠手输复制粘贴。最后一个技巧调试宏时把try...catch写在最外层然后把错误信息JSON.stringify写入结果文件。千万别只弹alert窗口带窗口的宏在无头环境或用户最小化 WPS 时会卡住整个调用链路。有一次我图省事写了个alert(msg)结果用户反馈“点了按钮没反应”查了一圈发现 WPS 窗口缩在任务栏里弹窗等点击等超时——从那以后我的宏和 Node 端回传全部硬编码为“写文件 日志控制台”再没因为弹窗堵过窗口。这份 wps-js-demo 的示例你可以直接照着改希望帮到你。本文还有配套的精品资源点击获取