ReactJS项目使用<PDFDownloadLink/>组件导致网站卡顿如何优化
问题根源诊断
你当前的写法存在3个核心问题,导致PDFDownloadLink频繁重渲染、提前生成PDF占用主线程:
- 你把
MyDocument组件直接定义在父组件内部,父组件每次重渲染都会重新创建全新的MyDocument函数实例,触发PDFDownloadLink的重渲染 - 传递给
document属性的是<MyDocument/>JSX节点,每次渲染都会生成新的节点引用,导致PDFDownloadLink误以为需要重新生成PDF MyDocument内部调用了getCurrentDate(),如果这个函数每次执行都返回新的时间戳(精确到秒/毫秒),那每次渲染PDF内容都会变化,强制触发重新生成
优化方案(按优先级排序)
1. 把MyDocument组件抽离到父组件外部,用useMemo缓存依赖
将MyDocument移到当前父组件的外层,所有依赖的动态参数(freightType、collname、logo等)通过props传递,再用useMemo缓存document节点,只有当依赖的PDF数据变化时才重新生成节点:
// 抽离到父组件外部,不要嵌套在父组件里定义 const MyDocument = ({ logo, freightType, collname, collcompany, colladdress, collphone, collemail, collcountry, collcity, collcountrycode, delcountrycode, delname, delcompany, deladdress, delphone, delemail, delcountry, delcity, rangeval, shipContent, pieces, shipValue, barcode, currentDate }) => ( <Document> <Page size="A4" orientation="landscape" style={styles.page}> <View style={styles.sectionHead}> <Image style={styles.image} src={logo}/> <Text style={styles.freightCode}>{freightType}</Text> <View style={styles.headDate}> </View> </View> <View style={styles.sectionInfo}> <View style={styles.sectionInfo1}> <Text style={styles.sectionInfoHeader}>COLLECTION:</Text> <Text style={styles.sectionInfoText}>{collname}</Text> <Text style={styles.sectionInfoText}>{collcompany}</Text> <Text style={styles.sectionInfoText}>{colladdress}</Text> <Text style={styles.sectionInfoText}>{collphone}</Text> <Text style={styles.sectionInfoText}>{collemail}</Text> </View> <View style={styles.sectionInfo2}> <Text style={styles.countryBlock}>{collcountry}</Text> <Text style={styles.locationBlock}>{collcity}</Text> </View> </View> <View style={styles.sectionCodeLocation}> <Text style={styles.collDelCode}>{collcountrycode} - {freightType} - {delcountrycode}</Text> </View> <View style={styles.sectionInfo}> <View style={styles.sectionInfo1}> <Text>DELIVERY:</Text> <Text style={styles.sectionInfoText}>{delname}</Text> <Text style={styles.sectionInfoText}>{delcompany}</Text> <Text style={styles.sectionInfoText}>{deladdress}</Text> <Text style={styles.sectionInfoText}>{delphone}</Text> <Text style={styles.sectionInfoText}>{delemail}</Text> </View> <View style={styles.sectionInfo2}> <Text style={styles.countryBlock}>{delcountry}</Text> <Text style={styles.locationBlock}>{delcity}</Text> </View> </View> <View style={styles.sectionDetail}> <View style={styles.sectionInfo1}> <Text style={styles.weightCode}>Weight: {rangeval}</Text> <Text style={styles.weightCode}>Content: {shipContent}</Text> </View> <View style={styles.sectionInfo2}> <Text style={styles.weightCode}>Pieces: {pieces}</Text> <Text style={styles.weightCode}>Value: {shipValue} BHD</Text> </View> </View> <View style={styles.sectionAWB}> <Image style={styles.barcode} src={barcode}/> </View> <View style={styles.footerDetail}> <Text>*Freight DOC* </Text> <Text>{currentDate}</Text> </View> </Page> </Document> ); // 父组件内部 const ParentComponent = () => { // 你的其他状态、变量定义 // 先把日期缓存,只有当PDF需要更新时才重新生成日期,不要每次渲染都调用getCurrentDate const currentDate = useMemo(() => getCurrentDate(), [/* 这里填触发PDF更新的依赖,比如运单提交成功的状态 */]) // 缓存PDF文档节点,只有所有依赖变化时才重新生成 const pdfDocument = useMemo(() => ( <MyDocument logo={logo} freightType={freightType} collname={collname} collcompany={collcompany} colladdress={colladdress} collphone={collphone} collemail={collemail} collcountry={collcountry} collcity={collcity} collcountrycode={collcountrycode} delcountrycode={delcountrycode} delname={delname} delcompany={delcompany} deladdress={deladdress} delphone={delphone} delemail={delemail} delcountry={delcountry} delcity={delcity} rangeval={rangeval} shipContent={shipContent} pieces={pieces} shipValue={shipValue} barcode={barcode} currentDate={currentDate} /> ), [ logo, freightType, collname, collcompany, colladdress, collphone, collemail, collcountry, collcity, collcountrycode, delcountrycode, delname, delcompany, deladdress, delphone, delemail, delcountry, delcity, rangeval, shipContent, pieces, shipValue, barcode, currentDate ]) return ( <li> <PDFDownloadLink document={pdfDocument} fileName="waybill.pdf"> {({ loading }) => loading ? '生成中...' : '下载运单'} </PDFDownloadLink> </li> ) }
2. 按需延迟生成PDF
如果你的场景是用户完成指定操作(比如提交表单、生成运单)后才需要下载PDF,可以再加个开关控制,只有操作完成后才渲染PDFDownloadLink:
const [isOrderConfirmed, setIsOrderConfirmed] = useState(false) // 操作完成的回调里把isOrderConfirmed设为true // 渲染部分 return ( <li> {isOrderConfirmed ? ( <PDFDownloadLink document={pdfDocument} fileName="waybill.pdf"> {({ loading }) => loading ? '生成中...' : '下载运单'} </PDFDownloadLink> ) : ( <button disabled>请先完成操作</button> )} </li> )
3. 用React.memo包装下载按钮组件
如果PDFDownloadLink还是会被父组件无关状态的更新触发重渲染,可以把整个下载按钮抽成独立组件,用React.memo做浅比较缓存:
const DownloadButton = React.memo(({ pdfDocument }) => ( <PDFDownloadLink document={pdfDocument} fileName="waybill.pdf"> {({ loading }) => loading ? '生成中...' : '下载运单'} </PDFDownloadLink> ))
内容的提问来源于stack exchange,提问作者motionless570
相关产品推荐
相关产品推荐

