react-pdf?打造在線簡(jiǎn)歷生成器的示例代碼
前言
PDF 格式是30年前開(kāi)發(fā)的文件格式,并且是使用最廣泛的文件格式之一,我們最喜歡使用它作為簡(jiǎn)歷、合同、發(fā)票、電子書(shū)等文件的格式,最主要的原因是文檔格式可以兼容多種設(shè)備和應(yīng)用程序,而且內(nèi)容 100%保持相同的格式。
React-PDF 簡(jiǎn)介
React PDF 是一個(gè)使用 React 創(chuàng)建 PDF 文件的工具,支持在瀏覽器、移動(dòng)設(shè)備和服務(wù)器上創(chuàng)建PDF文件。
可以用它們輕松地將內(nèi)容呈現(xiàn)到文檔中,我們可以使用 CSS 屬性進(jìn)行樣式設(shè)置,使用 flexbox 進(jìn)行布局,它支持渲染文本、圖像、 svg 等等,詳情可以參考官網(wǎng)
程序?qū)崿F(xiàn)
今天我將使用 React-pdf 和 next.js 來(lái)構(gòu)建一個(gè)在線簡(jiǎn)歷生成器,先一起來(lái)看下效果

在線地址:cv.runjs.cool/
初始化項(xiàng)目
yarn create next-app --example with-ant-design next-resume cd next-resume yarn add @react-pdf/renderer
React-pdf 渲染需要一些額外的依賴項(xiàng)和 webpack5 配置。
yarn add process browserify-zlib stream-browserify util buffer assert
這一步驟是因?yàn)?React-pdf 構(gòu)建在 PDFKit 的基礎(chǔ)之上,在使用瀏覽器時(shí)需要使用兩個(gè) node.js API polyfill。 而 webpack 5不再包括自動(dòng)引入 nodejs polyfill ,我們必須選擇進(jìn)入所有我們想要的 polyfill。為了做到這一點(diǎn),我們必須為我們的項(xiàng)目添加一些依賴項(xiàng):
在根目錄下創(chuàng)建一個(gè) next.config.js
module.exports = {
reactStrictMode: true,
webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
config.resolve.fallback = {
...config.resolve.fallback,
module: "empty",
dgram: "empty",
dns: "mock",
fs: "empty",
http2: "empty",
net: "empty",
tls: "empty",
child_process: "empty",
process: require.resolve("process/browser"),
zlib: require.resolve("browserify-zlib"),
stream: require.resolve("stream-browserify"),
util: require.resolve("util"),
buffer: require.resolve("buffer"),
asset: require.resolve("assert"),
};
config.plugins.push(
new webpack.ProvidePlugin({
Buffer: ["buffer", "Buffer"],
process: "process/browser",
})
);
return config;
},
};實(shí)現(xiàn)邏輯
新建在 App.js 將用戶輸入實(shí)時(shí)綁定到 state 中,然后時(shí)時(shí)渲染預(yù)覽頁(yè)面
import Preview from './component/Preview'
import React, { useState } from 'react'
function App() {
const [profile, setProfile] = useState({
name: "狂奔滴小馬",
about: "分享 Javascript 熱門(mén)\n框架,探索 web 極致\n優(yōu)化體驗(yàn)。",
email: "maqi1520@qq.com",
avatar:"https://p6-passport.byteacctimg.com/img/user-avatar/585e1491713363bc8f67d06c485e8260~300x300.image",
})
const handleChange = (name, value) => {
setProfile({ ...profile, [name]: value })
}
return (
<div
style={{
width: '100%',
height: '100vh',
display: 'flex',
}}
>
<div style={{ width: '50%' }}>
<div>
<label>姓名</label>
<input
name='name'
defaultValue={profile.name}
onChange={(e) => {
handleChange(e.target.name, e.target.value)
}}
/>
</div>
<div>
<label>頭像地址</label>
<input
name='avatar'
defaultValue={profile.avatar}
onChange={(e) => {
handleChange(e.target.name, e.target.value)
}}
/>
</div>
<div>
<label>簡(jiǎn)介</label>
<input
name='about'
defaultValue={profile.about}
onChange={(e) => {
handleChange(e.target.name, e.target.value)
}}
/>
</div>
<div>
<label>email</label>
<input
name='email'
defaultValue={profile.email}
onChange={(e) => {
handleChange(e.target.name, e.target.value)
}}
/>
</div>
</div>
<Preview profile={profile} />
</div>
)
}
export default AppPreview.js 是頁(yè)面的右側(cè)部分,并嵌入我們將要?jiǎng)?chuàng)建的PDF文檔。
另外我們還有 PDFDownloadLink,它可以用來(lái)下載 pdf 文件。
import React from 'react'
import { Document, Page, PDFViewer, PDFDownloadLink } from '@react-pdf/renderer'
import LeftSection from './LeftSection'
import RightSection from './RightSection'
import styles from '../styles'
const Preview = ({ profile }) => {
return (
<div style={{ flexGrow: 1 }}>
<PDFViewer
showToolbar={false}
style={{
width: '100%',
height: '95%',
}}
>
<Template profile={profile} />
</PDFViewer>
<PDFDownloadLink
document={<Template profile={profile} />}
fileName='somename.pdf'
>
{({ loading }) => (loading ? 'Loading document...' : 'Download now!')}
</PDFDownloadLink>
</div>
)
}
// 創(chuàng)建文檔組件
const Template = ({ profile }) => {
return (
<Document>
<Page size='A4' style={styles.page}>
<LeftSection profile={profile} />
<RightSection about={profile.about} />
</Page>
</Document>
)
}
export default Preview我們可以直接設(shè)置 PDF 為 A4 紙尺寸。
import { StyleSheet } from '@react-pdf/renderer'
export default StyleSheet.create({
page: {
display: 'flex',
flexDirection: 'row',
},
section_right: {
margin: 10,
padding: 10,
paddingTop: 20,
width: '75%',
},
section_left: {
width: '25%',
height: '100%',
backgroundColor: '#084c41',
},
profile_container: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: '20',
marginBottom: '20px',
height: '150',
},
name_text: {
paddingTop: '10px',
paddingBottom: '5px',
fontSize: '14px',
fontWeight: '900',
color: 'white',
}
})通過(guò) StyleSheet.create 創(chuàng)建 JavaScript 樣式表
LeftSection.js 代碼展示
import { View, Text, Image, } from '@react-pdf/renderer'
import styles from '../styles'
export const Profile = ({ profile }) => {
return (
<View style={styles.profile_container}>
<Image style={styles.profile_img} src={profile.avatar} />
<View
style={{
justifyContent: 'center',
}}
>
<Text style={styles.name_text}>{profile.name}</Text>
</View>
<Text style={styles.profession_text}>{profile.about}</Text>
</View>
)
}
const LeftSection = ({ profile }) => {
return (
<View style={styles.section_left}>
<Profile profile={profile} />
</View>
)
}
export default LeftSection也可以直接寫(xiě)內(nèi)聯(lián)樣式控制 FDF 內(nèi)的樣式。但是不支持 float 浮動(dòng)屬性,具體大家可以看官網(wǎng)
遇到問(wèn)題
本以為這樣就可以完成,沒(méi)想到還有一個(gè)巨坑,不支持中文,中文在pdf 中會(huì)顯示亂碼, 通過(guò) issue 找到了答案
import { StyleSheet, Font } from "@react-pdf/renderer";
Font.register({
family: "Alibaba-PuHuiTi-Light",
src: "/Alibaba-PuHuiTi-Light.ttf",
});
export const styles = StyleSheet.create({
page: {
fontFamily: "Alibaba-PuHuiTi-Light",
flexDirection: "row",
display: "flex",
...
},
})然后就可以顯示中文字體了。這邊我下載了阿里巴巴普惠體。
重構(gòu)
以上是一個(gè)簡(jiǎn)易版的實(shí)現(xiàn),通過(guò)上面的代碼示例,你應(yīng)該至少看懂了原理,為了讓整個(gè)簡(jiǎn)歷數(shù)據(jù)豐富,我使用了antd 來(lái)實(shí)現(xiàn)豐富的表單列表。使用 react context 來(lái)管理我們的數(shù)據(jù)。下面展示下目錄結(jié)構(gòu):
├── components
│ ├── app
│ │ └── index.tsx
│ ├── editor
│ │ ├── FormCreator.tsx
│ │ ├── conifg.js
│ │ └── index.tsx
│ ├── icon
│ │ └── index.tsx
│ └── preview
│ ├── avatar.tsx
│ ├── awardList.tsx
│ ├── educationList.tsx
│ ├── index.tsx
│ ├── profile.tsx
│ ├── projectList.tsx
│ ├── skillList.tsx
│ ├── style.ts
│ └── workExpList.tsx
├── context
│ └── resumeContext.ts
├── hooks
│ └── useResume
│ └── index.ts
├── pages
│ ├── _app.tsx
│ ├── api
│ │ └── hello.js
│ └── index.tsx
└── styles
├── logo.png
└── globals.css部署
最后我使用 vercel 部署并且綁定自定義域名
體驗(yàn)地址 cv.runjs.cool/
參考
到此這篇關(guān)于react-pdf 打造在線簡(jiǎn)歷生成器的示例代碼的文章就介紹到這了,更多相關(guān)react-pdf 在線簡(jiǎn)歷生成器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react-intl實(shí)現(xiàn)React國(guó)際化多語(yǔ)言的方法
這篇文章主要介紹了react-intl實(shí)現(xiàn)React國(guó)際化多語(yǔ)言的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
ahooks useVirtualList 封裝虛擬滾動(dòng)列表
這篇文章主要為大家介紹了ahooks useVirtualList 封裝虛擬滾動(dòng)列表詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
react native 原生模塊橋接的簡(jiǎn)單說(shuō)明小結(jié)
這篇文章主要介紹了react native 原生模塊橋接的簡(jiǎn)單說(shuō)明小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02
react-router實(shí)現(xiàn)跳轉(zhuǎn)傳值的方法示例
這篇文章主要給大家介紹了關(guān)于react-router實(shí)現(xiàn)跳轉(zhuǎn)傳值的相關(guān)資料,文中給出了詳細(xì)的示例代碼,對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-05-05
React中hook函數(shù)與useState及useEffect的使用
這篇文章主要介紹了React中hook函數(shù)與useState及useEffect的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10
React函數(shù)組件useContext useReducer自定義hooks
這篇文章主要為大家介紹了React函數(shù)組件useContext useReducer自定義hooks示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08

