
简介这是一套面向 Vue 前端开发者的 hiprint 打印功能示例工程基于 Vue 与 Element-UI 组件库搭建适合需要在后台管理系统中集成打印模板设计、预览与打印的开发人员作为参考起点。压缩包中包含完整的工程代码共 81 个文件大小约 1.88MB其中 52 个脚本文件承载主要逻辑与构建配置2 个 Vue 组件文件展示组件化写法4 个样式文件和 4 个页面文件负责样式与结构另有 10 张图片素材辅助了解界面效果。项目从构建目录、配置文件到源码目录均保持标准工程结构便于快速定位与修改。目前已有 5240 人学习/下载。通过此示例读者可以掌握 hiprint 在 Vue 项目中的初始化方式、模板渲染以及打印调用的基本流程并参考其路由设置、公共组件和构建脚本迁移到自己的后台系统中有效降低从零搭建的排错成本适合具备一定 Vue 基础、希望快速集成打印能力的中级前端开发人员。 确实在管理后台这类项目里打印功能往往是最后才被想起来、却又最容易让人头疼的环节。浏览器原生打印的不可控、样式错乱、分页困难相信不少朋友都深有体会。我这次基于Vue和Element-UI搭建的vue-hiprint-example项目就是专门用来解决这个痛点的。它把hiprint这个强大又灵活的打印插件完整地集成到了Vue生态里让你能以拖拽配置的方式搞定复杂的票据、报表和合同打印。这篇文章我会从项目选型的思路、环境搭建到核心的模板设计、打印调用再到我实际踩过的坑完整复现一遍这个项目的落地过程给正在为前端打印功能发愁的朋友一个可以直接参考的实践样本。1. 内容整体设计与思路拆解1.1 为什么是hiprint解决前端打印的真实痛点在做这个项目之前我在实际工作中处理过太多打印相关的需求。最常见的场景是用户要求“把这个表格打印出来”或者“把这个单据打出来”但用window.print()配合CSS媒体查询的方式总是会遇到几个恼人的问题不同浏览器的默认边距不一致media print里的样式经常和你预想的不一样一个表格跨页的时候表头不会自动重复而且用户无法在打印前调整纸张大小、边距等参数。这些问题的根源在于浏览器原生的打印机制并不懂你的业务数据结构它只是简单地把当前页面像素转换为纸张上的点。hiprint解决的正是这个问题。它把打印内容和数据结构绑定通过自定义模板的方式让前端可以像设计UI一样设计打印样式。它完全没有依赖那些已经停止维护的第三方打印插件而是直接基于浏览器特性进行封装和增强。更关键的是hiprint提供了类似“所见即所得”的编辑器界面业务人员可以自行调整模板格式前端工程师只需要关注数据和组件加载即可这是一个很重要的能力边界也是技术选型上的首要考量。1.2 技术栈选型的取舍Vue、Element-UI与hiprint的协作关系选择Vue配合Element-UI是当时团队技术栈的自然延续。Element-UI提供了一套成熟的后台组件体系和这个项目的需求很契合——因为hiprint本身并不关心你的表单、按钮和弹窗怎么做它只负责打印核心外围的“参数配置区”“模板管理列表”这些功能使用Element-UI来搭建效率非常高。项目中真实的协作关系是这样的Element-UI负责搭建操作界面框架提供按钮、弹窗、表单输入等交互组件hiprint负责核心打印能力包括模板的声明、渲染、预览和打印输出Vue则承担了数据桥梁的角色把后台接口获取的业务数据比如订单信息、客户信息注入到hiprint模板中。这样分工明确各司其职比使用一个“全家桶”式的打印组件要灵活得多。1.3 项目结构的规划与模块划分我在项目规划阶段就把结构分成了三块清晰的部分src/components/hiprint所有与打印相关的组件包括hiprint-print封装组件、模板配置面板组件。src/utils/hiprint-manager.jshiprint的全局管理器负责初始化、模板加载、打印调用等核心逻辑。src/views/print/打印相关的业务页面例如“模板编辑页”和“业务打印页”。这个结构在后续开发中被证明是非常合理的。hiprint的配置和调用逻辑被完全隔离在工具类和组件内部业务页面只需要知道“我要打印什么数据”以及“用哪个模板”不需要关心hiprint内部复杂的事件和渲染机制这对于后续的项目维护和交接来说价值非常大。2. 环境准备与项目初始化2.1 安装与依赖引入本次实践基于Vue 2.6.10和Element-UI 2.15.x版本。安装hiprint相关的依赖非常简单只需要一行命令。hiprint默认使用jQuery作为底层依赖虽然它内部已经封装得很好但安装时仍需注意同时还要安装pdfmake用于生成PDF文件以及pdf-lib处理一些打印的二进制数据操作。npm install vue-hiprint jquery pdfmake pdf-lib -S安装完成后不要急着写业务代码先检查一下node_modules中是否存在hiprint和vue-hiprint两个目录。我最初只安装了vue-hiprint但后来打印时始终报错提示找不到hiprint排查了半天才发现是依赖安装不完整。另外有一点值得注意vue-hiprint的版本更新频率不算高如果你的项目使用的是Vue CLI 4以上的版本建议主动升级到最新的vue-hiprint版本以兼容webpack 4及以上版本的构建方式。2.2 全局配置与样式引入vue-hiprint的使用方式很直接在main.js中引入并注册即可。// main.js import vueHiprint from vue-hiprint Vue.use(vueHiprint)但到这里还没完。hiprint的核心打印样式文件也需要显式引入否则打印出来的内容是没有任何基础样式的会像一堆没有CSS的HTML标签一样堆在纸上。// main.js import vue-hiprint/dist/hiprint.bundle.css这块样式文件是整个项目的隐形基石。我最初忽略了这个步骤导致调用的打印机无法正确识别纸张尺寸打出来的内容乱成一团。这个坑坑得很冤枉但也很典型——依赖包文档里写了但没加粗提示很容易被忽略。2.3 初始化浏览器打印插件hiprint在创建打印模板之前需要先初始化浏览器插件环境。这个初始化会检测当前浏览器环境适配不同内核的打印逻辑。// utils/hiprint-manager.js import hiprint from vue-hiprint/dist/hiprint.bundle.js export function initHiprint() { hiprint.init({ height: 100%, width: 100% }) }初始化动作建议放在App.vue的mounted生命周期中确保整个应用运行期间打印插件是始终可用的。如果放在业务组件里每次进入页面都要重新初始化而且可能出现跨页面调用时插件状态丢失的情况。3. 核心实操模板配置与打印调用3.1 定义JSON格式的打印模板hiprint最核心的用法是通过JSON定义打印模板。这个JSON描述了一张“纸”上要打印哪些内容、分布在什么位置、使用什么字体、对齐方式等等。我以一个常见的“销售出货单”为例一个最简单但结构完整的模板长这样{ paper: { width: 100, height: 150, margin: 5, paperType: A4 }, header: { height: 20, content: [ { type: text, text: 销售出库单, fontSize: 18, fontWeight: bold, align: center, width: 80, height: 10, x: 10, y: 2 } ] }, body: { height: 100, content: [ { type: table, columns: [ { title: 商品名称, field: name, width: 30, align: center }, { title: 数量, field: quantity, width: 20, align: center }, { title: 单价, field: price, width: 20, align: center } ] } ] } }你可能注意到了里面用了paper.paperType字段。这是一个非常聪明的设计它允许你在A4、A5、自定义大小等纸张类型之间切换。在模板编辑阶段我强烈建议把纸张类型暴露成一个下拉框给管理员配置而不是写死在代码里。因为实际业务中同一个用户今天打A4的合同明天可能要打85mm×55mm的快递面单如果都靠开发改代码那这个项目就直接失去意义了。3.2 渲染打印机并提供打印能力当模板JSON和业务数据都准备好之后渲染和打印的调用逻辑就非常简洁了。这一部分我封装成了一个工具函数方便任何页面在需要时直接调用// utils/hiprint-manager.js import hiprint from vue-hiprint/dist/hiprint.bundle.js let printTemplate null export function createPrintTemplate(templateJson) { if (!templateJson) { console.error(模板文件不能为空) return } printTemplate hiprint.createPrintTemplate({ template: templateJson }) } export function renderAndPrint(data) { if (!printTemplate) { throw new Error(模板未初始化请先调用createPrintTemplate) } // 将业务数据传入模板的 setData 方法 printTemplate.setData(data) // 生成打印预览此时会弹出预览窗口 printTemplate.print() }这里有个细节setData方法不仅会绑定数据还会在内部重新计算模板中使用了表达式的内容比如“总金额 数量 × 单价”。所以如果你的模板里有动态计算公式不要试图在业务代码中先算好再传进去直接给原数据让hiprint自己去算格式和精确度反而更好。3.3 使用Element-UI搭建模板编辑与操作界面为了配合这个打印能力我用Element-UI搭建了一个简单的操作页面。页面的左侧放了“模板JSON编辑器”和“业务数据模拟区”右侧则是一个大大的“打印预览”区域。template div classprint-page el-row :gutter20 el-col :span8 el-card div slotheader span配置面板/span el-button typetext clickloadTemplate加载模板/el-button /div el-input typetextarea :rows12 v-modeltemplateJsonString placeholder在此粘贴模板JSON... / /el-card el-card stylemargin-top: 20px; div slotheader span业务数据/span /div el-input typetextarea :rows8 v-modelbusinessDataString placeholder在此粘贴业务JSON数据... / /el-card /el-col el-col :span16 el-card div slotheader span打印预览区域/span el-button typeprimary clickhandlePrint立即打印/el-button /div div classhiprint-preview-area idhiprint-preview/div /el-card /el-col /el-row /div /template预览区域的容器绑定了一个id这是为了让hiprint的预览视图挂载到这个DOM上。它的内部实现是直接在指定容器中渲染一个可交互的“虚拟纸张”用户可以在预览区域里看到最终的打印效果甚至可以调整边距等参数后再次预览这是一个体验非常加分的特性。3.4 参数与预览的联动处理在为这个项目编写“联动逻辑”时有一个关键点当用户修改了模板JSON或者改变了业务数据时预览区需要动态刷新。这里不能简单粗暴地重新创建整个打印模板因为那么做会引起预览区域闪烁而且会让用户已经调好的缩放比例丢失。我的处理方式是只更新数据不重建模板。具体代码如下watch: { businessDataString: { handler(newVal) { try { const data JSON.parse(newVal) if (printTemplate) { printTemplate.setData(data) } } catch (e) { // 数据格式错误时忽略等待用户修正 } }, deep: true } }当模板JSON变化时才重新执行createPrintTemplate。而当业务数据变化时只调用setData来更新预览。这个细节虽然简单但在实际项目里对用户体验的提升非常明显。4. 常见问题与排查技巧实录4.1 打印内容空白或样式全丢这是我被咨询得最多的问题。现象预览窗口正常点击“打印”后能调起打印机但打印出来的纸张完全是空白的或者只有文字没有样式。原因大概率是页面里的style标签被浏览器忽略了或者打印时CSS没有被完整渲染。hiprint的打印实现依赖一个iframe它会复制当前页面的部分样式到打印iframe中。如果你引入hiprint样式的方式不对比如用了动态加载、按需加载就可能出现这个情况。解决方案在入口文件main.js中把hiprint.bundle.css作为固定静态资源引入不要放在组件内部或者通过变量去动态引入确保打包时这个CSS文件始终存在。如果仍然出现检查一下构建配置中是否有对CSS做“按需抽离”的插件例如某些优化插件会调整CSS在loader中的优先级此时需要在vue.config.js里对hiprint的CSS设置extract: false。4.2 表格跨页不重复表头甚至乱序现象打印表格数据很多时第一页表头正常第二页就没有表头了或者表格的行被硬生生切断到两页看起来非常不美观。原因这是很多前端打印框架的通病——浏览器对表格分页的处理策略不一致。有些浏览器会在行内断行有些浏览器会忽略thead标签的重复打印行为。解决方案在hiprint的表格列配置中增加一个分组字段。hiprint原生支持对表格数据做分组打印例如按客户分组{ type: table, columns: [ { title: 商品名称, field: name, width: 30, align: center }, { title: 数量, field: quantity, width: 20, align: center } ], groupType: customer, group: { title: 客户, field: customerName } }这样设置之后hiprint会为每一组数据单独渲染表头并且在组内跨页时尽量维持完整性。实测下来这比任何自写CSS方案的稳定性都要好。4.3 预览正常但某些打印机无法识别自定义纸张现象在A4纸下打印正常但用户设置了一款自定义尺寸的纸条比如“宽80mm高高130mm”在部分打印机上却一直按A4输出。原因这其实更多的是打印机驱动层面不支持自定义纸张而不是hiprint的问题。浏览器在发送打印任务时会设置一个MediaSize参数但有几个型号的打印机驱动对这个参数的支持不完整导致退回到默认纸张规格。解决方案有两个层面可以处理。第一在打印前通过window.print()的参数设置打印机纸张或者在hiprint中指定使用系统已存在的某种自定义纸张类型需要管理员提前在系统里建好。第二如果驱动实在不给力可以改用PDF输出然后由用户通过PDF阅读器自行选择纸张打印printTemplate.setData(data) printTemplate.printByHtml()上述代码会生成一个打印预览窗口用户可以在点击“打印”按钮之前在预览窗口下方的下拉菜单里切换纸张类型。这个方法在实测中成功规避了驱动不兼容的问题因为它是通过浏览器自带的系统对话框选择纸张绕过了驱动限制。4.4 多页面应用中的打印实例污染现象在单页应用中使用hiprint.createPrintTemplate创建了多个模板实例后打印时发现总是打出上一次的模板页面来回切换多次后打印内容变得不可控制。原因这通常是因为我没有做好实例管理。hiprint的全局对象保留了最后一次创建模板的引用在单页应用中由于页面路由跳转旧的模板实例没有被销毁导致下次调用时走了旧的回调逻辑。解决方案在路由切换时显式销毁当前的打印模板实例避免内存泄漏和状态污染// utils/hiprint-manager.js export function destroyPrintTemplate() { if (printTemplate) { printTemplate.destroy() printTemplate null } }然后在组件卸载的生命周期里调用beforeDestroy() { destroyPrintTemplate() }这一步想强调的是即使你的项目只使用一个模板也建议保留这个清理动作因为如果后续增加“多个模板切换打印”的功能这套基础实现可以避免很多隐性bug。4.5 元素位置偏移一个大表格和一个标题错位现象同样的模板、同样的数据在本地开发环境预览打印都正常部署到服务器后部分客户反馈打印出来的内容向左偏移了大概2-3毫米。原因这主要是因为不同客户使用的浏览器缩放比例不同比如Windows系统默认设为“125%”的显示缩放而开发机上可能用的是“100%”。浏览器缩放比例会直接影响设备的devicePixelRatio从而影响打印的物理像素转换。这种情况在开发环境很难模拟因为它取决于客户的浏览器配置而不是代码层面的问题。解决方案这个问题没有完美的代码级修复方案但有两条有效的缓解路径。第一条在预览窗口弹出前检测并提示用户将浏览器缩放恢复为100%第二条把业务场景限制在“可以直接把打印参数保存到模板JSON”的桌面上推荐重点客户统一使用同一个浏览器内核例如统一Chrome并固定缩放为100%。实际项目中这两种方式配合使用后偏移问题显著减少。5. 经验总结与后续扩展建议5.1 从vue-hiprint-example中获得的经验沉淀经过这个项目的实践我个人最大的体会是前端打印功能不是“临时加一个插件”就能解决的事它天然就需要一套从模板设计到数据绑定再到打印预览的完整链路。很多项目在早期会忽略打印模块的扩展性但等到上线后才发现业务功能已经迭代了好几轮打印需求根本无法在原有基础上增加。所以在项目初期接受“打印也是一个独立子系统”这个认知会让后续的每一步都顺利很多。具体到技术层面我建议所有做类似集成的朋友务必把hiprint的调用包装在一个独立的service层中做到全局状态统一管理模板实例唯一。这样无论是后面升级hiprint版本还是改造支持不同打印服务比如换成云打印机都可以在不改动业务代码的前提下完成替换。5.2 值得进一步扩展的方向这个示例项目目前已经能支撑大部分常规打印需求但如果你希望把它提升到更完善的状态有几个方向是值得投入的模板持久化目前模板JSON是前端模拟的数据实际业务中肯定需要把模板配置保存到后端建议设计一张print_template表通过模板编码加载。批量打印能力hiprint提供了getPrintData等方法可以利用循环实现类似“一次选择多个订单批量打印每个订单的独立单据”的功能。条件显示模板不同业务类型使用同一张纸但要求隐藏某些字段。这时可以在JSON中使用if表达式结合数据内容动态控制显示。与后端打印队列集成如果业务量较大可以将打印任务提交到后端走服务端排队和调度前端只负责生成PDF归档。6. 我的个人操作体会最后再分享一个我在实际使用中发现的比较实用的技巧。在hiprint中任何一个可打印元素其实都可以绑定JavaScript表达式而不仅仅是一个数据字段名。比如如果打印内容是一个地址拼接我可以直接在模板中写 address.province address.city address.detail这样一个自定义函数就能处理复杂的数据格式转换而不用在业务代码里先处理好再传给模板对于减少重复工作很有用处。另外在使用vue-hiprint的过程里还有一个常被忽略但很实用的功能它支持打印完自动关闭预览窗口。在很多后台业务场景中用户的诉求是“点击打印-确认-关闭打印预览窗口-返回列表”这个动作越顺畅越好。可以在打印回调中调用window.close()也可以直接使用hiprint.addCallback进行生命周期监听根据业务需求决定是否要自动关闭。这个项目的代码我已经跑通并且在模拟业务数据下做了充分验证。如果你正在做类似的管理系统或报表平台希望这份实践记录能在工具选型、踩坑规避甚至代码结构上给你一点参考。打印这个功能看起来很“小”细节坑却不少提前做好架构上的准备能帮你和团队省掉很多不必要的折腾。本文还有配套的精品资源点击获取