Next.js 里想静默打印,别往 Server Action 里塞
有次评审,同事很认真地问:App Router 能不能写个 Server Action,服务端渲完直接打到仓库打印机? 我说你服务器机房在哪儿,打印机在哪儿。两边对不上,这事就没戏。 听着像抬杠,其实是物理问题。Server Action、Route Handler 都在 Node 里跑,枚举不到员工电脑上的 USB 机,也进不了人家的系统打印队列。你最多 `page.pdf()` 出个文件、丢 OSS、发邮件。纸还是得出在**连着打印机的那台机器**上。 静默打印争的就是「少弹窗、指定机、工位出纸」。跟你是不是 Next、是不是 RSC,关系真不大。 ## window.print 也救不了「免确认」 `'use client'` 里照样可以: ```js window.print() ``` 弹窗还在。Next 没有私藏 API 能关掉系统打印框——浏览器不让。偶发达一张、用户点得起确定,用它就行;收银连打、面单高峰,弹一次确认都嫌慢。 ## 那出纸写哪儿 比较稳的拆法就两段: - 浏览器里的业务页负责触发(HTML / 打印专用 URL / 已有 PDF) - 工位上的 Web打印专家客户端接任务,走本机打印栈 页面侧装包: ```bash npm install web-print-pdf ``` 注意:这个 import 只能放客户端组件。写进 Server Action 没有意义,CI 机器上装了客户端也打不到门店的纸。 ```js 'use client' import webPrintPdf from 'web-print-pdf' export async function printOrderSheet(html) { await webPrintPdf.printHtml( html, { paperFormat: 'A4', margin: '8mm' }, { printerName: '仓内面单机', copies: 1 } ) } ``` `printerName` 填工位真实名字,别想当然写「默认打印机」然后怪 SDK。 如果版式已经是独立打印页,懒得拼字符串: ```js await webPrintPdf.printHtmlByUrl( `${location.origin}/print/order/${orderId}?print=1`, { paperFormat: 'A4', timeout: 30000 }, { printerName: '仓内面单机' } ) ``` 打印路由做成光板布局就行,别把侧边栏、消息铃铛一起打出去。要登录态的话,要么 cookie 能带上,要么服务端把 HTML 拼好再 `printHtml`。 ## 和「服务端出 PDF」并不打架 很多 Next 项目本来就用 Puppeteer / React-PDF 做归档,留着。归档归归档,出纸归出纸: ```js await webPrintPdf.printPdfByUrl( pdfUrl, {}, { printerName: '仓内面单机', copies: 1 }, { action: 'print' } ) ``` Server Action 继续签发 PDF、做权限;浏览器只负责把 URL 塞给本机客户端。别指望 Action 里「顺便把纸打了」。 ## 几个容易踩的点 联调失败先看客户端开没开,提示写成「请打开 Web打印专家」,比丢一串 WebSocket 错误强。 站点部署在 Vercel 完全没问题,客户端是装工位的,别往 Serverless 里找打印机。 纯手机浏览器、家里偶打合同,上静默客户端可能过重,`window.print` 或下载 PDF 就够。 ## 收束 Next 再新,也挪不走「出纸发生在哪台电脑」这件事。Server Action 生成内容可以,替用户按打印键不行。要静默,就老老实实:页面用 `web-print-pdf` 触发,工位跑 Web打印专家。 包名:`web-print-pdf`。不放外链。