React项目npm run build编译失败:Unexpected end of JSON input
执行
npm run build时出现「Unexpected end of JSON input」编译错误 本地执行npm start可正常运行,网站能正常访问,但执行npm run build时出现以下错误:
> my-app@0.1.0 build > react-scripts build Creating an optimized production build... Failed to compile. Unexpected end of JSON input
已尝试以下修复方案,均无效:
- 清理build缓存
- 清理npm缓存
- 删除并重新安装node_modules、package-lock.json
- 创建新React应用并迁移代码
package.json已通过jsonlint校验,项目文件如下:
项目配置文件
package.json
{ "homepage": "http://[redacted].github.io/", "name": "my-website", "version": "0.1.0", "private": true, "dependencies": { "framer-motion": "^10.16.4", "react": "^18.2.0", "react-dom": "^18.2.0", "react-icons": "^4.11.0", "react-scripts": "5.0.1" }, "scripts": { "start": "react-scripts start", "predeploy": "npm run build", "deploy": "gh-pages -d build", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "autoprefixer": "^10.4.14", "gh-pages": "^6.1.1", "postcss": "^8.4.23", "tailwindcss": "^3.3.5" } }
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { path: path.resolve(__dirname, 'build'), filename: 'bundle.js', }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: ['babel-loader'], }, { test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'], }, ], }, resolve: { extensions: ['*', '.js', '.jsx'], }, };
tailwind.config.js
/** @type {import('tailwindcss').Config} */ module.exports = { content: ["./src/**/*.{js,jsx,ts,tsx}"], theme: { extend: {}, }, plugins: [], }
核心代码文件
App.js
import React from 'react'; import HomePage from './components/Homepage'; function App() { return ( <div className="App"> <HomePage /> </div> ); } export default App;
Homepage.js
import React, { useEffect, useRef, useState } from 'react'; import { motion, useScroll, useSpring } from 'framer-motion'; import HeroSection from './HeroSection'; import AboutSection from './AboutSection'; import ProjectsSection from './ProjectsSection'; import SkillsSection from './SkillsSection'; import './styles.css'; const Home = () => { const [arrowSize, setArrowSize] = useState(window.innerHeight / 6); const heroSectionRef = useRef(null); const [showName, setShowName] = useState(true); const [nameSize, setNameSize] = useState('text-4xl sm:text-6xl'); const { scrollY } = useScroll(); const scaleX = useSpring(scrollY, { stiffness: 100, damping: 30, restDelta: 0.001 }); useEffect(() => { return scrollY.onChange((latest) => { if (latest > 0) { setShowName(false); } else { setShowName(true); } }); }, [scrollY]); useEffect(() => { const handleResize = () => { const screenWidth = window.innerWidth; if (screenWidth < 640) { setNameSize('text-2xl'); } else { setNameSize('text-4xl sm:text-6xl'); } }; handleResize(); // Initial calculation window.addEventListener('resize', handleResize); // Recalculate on window resize return () => { window.removeEventListener('resize', handleResize); }; }, []); useEffect(() => { const arrowObserver = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { const newSize = window.innerHeight / 6; setArrowSize(newSize); } }, { threshold: [0, 0.5, 1] } ); if (heroSectionRef.current) { arrowObserver.observe(heroSectionRef.current); } return () => { if (heroSectionRef.current) { arrowObserver.unobserve(heroSectionRef.current); } }; }, []); return ( <div className="snap-y snap-mandatory h-screen overflow-y-scroll"> {showName && ( <motion.div className="fixed top-2 right-2 p-5 text-orange-500 z-50" initial={{ opacity: 0, scale: 0.5 }} animate={{ opacity: 1, scale: 0.7 }} transition={{ duration: 0.8, delay: 0.5, ease: [0, 0.71, 0.2, 1.01] }} > <h1 className={nameSize}>[MY NAME]</h1> </motion.div> )} {/* <HeroSection arrowSize={arrowSize} /> <AboutSection arrowSize={arrowSize} /> <ProjectsSection arrowSize={arrowSize} /> <SkillsSection arrowSize={arrowSize} /> <motion.div className="progress" style={{ scaleX }} /> */} </div> ); }; export default Home;
styles.css
/* Custom scrollbar styles */ ::-webkit-scrollbar { width: 12px; } ::-webkit-scrollbar-track { background: #a5f3fc; /* Cyan-200 color */ } ::-webkit-scrollbar-thumb { background-color: #ff7f00; /* Orange color */ border-radius: 10px; border: 3px solid #a5f3fc; /* Cyan-200 color to match the track */ } ::-webkit-scrollbar-thumb:hover { background-color: #ff6600; /* Darker orange color on hover */ } .line-drawing { stroke-dasharray: 1000; stroke-dashoffset: 1000; animation: draw 2s forwards; } @keyframes draw { to { stroke-dashoffset: 0; } }
Homepage.css
/* Homepage specific styles */ .home-container { scroll-snap-type: y mandatory; overflow-y: scroll; height: 100vh; } section { scroll-snap-align: center; height: 100vh; display: flex; justify-content: center; align-items: center; text-align: center; padding: 20px; transition: opacity 0.5s ease-in-out; position: relative; width: 100%; } /* Animation styles */ @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } .animate-fade-in { animation: fade-in 1s ease-in-out forwards; } .animate-fade-in-delay { animation: fade-in 2s ease-in-out forwards; animation-delay: 2s; } /* Line drawing animation */ .line-drawing { stroke-dasharray: 1000; stroke-dashoffset: 1000; animation: draw 2s forwards; } @keyframes draw { to { stroke-dashoffset: 0; } }
HeroSection.js
import React, { useRef, useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import Arrow from './Arrow'; import { FaLinkedin, FaGithub, FaEnvelope } from 'react-icons/fa'; const HeroSection = ({ arrowSize }) => { const heroSectionRef = useRef(null); const [arrowSizeDynamic, setArrowSizeDynamic] = useState(arrowSize); const [buttonSize, setButtonSize] = useState('text-base sm:text-lg'); const [textSize, setTextSize] = useState('text-xl sm:text-2xl'); const [titleSize, setTitleSize] = useState('text-4xl sm:text-6xl'); useEffect(() => { const handleResize = () => { if (heroSectionRef.current) { const heroSectionHeight = heroSectionRef.current.clientHeight; const contentHeight = heroSectionRef.current.scrollHeight; const remainingSpace = heroSectionHeight - contentHeight; const newSize = Math.max(arrowSize, remainingSpace / 2); // Adjust the divisor to control the size setArrowSizeDynamic(newSize); // Adjust button and text sizes based on screen width const screenWidth = window.innerWidth; if (screenWidth < 640) { setButtonSize('text-sm px-4 py-2'); setTextSize('text-lg'); setTitleSize('text-4xl'); } else { setButtonSize('text-base sm:text-lg px-6 py-3 sm:px-10 sm:py-5'); setTextSize('text-xl sm:text-3xl'); setTitleSize('text-5xl sm:text-9xl'); } } }; handleResize(); // Initial calculation window.addEventListener('resize', handleResize); // Recalculate on window resize return () => { window.removeEventListener('resize', handleResize); }; }, [arrowSize]); return ( <section ref={heroSectionRef} className="section hero snap-start h-screen flex flex-col justify-between items-center text-center relative bg-cyan-200" > <div className="container mx-auto px-4 sm:px-10 relative z-10 mt-10"> <motion.h1 className={`${titleSize} mb-6 hero-title`} initial={{ opacity: 0, y: -50 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1 }} > Hi! 👋 </motion.h1> <motion.p className={`${textSize} mb-4`} initial={{ opacity: 0, y: -50 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1, delay: 0.5 }} > I'm [Redacted] </motion.p> <motion.p className="text-lg sm:text-2xl mb-16 sm:mb-32" initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 1, delay: 1.5 }} > Passionate about things. </motion.p> <motion.div className="flex justify-center space-x-4 sm:space-x-8 mb-8" initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 1, delay: 2 }} > <a href="https://www.linkedin.com/in/myname" target="_blank" rel="noopener noreferrer"> <FaLinkedin className="text-3xl sm:text-5xl text-blue-600 hover:text-blue-800" /> </a> <a href="https://github.com/myusername" target="_blank" rel="noopener noreferrer"> <FaGithub className="text-3xl sm:text-5xl text-gray-800 hover:text-gray-600" /> </a> <a href="mailto:email@gmail.com" target="_blank" rel="noopener noreferrer"> <FaEnvelope className="text-3xl sm:text-5xl text-orange-500 hover:text-orange-700" /> </a> </motion.div> <motion.a href="/resume.pdf" className={`inline-block ${buttonSize} bg-orange-500 text-white rounded-full hover:bg-orange-600 transition-colors mb-10`} initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} transition={{ duration: 1, delay: 2.5 }} download > Download Resume </motion.a> </div> <Arrow size={arrowSizeDynamic} target="#about" /> </section> ); }; export default HeroSection;
解决方案
1. 移除自定义webpack.config.js
Create React App 默认使用内置Webpack配置,自定义配置会与react-scripts的生产构建逻辑冲突,直接删除该文件即可。
2. 配置PostCSS
如果项目中没有postcss.config.js,创建该文件并添加以下内容:
module.exports = { plugins: { tailwindcss: {}, autoprefixer: {}, }, };
3. 彻底清理依赖与缓存
执行以下命令完成全量清理与重装:
# 删除依赖、锁文件、构建产物 rm -rf node_modules package-lock.json build # 清理npm缓存 npm cache clean --force # 重新安装依赖 npm install
4. 检查所有JSON格式文件
排查项目中所有.json文件(如manifest.json、.env.local等),确保没有语法错误(如缺失逗号、括号不闭合)。
5. 启用CI模式构建
执行以下命令触发严格模式构建,获取更详细的错误日志:
CI=true npm run build
6. 检查public目录文件
确认public目录下的静态文件(尤其是JSON格式文件)完整无截断。
内容的提问来源于stack exchange,提问作者Steelcrawler
相关产品推荐
相关产品推荐

