核心原理是,动态生成的canvas元素,要在绘制之前,插入到真实的DOM中去。
1 | // 处理html2canvas 生成图片字体丢失 |
1 | // 处理 vue-plugin-hiprint 导出PDF 第三方字体丢失 |
核心原理是,动态生成的canvas元素,要在绘制之前,插入到真实的DOM中去。
1 | // 处理html2canvas 生成图片字体丢失 |
1 | // 处理 vue-plugin-hiprint 导出PDF 第三方字体丢失 |
1 | // 核心兼容代码 |
1 | # 生成包的一个修改路路径 (包名称@版本号) |
修改生成包的内容
1 | # file-path /Users/zhangyu/work/ttzz/ttzzerp-app/node_modules/.pnpm_patches/pdfh5@1.4.9 |
重新patch-commit 修改的包
1 | # 生成patch目录,保存修改的diff信息 (file-path: /Users/zhangyu/work/ttzz/ttzzerp-app/node_modules/.pnpm_patches/pdfh5@1.4.9) |
1 | # 启动项目 |
有一个网页版的报告需要打印成pdf, 供用户下载. 要求打印原版的网页, 美观漂亮.
安装不用多说, 老套路, 没啥子坑.
npm i puppeteer
网上抄一段打印pdf的代码.
... let browser = await puppeteer.launch(); let page = await browser.newPage(); let options = { waitUntil: 'networkidle0' }; page.setCookie(...cookies); let url = `http://0.0.0.0/page/report/down?rid=3a1325b6`; await page.goto(url, options); let pdf = await page.pdf({format: 'A4', fullPage: true); await page.close(); await browser.close(); ... res.set({ 'Content-Disposition': `attachment`, 'filename': 'report.pdf', 'Content-Type': 'application/octet-stream', 'Content-Length': pdf.length }); res.send(pdf);
这段代码大部分执行都没啥子问题, 但没有输出report.pdf文件. 好吧, 逐一填坑.
修改res.set的内容, 把文件名写到Content-Disposition对象上去.
旧:
res.set({ 'Content-Disposition': 'attachment', 'filename': 'report.pdf', 'Content-Type': 'application/octet-stream', 'Content-Length': pdf.length })
修改后:
res.set({ 'Content-Disposition': `attachment;filename=report.pdf`, 'Content-Type': 'application/octet-stream', 'Content-Length': pdf.length });
经过修改, 这次输出了pdf文件.
但是, 新坑来了, pdf只有一页, 并且显示不合, 仅显示网页上边中间的部分内容, 没有背景, 整个内容奇丑无比且, 看来是应该是pdf的设置不正确, 丢失了背景色, A4大小也不能适用于 1200宽的网页.
这个好解决, 加入一个参数即可 :
let pdf = await page.pdf({
format: 'A4', printBackground: true });
这是花了我时间最多的地方, 并一度放弃改为图片输出. 但后来将就着找到解决办法.
不通的办法:
是不是因为网页没有渲染完成呢? 因为是vue项目, 先下回代码来, 然后再由浏览器计算得到网页, 极有可能呀, 改吧.
let options = { waitUntil: 'networkidle0' };
let options = { waitUntil: 'networkidle2' };
page.waitForTimeout(8000);
page.waitForFunction('window.renderdone', ...
上面全部失败, 根本不是解决问题的核心点.
看来, 解决的途径还应该是读取正确的网页宽高值, 可上面的试过的方法怎么就不起作用了. 经详细检查, 发现, 读取的body的宽度值不正确, 高度总是0, 所以, 打印的pdf肯定不正确.
打印了一下另外一个网站的网页, 我K, 竟然全页成功了. 可为什么我的不行呢? 我的网页是VUE写的, 与其他有什么不同呢? 最终都是生成html呀. ..
chrome 开发工具检查一下宽高值, 发现 body 元素真的是 0高度, 无语. 改用 id 为app的元素, 成功读取到了网页的宽高值.
具体代码如下:
const dimensions = await page.evaluate(() => { return { width: document.getElementById("app").scrollWidth + 100, height: document.getElementById("app").scrollHeight + 100, deviceScaleFactor: window.devicePixelRatio }; }); let pdf = await page.pdf({ margin: { top: '0.5cm', bottom: '0.5cm', left: '0.5cm', right: '0.5cm' },
printBackground: true,
width: dimensions.width,
height: dimensions.height });
顺便, 打印一下pdf的边距, 设成0.5cm, 并把网页的宽度高度各加了100, 以抵销边距的开销, 保证内容不丢失.
上面打印的网页只是一个示例页, 还不是最终的页面, 因为最终页面需要登录, 得需要解决登录的问题.
这个网站使用的cookie中存着用户信息 userInfo. 和一个原始的token, 在具体的网页中, 需要读取这两个cookie值完成用户的检查.
于是加上cookie的内容:
let cookies = [{ name: 'token', value: token }, { name: 'userInfo', value: userInfo, }];
我去, 竟然不起作用. 为什么? 查找资源, 原来cookie的对象内容不正确,要把域名, 过期时间, 路径等等都写上.
最终的结果:
let cookies = [{ name: 'token', value: token, path: '/', domain: conf.app.domain, expires: -1, httpOnly: false, secure: false, session: true }, { name: 'userInfo', value: userInfo, path: '/', domain: conf.domain, expires: -1, httpOnly: false, secure: false, session: true }]; page.setCookie(...cookies);
这次读取到了cookie, 开心.
最终的结果:
可以打印完整网页的pdf, 但是仅一页, 不像其他pdf一样, 一个页面一个页面的, 这个就不再改了, 一来是分页比较麻烦, 二来是即便是分了, 数据内容被拆到多页,反而不利于阅读. 三就是我不会拆页(汗).
完成pdf的打印, 又顺手试了一下png的打印, 没有问题, 只要把图片的大小设置好, 成功打印了一个png图片.
好了, 线上部署吧.
使用的centos 7的服务器,
正确上传代码,然后 "npm i"
正在所预料的不正确, 改为"cnpm i", 非常快, 100多M在服务器上是小case.
启动项目, .. 尝试打印一个网页, 报错, 说什么browser没有启动.
找解决方法, 明明是本机测试正常的代码, 到了服务器就不行呢, ... 可能是macos和centos的差异吧.
网搜解决办法, 需要安装一些依赖的包:
yum install pango.x86_64 libXcomposite.x86_64 libXcursor.x86_64 libXdamage.x86_64 libXext.x86_64 libXi.x86_64 libXtst.x86_64 cups-libs.x86_64 libXScrnSaver.x86_64 libXrandr.x86_64 GConf2.x86_64 alsa-lib.x86_64 atk.x86_64 gtk3.x86_64 ipa-gothic-fonts xorg-x11-fonts-100dpi xorg-x11-fonts-75dpi xorg-x11-utils xorg-x11-fonts-cyrillic xorg-x11-fonts-Type1 xorg-x11-fonts-misc
好了. 很快安装完毕, 终于可以启动了. 什么? 又报错了, 看了一下错误原因, brower启动不能在不使用sand-box时情况以root用户吂动.
搜索一下吧, 很快找到了答案:
let browserOptions = { args: ['--no-sandbox', '--disable-setuid-sandbox'] };
let browser = await puppeteer.launch(browserOptions);
加上一个参数即可, 我懒得换服务器上的账号了, 就这样将就着吧.
再打印一个pdf吧.... 怎么, 里面的汉字全成了框框与叉叉了! 应该是服务器上没有字体. 这个在其他项目上解决过, 这次就好办了.
轻车熟路:
yum groupinstall Fonts
安装的也很快, 这次终于打印了一个完美的网页.
散花了.
谷歌浏览器是全球最受欢迎的网络浏览器之一,为用户提供功能强大、功能丰富且用户友好的体验。对于 Debian 12 书虫用户,安装 Google Chrome 可确保最佳的网页浏览性能和访问大量扩展程序。

第 1 步。在我们安装任何软件之前,通过在终端中运行以下命令来确保您的系统是最新的非常重要:apt
1 | sudo apt update |
此命令将刷新存储库,允许您安装最新版本的软件包。
第 2 步。在 Debian 12 上安装 Google Chrome。
谷歌为Chrome提供了一个专用的存储库,简化了安装过程。使用以下命令将谷歌浏览器存储库添加到您的系统:
1 | wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/google-archive-keyring.gpg |
接下来,执行以下命令以安装谷歌浏览器存储库:
1 | echo "deb [signed-by=/usr/share/keyrings/google-archive-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list |
将谷歌浏览器存储库添加到您的系统中后,现在使用以下命令安装谷歌浏览器:
1 | sudo apt update |
打开终端并执行以下命令下载谷歌浏览器软件包:
1 | wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb |
接下来,导航到保存 Google Chrome 软件包的目录,然后执行以下命令来安装软件包:
1 | sudo apt install ./google-chrome-stable_current_amd64.deb |
包管理器将处理安装过程,包括任何必要的依赖项。
第 3 步。在 Debian 上访问 Google Chrome。
要从应用程序菜单启动谷歌浏览器,请单击屏幕左上角的“活动”按钮。然后,搜索“谷歌浏览器”并单击图标以打开浏览器。

第 4 步。排查常见问题。
尽管安装过程通常很简单,但您可能会遇到一些常见问题。以下是解决这些问题的一些故障排除提示:
如果在安装过程中遇到依赖项冲突,请运行以下命令进行修复:
1 | sudo apt --fix-broken install |
如果出现安装错误,请删除所有部分安装,然后重新安装谷歌浏览器:
1 | sudo apt purge google-chrome-stable |
感谢您使用本教程在 Debian 12 书虫上安装最新版本的 Chrome 网页浏览器。如需其他帮助或有用信息,我们建议您查看 Chrome 官方网站。
</article>
</div>
1 | // type 标记了 这是一个列表页面 |

1 | // type 表示了这是一个form组组件,在使用的时候去通过定义 pageId: 215,调用这个form组件。 |

面试时间 2023年7月20日16点
1. TCP协议为什么是三次握手,四次挥手,为什么断开时会多一次?
因为一个连接建立之前,并不需要考虑其他因素,但是在连接断开之前,客户端和服务端可能还存在其他通信操作,所以就需要多一次连接用来通知双方取消其他通信操作。
2. VUE&SSR 原理,在注水时框架做了什么操作,对比CSR有什么缺点?
Vue框架在浏览器注水时,会根据服务端返回的HTML文本对应生成VNode(虚拟DOM),并且将HTML文本中的静态State等数据初始化在VNode中作为初始化状态数据,以及绑定事件等操作。
3. HTTP协议在每个版本都改进了什么?
http各版本的改进都是什么?
4. WebPack&Vue组件在编译过程是什么样的,Vue3比Vue2做了哪些优化?
将模板进行词法分析,转化为对应的ast树(JS描述对象,与虚拟DOM原理差不多)。
转换流程transform,对动态节点做一些标记
生成代码codegen – 虚拟dom
经过render方法将该虚拟dom挂载到宿主元素上
render时直接比对动态节点。
5. 笔试题
1 | class Lottery { |
使用webpack5 默认的配置,在开发阶段编译速度太慢,优化完成之后,可以从60S的速度提升到1S的速度~
初次启动项目需要将近100S
热更新耗时68S 
初次启动项目仅需要不到30S
热更新耗时不到2S 
1. 处理webpack缓存
官方文档
1 | // webpack.config.js |
2. 处理babel-loader缓存
官方文档
1 | // 在rules: babel-loader 中,如果 将node_modules 不排除的话,将会有不可计数的modules参与计算,会大大减慢编译速度。 |
3. 代码压缩在开发过程中,无需压缩代码
官方文档
1 | // minimize 开发过程中无需代码压缩 |
1 | class MyPromise { |
1 | // 使用while递归进行原型链的判断 |
1 | class MyPromise { |