使用 Next.js 生成 PDF 文件
Next.js 是一个基于 React 的框架,最近非常流行,这得益于它一些很棒的功能,例如 SSR、SSG……相比 React Vanilla 来说有很多优势。在 Web 开发中,一个常见的任务是生成 PDF 文件,你可能需要显示一些额外的信息,或者生成一些动态生成数据的报告。它有很多用例。在我最近的一个项目中,我一直在研究如何直接从 next.js 中实现这一点,今天我将向你展示如何做到这一点。
设置
首先,我们需要像平常一样使用 next.js 初始化我们的项目,但要添加生成 pdf 的jsPDF库
mkdir next-pdf
cd ./next-pdf
yarn init --yes
yarn add react react-dom next js-pdf normalize.css
PS:normalize.css 是可选的但很有用,使浏览器更一致地呈现所有元素,强烈建议。
现在编辑你的 package.json 并添加以下几行
...
"scripts": {
"dev": "next",
"start": "next start",
"build": "next build",
},
...
然后运行
mkdir pages
页面/index.js
const app =()=>(<h1>Hello world</h1>);
export default app;
有了这个设置就足以开始了,但如果你喜欢到处使用打字稿(像我一样哈哈)你可以使用下一行来使用它。
touch tsconfig.json
yarn add --dev @types/react @types/node @types/jspdf typescript
将 index.js 重命名为 index.tsx 并最终运行
yarn run dev
好的,我们开始吧
我们将创建一个名为 src 的文件夹,用于放置用于生成 PDF 和样式的组件,我们的脚手架将如下所示
/src/
/components/
GeneratePDF.tsx
/styles/
styles.css
/pages/
index.tsx
_app.tsx
让我们为我们的应用程序添加全局样式,这是在 _app.tsx 上进行的,导入 style.css 和 normalize.css:
import * as React from "react";
import "normalize.css"
import "../src/styles/styles.css";
const MyApp = ({ Component, pageProps }) => {
return (
<Component {...pageProps} />
);
};
export default MyApp;
我们的样式/styles.css
.content{
display:flex;
align-items: center;
flex-direction: column;
}
.main > .content > p, h1{
font-family: sans-serif;
}
.main > .content > p{
font-size: 1.7em;
text-align: justify;
width:80%;
}
.main button{
display: block;
cursor: pointer;
background-color: crimson;
color: white;
font-size: 1.5em;
font-family: sans-serif;
width:8em;
height: 3em;
font-weight: 500;
border-radius: 5px;
border-color: transparent;
margin:0 auto 0 auto;
}
.main .button-container{
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
}
现在我们的主要组件
/src/components/GeneratePDF.tsx
import React from "react";
import { jsPDF,HTMLOptionImage } from "jspdf";
import { toPng,toCanvas } from "html-to-image";
type props = {
html?: React.MutableRefObject<HTMLDivElement>;
};
const GeneratePdf: React.FC<props> = ({ html }) => {
const generatePdf = () => {
const doc = new jsPDF();
let split=doc.splitTextToSize(document.getElementById("text").innerText,200);
let image = document.getElementById("image").getAttribute('src');
doc.text(document.querySelector(".content > h1").innerHTML,75,5);
doc.addImage(image,70,7,60,60);
doc.text(split,5,75);
doc.output("dataurlnewwindow");
};
const generateImage=async ()=>{
const image = await toPng(html.current,{quality:0.95});
const doc = new jsPDF();
doc.addImage(image,'JPEG',5,22,200,160);
doc.save();
}
return (
<div className="button-container">
<button onClick={generateImage}>
Get PDF using image
</button>
<button onClick={generatePdf}>
Get PDF as text
</button>
</div>
);
};
export default GeneratePdf;
解释 = 我们正在创建 2 个按钮来生成 2 个具有相同内容但使用 2 种不同方法的 pdf,generateImage将从我们的 HTML 生成一个图像,我们将它放在 pdf 中,generatePdf只创建 pdf,从我们的 Dom 中获取内容,它们都有各自的优点和缺点
使用图像:
优势
✅ 结果和你的页面完全一样
✅ 易于设置
缺点
❌ 生成速度慢
❌ pdf 文件重量相对较高
❌ 您无法复制和粘贴内容(如果这对您很重要)
使用来自 dom 的内容:
优势
✅ 文件大小轻量
✅ 生成速度快
✅ 文本可选
缺点
❌ 把所有东西都摆在自己的地方并不容易
让我们继续 pages/index.tsx
import * as React from "react";
import Image from "next/image";
import dynamic from "next/dynamic";
const GeneratePDF = dynamic(()=>import("./../src/components/GeneratePDF"),{ssr:false});
const app =()=>{
const ref = React.useRef();
return(<div className="main">
<div className="content" ref={ref}>
<h1>Hello PDF</h1>
<img id="image" src="/images/image_header.jpg" width="300" height="200"/>
<p id="text">
Lorem ipsum dolor sit, amet consectetur adipisicing elit. Quisquam animi, molestiae quaerat assumenda neque culpa ab aliquam facilis eos nesciunt! Voluptatibus eligendi vero amet dolorem omnis provident beatae nihil earum!
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Ea, est. Magni animi fugit voluptates mollitia officia libero in. Voluptatibus nisi assumenda accusamus deserunt sunt quidem in, ab perspiciatis ad rem.
Lorem ipsum dolor sit amet consectetur adipisicing elit. Nihil accusantium reprehenderit, quasi dolorum deserunt, nisi dolores quae officiis odio vel natus! Pariatur enim culpa velit consequatur sapiente natus dicta alias!
Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur, asperiores error laudantium corporis sunt earum incidunt expedita quo quidem delectus fugiat facilis quia impedit sit magni quibusdam ipsam reiciendis quaerat!
</p>
</div>
<GeneratePDF html={ref}/>
</div>);
}
export default app;
解释
我们可以看到另一个组件是用 React 制作的……其实不然,正如你所见,我们从下一个组件开始使用动态
import dynamic from "next/dynamic";
const GeneratePDF = dynamic(()=>import("./../src/components/GeneratePDF"),{ssr:false});
通过这种方式,我们可以动态地导入组件(基本上仅在需要时),并且我们正在停用 SSR(服务器端渲染),因为 jsPDF 需要在浏览器上初始化,否则,我们会从中捕获错误。
现在您可以从下一个应用程序生成 PDF,您可以使用多种方法,例如自动生成表格
感谢阅读
如果您有任何问题或建议,请在下面发表评论,请在这里关注我,也请关注我的推特,保重👍
文章来源:https://dev.to/wonder2210/generate-pdf-files-using-next-js-24dm