You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js登录跳转首页报Abort fetching component错误修复

问题根因分析

开发环境和生产环境的两个报错属于同一问题链触发,核心诱因共3个:

  • 表单默认提交行为未被完全拦截:代码仅给type="submit"的登录按钮绑定了onClick事件执行preventDefault(),但未给<form>标签本身绑定onSubmit处理逻辑。当用户按回车触发提交、或者点击按钮的事件冒泡顺序异常时,会触发表单原生GET提交,直接中断Next.js的客户端路由拉取流程,开发环境就会抛出Error: Abort fetching component for route: "/"。
  • 响应解析逻辑无容错:sendLoginData方法中未校验接口返回的HTTP状态、响应格式,直接调用response.json()。如果接口异常返回HTML格式的错误页(比如服务端500/404页、反向代理错误页),JSON解析时遇到HTML开头的<字符就会抛出语法错误;生产环境下路由跳转时未完成的请求被中断、静态资源加载失败返回HTML兜底页,也会触发Uncaught SyntaxError: Unexpected token '<'。
  • 事件监听与DOM嵌套存在逻辑错误:
    1. 路由监听里url.includes("/")的判断恒成立,所有路由跳转都会触发loading状态,和预期仅在跳首页时触发的逻辑不符
    2. 注册入口的<Link>标签内部嵌套了带href=""的a标签,空href会覆盖Link的路由配置,点击时会触发当前页刷新,中断路由流程

修复方案

按以下顺序修改代码即可解决问题:

  1. 把表单提交逻辑绑定到form标签的onSubmit事件上,移除提交按钮上的onClick绑定,从根源拦截原生表单提交
  2. 给fetch逻辑增加响应状态、格式校验,只有请求成功且返回格式为JSON时再做解析,确认登录成功后再执行路由跳转
  3. 修正路由监听的判断逻辑,修复Link标签的无效嵌套问题
  4. 增加loading状态的竞态处理,避免重复提交、组件卸载后更新状态

修正后的核心代码示例
const SigninForm = (props) => {
  const Router = useRouter();
  const usernameRef = useRef();
  const passwordRef = useRef();
  const [isLoading, setIsLoading] = useState(false);
  const [captchaInput, setCaptchaInput] = useState(false);
  const [dataBaseResponse, setDataBaseResponse] = useState({});
  const [fetchedData, setFetchedData] = useState(false);
  const [captchaCode, setCaptchaCode] = useState();

  const sendLoginData = async () => {
    // 增加错误捕获和响应校验
    try {
      const response = await fetch("/api/formDataSignIN", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          id: usernameRef.current.value,
          password: passwordRef.current.value,
          captchaCode: captchaCode,
        }),
      });
      // 先判断HTTP状态是否正常
      if (!response.ok) throw new Error('Request failed');
      // 判断响应类型是不是JSON,避免解析HTML报错
      const contentType = response.headers.get('content-type');
      if (!contentType || !contentType.includes('application/json')) {
        throw new Error('Invalid response format');
      }
      const data = await response.json();
      setDataBaseResponse({
        message: data.message,
        data,
      });
      // 登录成功后显式跳转,不要把跳转逻辑放到外部hook里隐式执行
      if (data.state === true) {
        Router.push("/");
      }
      return data;
    } catch (err) {
      setDataBaseResponse({
        message: err.message,
        data: null
      })
      return null;
    }
  };

  // 把提交逻辑绑定到form的onSubmit上,不要绑定按钮onClick
  const handleLoginSendData = async (e) => {
    e.preventDefault();
    if (isLoading) return;
    setIsLoading(true);
    await sendLoginData();
    setFetchedData(true);
    setIsLoading(false);
  };

  useNotification(dataBaseResponse, fetchedData, setFetchedData);

  useEffect(() => {
    const handleStart = (url) => {
      // 修正判断逻辑:只有跳根路径且当前不在首页时才触发loading
      if (url === "/" && window.location.pathname !== "/") {
        setIsLoading(true);
      }
    };
    const handleStop = () => {
      setIsLoading(false);
    };
    Router.events.on("routeChangeStart", handleStart);
    Router.events.on("routeChangeComplete", handleStop);
    Router.events.on("routeChangeError", handleStop);

    return () => {
      Router.events.off("routeChangeStart", handleStart);
      Router.events.off("routeChangeComplete", handleStop);
      Router.events.off("routeChangeError", handleStop);
    };
  }, [Router]);

  const handleCaptchaValue = (e) => {
    if (e) {
      setCaptchaInput(true);
      setCaptchaCode(e);
    }
  };
  return (
    <Fragment>
      <Head>
        <title>Sign in</title>
        <meta name="description" content="Generated by create next app" />
      </Head>
      <section className={style.section}>
        <div className={style.container}>
          <div className={cls(style.user, style.signinBx)}>
            <div className={style.imgBx}>
              <Image src="/meta2.jpeg" alt="meta" layout="fill" />
            </div>
            <div className={style.formBx}>
              {/* 在这里绑定onSubmit,拦截原生表单提交 */}
              <form onSubmit={handleLoginSendData}>
                <div
                  style={{
                    marginBottom: "80px",
                  }}
                >
                  <Link href="/" shallow={true}>
                    <a>
                      <FontAwesomeIcon icon={faArrowLeft} />
                    </a>
                  </Link>
                </div>
                <h2>Sign In</h2>
                <input
                  type="text"
                  name="email"
                  ref={usernameRef}
                  placeholder="email or phoneNumber"
                />

                <input
                  type="password"
                  name="Password"
                  ref={passwordRef}
                  placeholder="Password"
                />
                <Googlerecaptcha handleCaptchaValue={handleCaptchaValue} />
                <div className={style.btnBx}>
                  <LoginButton />
                </div>
                <input
                  type="submit"
                  value={isLoading ? "Loading..." : "Login"}
                  // 移除按钮上的onClick,提交逻辑统一交给form处理
                  disabled={isLoading || !captchaInput}
                />
                <p className={style.signup}>
                  Dont have an account ?
                  {/* 移除a标签上的空href,避免覆盖Link的路由逻辑 */}
                  <Link href="/signUp">
                    <a>Sign Up</a>
                  </Link>
                </p>
              </form>
            </div>
          </div>
        </div>
      </section>
    </Fragment>
  );
};

export default SigninForm;

额外注意事项
  • 不要在隐式执行的hook(比如当前使用的useNotification)里做路由跳转,必须拿到登录接口的成功响应后再显式调用Router.push,避免接口还没返回就提前跳转中断请求
  • 提交按钮增加disabled状态,避免用户重复点击触发多次提交
  • 如果生产环境部署在反向代理后面(比如Nginx),需要检查静态资源的路径配置,确保_next/static目录下的资源可以被正常访问,否则资源加载404返回HTML也会触发<开头的语法错误

内容的提问来源于stack exchange,提问作者Arman Amini

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 19:24:24