NextJS中CoinbaseWalletSDK服务端渲染报错求助
问题根源
你当前的核心问题是:getServerSideProps 在服务器端运行,而你在其中导入的 coinbase 服务模块,在加载时就直接执行了 new CoinbaseWalletSDK()——服务器环境没有 window 和 localStorage,因此必然抛出 ReferenceError。动态导入在这里无效,因为模块顶层的初始化代码依然会在服务器端执行。
解决方案
需要将 Coinbase Wallet SDK 的初始化逻辑完全限制在客户端环境执行,同时重构服务代码,避免模块加载时自动初始化。
步骤1:重构 Coinbase 服务代码
将初始化逻辑封装为函数,且仅在客户端环境下执行:
// components/services/coinbase.ts import CoinbaseWalletSDK from '@coinbase/wallet-sdk' import Web3 from 'web3' const APP_NAME = 'Practice App' const APP_LOGO_URL = process.env.WEBSITE_URL + '/logo.png' const DEFAULT_ETH_JSONRPC_URL = 'https://mainnet.infura.io/v3/' + process.env.INFURA_PROJECT_ID const DEFAULT_CHAIN_ID = 1 // 仅在客户端环境执行初始化 const isClient = typeof window !== 'undefined' export const initCoinbaseWallet = () => { if (!isClient) { throw new Error('Coinbase Wallet SDK 只能在客户端初始化') } const coinbaseWallet = new CoinbaseWalletSDK({ appName: APP_NAME, appLogoUrl: APP_LOGO_URL, darkMode: false }) const coinbaseEth = coinbaseWallet.makeWeb3Provider(DEFAULT_ETH_JSONRPC_URL, DEFAULT_CHAIN_ID) const web3 = new Web3(coinbaseEth as any) return { coinbaseWallet, coinbaseEth, web3 } }
步骤2:修改页面的 getServerSideProps
移除其中与 Coinbase 相关的导入和逻辑,仅保留项目数据处理:
export async function getServerSideProps({ params, }: { params: { project_id: string }; }) { const project_id = params.project_id; let project: any = fakeProjects[0]; if (project_id && typeof project_id === 'string' && !isNaN(parseInt(project_id))) { const id = project_id; project = fakeProjects.find(p => p.id === parseInt(id)); let item = await getNFTStatsByProjectId(parseInt(project_id)); if (project && item && item['nftTotal'] && item['nftSold']) { if (item.nftSold > item.nftTotal) { item.nftSold = item.nftTotal; } project.nftTotal = item.nftTotal; project.nftSold = item.nftSold; } } return { props: { project: project }, }; }
步骤3:在页面组件中客户端初始化钱包
使用 useEffect(仅在客户端执行)来初始化 SDK,或者用 Next.js 的动态导入禁用 SSR:
方案A:直接在页面组件中用 useEffect
import { useEffect, useState } from 'react' import { initCoinbaseWallet } from '../../components/services/coinbase' export default function ProjectPage({ project }: { project: any }) { const [coinbaseEth, setCoinbaseEth] = useState<any>(null) useEffect(() => { const initWallet = async () => { try { const { coinbaseEth } = initCoinbaseWallet() setCoinbaseEth(coinbaseEth) } catch (err) { console.error('初始化 Coinbase 钱包失败:', err) } } initWallet() }, []) return ( <div> <h1>{project.name}</h1> {coinbaseEth ? ( <button onClick={() => coinbaseEth.enable()}>连接 Coinbase 钱包</button> ) : ( <p>加载钱包中...</p> )} </div> ) }
方案B:动态导入钱包组件(更优雅)
创建独立的钱包组件:
// components/CoinbaseWalletButton.tsx import { useEffect, useState } from 'react' import { initCoinbaseWallet } from './services/coinbase' export default function CoinbaseWalletButton() { const [coinbaseEth, setCoinbaseEth] = useState<any>(null) useEffect(() => { const { coinbaseEth } = initCoinbaseWallet() setCoinbaseEth(coinbaseEth) }, []) const handleConnect = async () => { if (coinbaseEth) { try { await coinbaseEth.enable() console.log('钱包已连接') } catch (err) { console.error('连接失败:', err) } } } return ( <button onClick={handleConnect} disabled={!coinbaseEth}> {coinbaseEth ? '连接 Coinbase 钱包' : '加载中...'} </button> ) }
在页面组件中动态导入该组件并禁用 SSR:
import dynamic from 'next/dynamic' const CoinbaseWalletButton = dynamic(() => import('../../components/CoinbaseWalletButton'), { ssr: false, loading: () => <p>加载钱包按钮中...</p> }) export default function ProjectPage({ project }: { project: any }) { return ( <div> <h1>{project.name}</h1> <CoinbaseWalletButton /> </div> ) }
关键注意事项
- 任何依赖
window、localStorage的代码,都不能在服务器端执行(包括模块顶层代码、getServerSideProps、getStaticProps) - 通过
typeof window !== 'undefined'可以准确判断是否处于客户端环境 - 动态导入时设置
ssr: false,会强制组件仅在客户端加载和渲染
内容的提问来源于stack exchange,提问作者Terry Windwalker
相关产品推荐
相关产品推荐

