Vite+React生产环境仅桌面端报TypeError: E is not a function,useMediaQuery求助
react-responsive useMediaQuery 生产环境桌面端 TypeError 问题解决
问题背景
项目构建并启动生产服务器后,桌面端出现TypeError: E is not a function错误,移除useMediaQuery Hook后错误不再出现,但业务仍需使用该Hook。使用示例代码如下:
import React from 'react' import { Swiper, SwiperSlide } from 'swiper/react' import { useMediaQuery } from 'react-responsive' export const TrustBlock = (): JSX.Element => { const isMobile = useMediaQuery({ query: '(max-width: 650px)' }) const isTablet = useMediaQuery({ query: '(max-width: 1200px)' }) return ( <div className='container'> <Swiper spaceBetween={20} slidesPerView={isMobile ? 1 : isTablet ? 3 : 4} navigation loop> </Swiper> </div> ) }
可能的原因
- 生产打包压缩导致变量混淆:react-responsive内部的核心函数在生产压缩过程中被重命名(比如错误中的
E),导致调用时无法识别。桌面端的媒体查询逻辑依赖的函数更容易触发这类问题。 - 环境初始化时机问题:桌面端某些浏览器环境下,
window.matchMediaAPI在组件初始化时未完全就绪,useMediaQuery内部调用出错。 - 版本兼容性问题:react-responsive版本与React、打包工具(Webpack/Vite)版本不兼容,生产编译时出现隐性错误。
解决方案
1. 配置打包工具避免压缩关键变量
针对Webpack(使用Terser插件)或Vite,在压缩配置中保留报错的变量名(如E),或者直接保留react-responsive相关的核心标识符:
- Webpack配置示例:
module.exports = { optimization: { minimizer: [ new require('terser-webpack-plugin')({ terserOptions: { mangle: { reserved: ['E'] // 根据错误提示的变量名添加,也可尝试添加'react-responsive'相关核心函数名 } } }) ] } }
- Vite配置示例:
import { defineConfig } from 'vite' export default defineConfig({ build: { minify: 'terser', terserOptions: { mangle: { reserved: ['E'] } } } })
2. 调整useMediaQuery的调用方式
改用字符串参数或快捷属性形式调用,避免复杂的对象配置,可能规避内部逻辑的压缩问题:
// 直接传入媒体查询字符串 const isMobile = useMediaQuery('(max-width: 650px)') const isTablet = useMediaQuery('(max-width: 1200px)') // 或者使用快捷属性 const isMobile = useMediaQuery({ maxWidth: 650 }) const isTablet = useMediaQuery({ maxWidth: 1200 })
3. 版本适配
升级react-responsive到最新稳定版,或回退到社区反馈无此问题的版本(比如v9.0.2),同时确保React版本与插件兼容。
4. 添加客户端环境检查
确保在浏览器环境下才调用useMediaQuery,避免服务端渲染或初始化时的异常:
export const TrustBlock = (): JSX.Element => { const isClient = typeof window !== 'undefined' const isMobile = isClient ? useMediaQuery({ query: '(max-width: 650px)' }) : false const isTablet = isClient ? useMediaQuery({ query: '(max-width: 1200px)' }) : false return ( <div className='container'> <Swiper spaceBetween={20} slidesPerView={isMobile ? 1 : isTablet ? 3 : 4} navigation loop> </Swiper> </div> ) }
5. 自定义媒体查询Hook
如果以上方法都无效,可以基于原生API实现自己的useMediaQuery,完全规避第三方库的问题:
import { useState, useEffect } from 'react' const useMediaQuery = (query: string): boolean => { const [matches, setMatches] = useState(false) useEffect(() => { if (typeof window === 'undefined') return const media = window.matchMedia(query) setMatches(media.matches) const handleChange = () => setMatches(media.matches) media.addEventListener('change', handleChange) return () => media.removeEventListener('change', handleChange) }, [query]) return matches } // 使用方式和原Hook一致 export default useMediaQuery
内容的提问来源于stack exchange,提问作者Алексей Хоменков
相关产品推荐
相关产品推荐

