如何在Remix中加载依赖window对象的npm库?
问题分析与解决方案
你遇到的问题不是Remix和print-js不兼容,而是print-js在模块加载阶段就会访问window对象——哪怕你没调用它的打印函数,只要代码里导入了这个库,Remix的服务端渲染(SSR)环境就会执行到这部分代码,而服务端没有window,所以直接报错。
下面是两种可行的解决方法,确保print-js仅在浏览器环境加载:
方法1:动态导入print-js(推荐)
利用React的useEffect在组件挂载后(仅浏览器环境)动态导入print-js,避免服务端加载该模块:
import { useEffect, useState } from 'react'; export default function PdfPrintComponent() { const [printJS, setPrintJS] = useState(null); // 假设pdfData是你已加载的PDF base64数据 const pdfData = "你的PDF base64字符串"; useEffect(() => { // 仅在浏览器环境动态导入print-js import('print-js').then(module => { setPrintJS(module.default); }); }, []); const onPrintPrintJS = () => { if (printJS) { printJS({ printable: pdfData, type: 'pdf', base64: true }); } }; return ( <button onClick={onPrintPrintJS} disabled={!printJS}> 打印PDF </button> ); }
方法2:封装ClientOnly组件隔离浏览器代码
把依赖print-js的逻辑放到仅在浏览器渲染的组件中,服务端会跳过这部分代码的执行:
首先创建一个通用的ClientOnly组件:
// app/components/ClientOnly.jsx import { useEffect, useState } from 'react'; export default function ClientOnly({ children, fallback = null }) { const [hasMounted, setHasMounted] = useState(false); useEffect(() => { setHasMounted(true); }, []); return hasMounted ? children : fallback; }
然后在页面中使用:
import ClientOnly from '~/components/ClientOnly'; export default function PdfPage() { const pdfData = "你的PDF base64字符串"; return ( <div> {/* 其他支持服务端渲染的内容 */} <ClientOnly fallback={<button disabled>加载中...</button>}> {() => { const printJS = require('print-js').default; const handlePrint = () => { printJS({ printable: pdfData, type: 'pdf', base64: true }); }; return <button onClick={handlePrint}>打印PDF</button>; }} </ClientOnly> </div> ); }
关键补充
你之前加的typeof document !== "undefined"保护只对函数调用有效,但print-js的模块本身在导入时就会执行依赖window的初始化代码,所以必须从加载时机上确保它只在浏览器环境被引入,而不是只保护函数调用。
内容的提问来源于stack exchange,提问作者MarcosCunhaLima
相关产品推荐
相关产品推荐

