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

Next.js集成next-pwa后首页链接无法正常导航的技术问题

问题:Next.js + next-pwa 首页链接点击后URL变化但页面不渲染

原本Web应用所有链接正常,接入next-pwa迁移为PWA后,仅首页(manifest.json中start_url对应的/路由)的链接失效:点击链接URL变化,但页面不重新渲染;直接输入URL可正常跳转,离开首页后所有链接恢复正常。

相关代码

自定义Link组件(Next.js 12.3.1)

import styled from '@emotion/styled';
import NextLink from 'next/link';


export const Link = ({ href, children, justifyContent = 'center', width = '', ...rest }) => {
  const A = styled.a({
    color: 'inherit',
    textDecoration: 'inherit',
    display: 'flex',
    alignItems: 'center',
    margin: '0',
    padding: '0',
    width,
    height: '100%',
    justifyContent
  });

  return (
    <NextLink href={href} {...rest} passHref>
      <A {...rest}>
        {children}
      </A>
    </NextLink>
  );
};

next.config.js

const runtimeCaching = require('next-pwa/cache');

/** @type {import('next').NextConfig} */
const withPWA = require('next-pwa')({
  dest: 'public',
  register: true,
  skipWaiting: true,
  runtimeCaching,
  navigationPreload: true
});

const nextConfig = withPWA({
  reactStrictMode: true,
  swcMinify: true,
  trailingSlash: true,
  images: {
    domains: [
      'firebasestorage.googleapis.com',
      'i.gyazo.com',
      'gateway.pinata.cloud'
    ]
  },
  async headers() {
    return [
      {
        source: '/:path*',
        headers: securityHeaders,
      }
    ]
  },
  experimental: {
    modularizeImports: {
      '@mui/material': {
        transform: '@mui/material/{{member}}'
      },
      '@mui/icons-material': {
        transform: '@mui/icons-material/{{member}}'
      },
      lodash: {
        transform: 'lodash/{{member}}'
      },
      'react-xarrows': {
        transform: 'react-xarrows/lib/{{member}}'
      }
    }
  },
});

// Use to analyze bundle size
// const withBundleAnalyzer = require('@next/bundle-analyzer')({
//   enabled: process.env.ANALYZE === 'true',
// });

module.exports = nextConfig;

manifest.json

{
  "theme_color": "#ffffff",
  "background_color": "#1565c0",
  "display": "fullscreen",
  "start_url": "/",
  "scope": ".",
  "name": "Student Property Review",
  "short_name": "Student Property Review",
  "description": "A platform to read and review reviews about student properties and landlords; review your properties to help other students.",
  "icons": [
    {
      "src": "icons/icon-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icons/icon-256x256.png",
      "sizes": "256x256",
      "type": "image/png"
    },
    {
      "src": "icons/icon-384x384.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "icons/icon-512x512.png",
      "sizes": "512x512",
      "type": "image/png",
      "purpose": "any maskable"
    }
  ]
}

解决方案

1. 修复自定义Link组件的props传递冲突

你同时将...rest传给NextLink和a标签,可能导致props冲突(比如把a标签的事件props传给NextLink)。拆分props,只传递对应组件需要的内容:

import styled from '@emotion/styled';
import NextLink from 'next/link';

export const Link = ({ href, children, justifyContent = 'center', width = '', ...rest }) => {
  // 分离NextLink和a标签的props,避免冲突
  const { onClick, ...linkProps } = rest;
  const A = styled.a({
    color: 'inherit',
    textDecoration: 'inherit',
    display: 'flex',
    alignItems: 'center',
    margin: '0',
    padding: '0',
    width,
    height: '100%',
    justifyContent
  });

  return (
    <NextLink href={href} passHref {...linkProps}>
      <A {...rest}>
        {children}
      </A>
    </NextLink>
  );
};

2. 调整PWA的runtimeCaching策略

next-pwa默认缓存规则可能拦截首页的客户端导航,自定义缓存规则优先使用网络请求:

const runtimeCaching = [
  {
    urlPattern: /^https?.*/,
    handler: 'NetworkFirst',
    options: {
      cacheName: 'offline-cache',
      networkTimeoutSeconds: 10,
      expiration: {
        maxEntries: 200,
        maxAgeSeconds: 7 * 24 * 60 * 60, // 缓存7天
      },
      cacheableResponse: {
        statuses: [0, 200],
      },
    },
  },
  // 针对静态资源添加单独缓存规则
  {
    urlPattern: /^https:\/\/firebasestorage\.googleapis\.com\/.*/,
    handler: 'CacheFirst',
    options: {
      cacheName: 'firebase-storage',
      expiration: {
        maxEntries: 50,
      },
    },
  },
];

3. 修改manifest的scope配置

将scope改为"/",确保首页导航不受PWA范围限制:

{
  // ...其他配置
  "scope": "/",
  // ...其他配置
}

4. 清除PWA缓存并重新注册Service Worker

开发环境:打开浏览器DevTools的Application标签,清除Service Worker和缓存存储;
生产环境:确保skipWaiting: true生效,或手动触发用户更新Service Worker。

内容的提问来源于stack exchange,提问作者J. Whitehead

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 23:40:40