NextJS+Antd在Sunmi D2设备WebView中语法错误及样式失效问题
问题描述
在React Native WebView中运行NextJS+Antd项目时,在Sunmi D2设备(Android 11系统,WebView内核为Chrome 83.0.4103.120)上出现两个问题:
- 控制台报错
Uncaught SyntaxError: Unexpected token '('(来自_app-455-3437e82bbe2dfd49.js:1) - Antd组件无样式,仅显示原始元素
已尝试在package.json中配置"browserslist": ["chrome 83"],但问题仍未解决。
环境信息
- WebView User Agent:
Mozilla/5.0 (Linux; Android 11; D2_2nd_STGL Build/942; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/83.0.4103.120 Safari/537.36 - 运行设备:Sunmi D2
相关配置文件
package.json
{ "name": "mona-fnb", "version": "0.1.0", "private": true, "browserslist": [ "chrome 83" ], "scripts": { "dev": "next dev -p 3030", "build": "next build", "start": "next start -H 192.168.3.121", "prd-start": "bun run build && bun start", "lint": "next lint", "export": "next build", "host": "next dev -p 3030 -H 192.168.3.121", "export:live": "env-cmd -f .env.live yarn build && yarn export", "export:mona": "env-cmd -f .env.mona yarn build && yarn export" }, "dependencies": { "@ant-design/cssinjs": "^1.21.1", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^9.0.0", "@microsoft/signalr": "^8.0.7", "@nivo/bar": "^0.88.0", "@nivo/boxplot": "^0.88.0", "@nivo/core": "^0.88.0", "@nivo/line": "^0.88.0", "@nivo/pie": "^0.88.0", "@number-flow/react": "^0.4.2", "@react-google-maps/api": "^2.20.3", "@studio-freight/lenis": "^1.0.42", "@studio-freight/react-lenis": "^0.0.47", "@tanstack/react-query": "^5.59.8", "@tinymce/tinymce-react": "^6.0.0", "@types/tinymce": "^4.6.9", "antd": "^5.21.3", "axios": "^1.7.7", "clsx": "^2.1.1", "date-fns": "^4.1.0", "dayjs": "^1.11.13", "devtools-detector": "2.0.22", "gsap": "^3.12.5", "html2pdf.js": "^0.10.2", "html5-qrcode": "2.2.7", "js-cookie": "^3.0.5", "jszpl": "^1.1.8", "jwt-decode": "^4.0.0", "leaflet": "^1.9.4", "leaflet-geosearch": "^4.0.0", "lodash": "^4.17.21", "next": "14.2.13", "nprogress": "^0.2.0", "react": "^18", "react-confetti": "^6.1.0", "react-date-range": "^2.0.1", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18", "react-grid-layout": "^1.5.0", "react-icons": "^5.4.0", "react-intersection-observer": "^9.13.1", "react-leaflet": "^4.2.1", "react-lottie-player": "^2.1.0", "react-player": "^2.16.0", "react-thermal-printer": "^0.19.3", "react-to-print": "^3.0.5", "react-zoom-pan-pinch": "^3.4.4", "sass": "^1.79.4", "sharp": "^0.33.5", "uuid": "^10.0.0", "zustand": "4.5.4" }, "devDependencies": { "@tanstack/eslint-plugin-query": "^5.59.7", "@tanstack/react-query-devtools": "^5.59.8", "@types/js-cookie": "^3.0.6", "@types/leaflet": "^1.9.14", "@types/lodash": "^4.17.13", "@types/node": "^20", "@types/react": "^18", "@types/react-date-range": "^1.4.9", "@types/react-dom": "^18", "@types/uuid": "^10.0.0", "@types/w3c-web-usb": "^1.0.10", "@types/web-bluetooth": "^0.0.21", "autoprefixer": "^10.4.20", "env-cmd": "^10.1.0", "eslint": "^8", "eslint-config-next": "14.2.13", "postcss": "^8", "tailwind-merge": "^2.5.4", "tailwindcss": "^3.4.1", "typescript": "^5" } }
_document.tsx
import { createCache, extractStyle, StyleProvider } from "@ant-design/cssinjs"; import type { DocumentContext } from "next/document"; import Document, { Head, Html, Main, NextScript } from "next/document"; const MyDocument = () => ( <Html lang="en"> <Head /> <body> <Main /> <NextScript /> </body> <div id="root-printer"></div> </Html> ); MyDocument.getInitialProps = async (ctx: DocumentContext) => { const cache = createCache(); const originalRenderPage = ctx.renderPage; ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => ( <StyleProvider cache={cache}> <App {...props} /> </StyleProvider> ), }); const initialProps = await Document.getInitialProps(ctx); const style = extractStyle(cache, true); return { ...initialProps, styles: ( <> {initialProps.styles} <style dangerouslySetInnerHTML={{ __html: style }} />{" "} </> ), }; }; export default MyDocument;
React Native Webview.tsx
import React, {useEffect, useRef} from 'react'; import { ActivityIndicator, BackHandler, Dimensions, StyleSheet, View, } from 'react-native'; import {WebView} from 'react-native-webview'; interface WebViewComponentProps { uri: string; } const INJECTED_JS = 'window.ReactNativeWebView.postMessage(navigator.userAgent);'; const WebViewComponent: React.FC<WebViewComponentProps> = ({uri}) => { const webViewRef = useRef<any>(null); useEffect(() => { const handleBackPress = () => { if (webViewRef.current) { webViewRef.current?.goBack(); return true; } return false; }; const backHandler = BackHandler.addEventListener( 'hardwareBackPress', handleBackPress, ); return () => { backHandler.remove(); }; }, []); const renderLoadingView = () => ( <View style={styles.loadingContainer}> <ActivityIndicator size="large" color="#0000ff" /> </View> ); const handleError = () => { console.error('WebView failed to load'); }; return ( <WebView source={{uri}} style={styles.container} onError={handleError} androidLayerType="software" javaScriptEnabled={true} domStorageEnabled={true} startInLoadingState={false} scalesPageToFit={true} injectedJavaScript={INJECTED_JS} onMessage={event => console.log('User Agent:', event.nativeEvent.data)} /> ); }; const deviceWidth = Dimensions.get('window').width; const styles = StyleSheet.create({ container: { flex: 1, width: deviceWidth, }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'red', }, }); export default WebViewComponent;
解决方案
一、解决语法错误(Unexpected token '(')
Chrome 83不支持部分ES6+语法,仅靠browserslist无法完全覆盖依赖包的编译降级,需手动配置Next.js的编译规则:
- 创建/修改
next.config.js,添加以下配置:
/** @type {import('next').NextConfig} */ const nextConfig = { transpilePackages: ['antd', '@ant-design/cssinjs'], webpack: (config, { isServer }) => { config.module.rules.push({ test: /\.(js|jsx|ts|tsx)$/, exclude: /node_modules\/(?!(antd|@ant-design)\/)/, use: { loader: 'babel-loader', options: { presets: [ ['next/babel', { 'preset-env': { targets: { chrome: '83' }, useBuiltIns: 'usage', corejs: 3, } }] ] } } }); return config; }, } module.exports = nextConfig;
- 安装必要依赖:
npm install core-js@3 babel-loader @babel/core --save-dev
- 重新构建项目:
npm run build
注意:开发环境next dev不会完全应用降级编译,必须用生产构建测试。
二、解决Antd样式失效问题
Antd v5依赖cssinjs动态生成样式,WebView环境下需确保样式注入逻辑正确:
- 修复
_document.tsx的DOM结构错误:
将div#root-printer移到body标签内部(原代码中放在body外会导致DOM解析异常):
import { createCache, extractStyle, StyleProvider } from "@ant-design/cssinjs"; import type { DocumentContext } from "next/document"; import Document, { Head, Html, Main, NextScript } from "next/document"; const MyDocument = () => ( <Html lang="en"> <Head /> <body> <Main /> <NextScript /> <div id="root-printer"></div> </body> </Html> ); MyDocument.getInitialProps = async (ctx: DocumentContext) => { const cache = createCache(); const originalRenderPage = ctx.renderPage; ctx.renderPage = () => originalRenderPage({ enhanceApp: (App) => (props) => ( <StyleProvider cache={cache}> <App {...props} /> </StyleProvider> ), }); const initialProps = await Document.getInitialProps(ctx); const style = extractStyle(cache, true); return { ...initialProps, styles: ( <> {initialProps.styles} <style dangerouslySetInnerHTML={{ __html: style }} /> </> ), }; }; export default MyDocument;
- 在
_app.tsx中导入Antd全局重置样式:
import 'antd/dist/reset.css'; import type { AppProps } from 'next/app'; export default function App({ Component, pageProps }: AppProps) { return <Component {...pageProps} />; }
- 确认WebView配置:
保留现有WebView的androidLayerType="software"、javaScriptEnabled={true}、domStorageEnabled={true}配置,这些是样式动态生成的必要条件。
额外排查步骤
- 验证User Agent传递:在
_app.tsx中打印navigator.userAgent,确认Next.js能识别到Chrome 83的环境。 - 简化测试:创建仅包含基础Antd组件的页面,排除其他依赖的干扰,确认问题是否由特定包导致。
内容的提问来源于stack exchange,提问作者ANH TỪ
相关产品推荐
相关产品推荐

