You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 04:06:05