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 渲染需要一些額外的依賴(lài)項(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)目添加一些依賴(lài)項(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
將用戶(hù)輸入實(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 App
Preview.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-09ahooks useVirtualList 封裝虛擬滾動(dòng)列表
這篇文章主要為大家介紹了ahooks useVirtualList 封裝虛擬滾動(dòng)列表詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09react 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-02react-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-05React中hook函數(shù)與useState及useEffect的使用
這篇文章主要介紹了React中hook函數(shù)與useState及useEffect的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10React函數(shù)組件useContext useReducer自定義hooks
這篇文章主要為大家介紹了React函數(shù)組件useContext useReducer自定義hooks示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08