React项目CSR渲染时如何自动剔除SSR相关代码
React项目CSR构建自动剔除SSR代码实现方案
核心实现逻辑和Next.js的代码消除能力一致,完全基于构建阶段的静态死代码消除实现,不需要运行时做额外判断,最终可以做到CSR产物里零SSR侧残留代码。
1. 构建阶段注入环境标记常量
不要用typeof window这类运行时可变化的条件做判断,这类条件无法被打包器100%静态求值,很容易导致代码残留。
- 给CSR、SSR的独立构建流程分别注入不可变的全局常量:
- Webpack场景用
DefinePlugin配置,Vite场景用define配置,Rollup场景用@rollup/plugin-replace配置 - CSR构建时固定注入
__BUILD_TARGET__ = 'client',SSR构建时固定注入__BUILD_TARGET__ = 'server'
- Webpack场景用
- 同文件内的SSR/CSR分支代码必须用常量做直接的相等判断,不要封装成函数再调用:
// 正确写法,静态分析可直接识别分支 let renderLogic; if (__BUILD_TARGET__ === 'server') { // SSR专属逻辑:服务端数据拉取、renderToString处理、服务端依赖调用 renderLogic = await import('./ssr-render'); export async function getServerData() { return db.query('select site config'); } } else { // CSR专属逻辑:客户端hydrate、事件绑定、浏览器API调用 renderLogic = await import('./csr-render'); } // 错误写法,静态分析无法识别,不会触发代码消除 const isServer = () => typeof window === 'undefined'; if (isServer()) { /* SSR逻辑 */ }
2. 开启打包器的生产环境死代码消除
生产构建模式下打包器默认会开启tree-shaking和压缩阶段的不可达代码删除,只需要做好两个配置避免失效:
- 在
package.json中正确配置sideEffects字段,不要把SSR专属的工具模块、依赖包标记为存在副作用,否则tree-shaking不会删除未使用的SSR代码引用 - 确保压缩工具(ESBuild/Terser/SWC)的死代码消除配置处于开启状态(生产模式默认开启):当构建CSR包时,
__BUILD_TARGET__ === 'server'这个判断会被静态求值为false,整个if块内的SSR代码会被判定为不可达代码,压缩阶段会被完全删除,不会进入最终产物。
3. 复杂场景的优化方案
如果单文件内SSR/CSR混写逻辑太多,到处写常量判断维护成本高,可以选两种方案简化:
- 配置路径别名:CSR构建时把所有SSR专属的依赖路径(比如服务端工具方法、数据库驱动)指向一个空的占位模块,从依赖引入层面就阻断SSR代码进入CSR构建链路
- 自定义编译插件:写一个简单的Babel/SWC插件,识别代码中标记了
/* ssr-only */注释的函数、代码块,CSR构建时直接将对应代码替换为空,不需要手动写分支判断,示例写法:
/* ssr-only */ export async function getServerSideProps(ctx) { // 这段逻辑仅在SSR构建时保留,CSR构建时会被插件直接删除 return { props: { data: await serverFetch(ctx.req) } }; }
4. 效果校验
构建完成后通过两个方式确认剔除效果:
- 全局搜索CSR产物文件,确认没有SSR专属关键字(比如服务端依赖包名、SSR专属方法名)
- 用bundle分析工具查看CSR包的依赖列表,确认没有引入任何SSR专属的第三方依赖
内容的提问来源于stack exchange,提问作者sidverma
相关产品推荐
相关产品推荐

