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

Nuxt 2.15.3+Tailwind+PWA模块下iOS Safari安装PWA后字体首次加载失败

iOS 主屏幕PWA自定义字体首次加载异常问题

问题现象

我开发了一款Nuxt PWA应用,访问地址为www.shirime.one,整体运行状态正常,但在移动端Safari环境下存在自定义字体加载异常的问题。
将应用通过iOS版Safari添加到主屏幕作为PWA安装后,使用MacBook连接设备调试时,在资源目录中找不到字体文件夹;如果通过Safari检查器对PWA页面执行一次刷新操作,字体即可正常加载。
该现象表明,iOS Safari环境下安装的独立运行PWA,在首次打开时Nuxt PWA模块无法正常加载字体类资源,目前暂不明确具体成因。

现有项目配置

Nuxt PWA 配置

pwa: {
    icon: {
      fileName: 'app-icon.png',
    },

    meta: {
      name: 'Shirime Challenge',
      mobileApp: true,
      mobileAppIOS: true,
      description: false,
      lang: false,
      theme_color: '#000000',
      ogHost: 'https://shirime.one',
      twitterCard: 'summary',
      twitterSite: '@jbty_dev',
      twitterCreator: '@jbty_dev',
    },
  },

@font-face 配置

@font-face {
  font-family: 'Nouveau';
  src: url('~/assets/fonts/NouveauRegular.eot');
  font-weight: 100;
  font-style: normal;
}
@font-face {
  font-family: 'Nouveau';
  src: local('Nouveau Regular'), local('NouveauRegular'),
  url('~/assets/fonts/NouveauRegular.woff2') format('woff2'),
  url('~/assets/fonts/NouveauRegular.woff') format('woff'),
  url('~/assets/fonts/NouveauRegular.ttf') format('truetype'),
  url('~/assets/fonts/NouveauRegular.svg#NouveauRegular') format('svg');
  font-weight: 100;
  font-style: normal;
  font-display: swap;
}

Tailwind 配置

import plugin from 'tailwindcss/plugin'

module.exports = {
  purge: [
    './components/**/*.{vue,js}',
    './layouts/**/*.vue',
    './pages/**/*.vue',
    './plugins/**/*.{js,ts}',
    './nuxt.config.{js,ts}',
  ],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {
      fontFamily: {
        heading: ['Nouveau', 'sans-serif'],
        body: ['Futura CondensedLight', 'sans-serif'],
        incantation: ['Stranger back in the Night', 'sans-serif'],
      },
      colors: {
        red: '#FF0000',
        redAccent: '#ff1414',
        red40: 'rgba(255,0,0,0.4)',
        black: '#000000',
        black92: 'rgba(0,0,0,0.92)',
        white: '#ffffff',
        white40: 'rgba(255,255,255,0.4)',
      },
      fontSize: {
        xs: '.82rem',
        sm: '.95rem',
        tiny: '.875rem',
        base: '1rem',
        lg: '1.125rem',
        xl: '1.25rem',
        '2xl': '1.5rem',
        '3xl': '2.2rem',
        '4xl': '2.9rem',
        '5xl': '3.3rem',
        '6xl': '3.8rem',
        '7xl': '6rem',
        '8xl': '11rem',
      },
    },
  },
  variants: {
    extend: {},
  },
  plugins: [
    plugin(function ({ addUtilities }) {
      const newUtilities = {
        '.no-text-shadow': {
          textShadow: 'none',
        },
        '.red-text-shadow': {
          textShadow: '0px 0px 10px rgba(255,0,0,0.2)',
        },
        '.red-text-shadow-big': {
          textShadow: '0px 0px 15px rgba(255,0,0,0.6)',
        },
        '.white-text-shadow': {
          textShadow: '0px 0px 10px rgba(255,255,255,0.4)',
        },
        '.red-box-b-shadow': {
          boxShadow: '0 18px 10px -10px  rgba(255,0,0,0.2)',
        },
        '.white-box-b-shadow': {
          boxShadow: '0 18px 10px -10px  rgba(255,255,255,0.2)',
        },

        '.backdrop-blur': {
          backdropFilter: 'blur(2px)',
        },
      }

      addUtilities(newUtilities)
    }),
  ],
}

package.json 配置

{
  "name": "shirime",
  "author": {
    "name": "Thery Jean-Baptiste",
    "email": "jb.thery@flameapp.fr",
    "url": "https://flameapp.fr"
  },
  "description": "Shirime Challenge - www.shirime.one digital ouija, make your friends believe that you speak with an all-knowing mind! Progressive Web App available for installation on Ios, Android, Windows, Mac Os and Linux.",
  "version": "1.1.0",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt generate",
    "start": "nuxt start",
    "lintfix": "eslint --fix --ext .js,.vue --ignore-path .gitignore .",
    "lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore",
    "lint": "npm run lint:js && npm run lint:style",
    "test": "jest"
  },
  "lint-staged": {
    "*.{js,vue}": "eslint",
    "*.{css,vue}": "stylelint"
  },
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
      "pre-commit": "lint-staged"
    }
  },
  "dependencies": {
    "@nuxtjs/amp": "^0.5.4",
    "@nuxtjs/google-adsense": "^1.3.0",
    "@nuxtjs/pwa": "^3.3.5",
    "@nuxtjs/sitemap": "^2.4.0",
    "cookie-universal-nuxt": "^2.1.4",
    "core-js": "^3.9.1",
    "nuxt": "^2.15.3",
    "nuxt-i18n": "^6.21.1",
    "nuxt-lazy-load": "^1.2.5",
    "vue-social-sharing": "^3.0.5"
  },
  "devDependencies": {
    "@commitlint/cli": "^12.0.1",
    "@commitlint/config-conventional": "^12.0.1",
    "@nuxt/types": "~2.15.3",
    "@nuxtjs/eslint-config": "^6.0.0",
    "@nuxtjs/eslint-module": "^3.0.2",
    "@nuxtjs/stylelint-module": "^4.0.0",
    "@nuxtjs/tailwindcss": "^4.0.1",
    "@tailwindcss/postcss7-compat": "^2.0.3",
    "autoprefixer": "^10.2.5",
    "babel-core": "7.0.0-bridge.0",
    "babel-eslint": "^10.1.0",
    "babel-jest": "^26.6.3",
    "eslint": "^7.22.0",
    "eslint-config-prettier": "^7.2.0",
    "eslint-plugin-nuxt": "^2.0.0",
    "eslint-plugin-prettier": "^3.3.1",
    "fibers": "^5.0.0",
    "husky": "4.3.8",
    "lint-staged": "^10.5.4",
    "postcss": "^8.2.8",
    "prettier": "^2.2.1",
    "stylelint": "^13.12.0",
    "stylelint-config-prettier": "^8.0.2",
    "stylelint-config-standard": "^21.0.0",
    "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.0.3"
  }
}

问题成因与解决方法

核心根源是iOS独立运行的PWA环境下,Service Worker 首次启动时未将字体资源纳入预缓存列表,首次打开时无法从离线缓存读取字体,手动刷新后字体请求被SW缓存,因此后续加载正常。

  • 基础修复方案:调整字体存放路径
    • 将所有字体文件从~/assets/fonts/目录移动到Nuxt项目的~/static/fonts/目录
    • 同步修改@font-face中的资源路径,将原有的~/assets/fonts/前缀替换为/fonts/,例如把url('~/assets/fonts/NouveauRegular.woff2')修改为url('/fonts/NouveauRegular.woff2')
    • 优势:static目录下的资源不会被Webpack打包添加内容哈希,会被Nuxt PWA模块默认识别为静态资源,自动纳入Service Worker的预缓存清单,PWA首次启动时就会完成字体资源的缓存加载。
  • 进阶修复方案:显式配置Service Worker预缓存规则
    • 如果希望保留assets目录下的字体资源,可在Nuxt的pwa配置中补充workbox规则,显式指定需要预加载的字体资源:
      pwa: {
        // 保留原有pwa配置...
        workbox: {
          preCaching: [
            '/fonts/*.woff2',
            '/fonts/*.woff',
            '/fonts/*.ttf'
          ],
          runtimeCaching: [
            {
              urlPattern: /^https:\/\/www\.shirime\.one\/fonts\/.*\.(?:woff2|woff|ttf|eot|svg)$/,
              handler: 'CacheFirst',
              options: {
                cacheName: 'font-cache',
                expiration: {
                  maxEntries: 10,
                  maxAgeSeconds: 60 * 60 * 24 * 365
                }
              }
            }
          ]
        }
      }
      
    • 作用:避免打包过程中资源路径变更导致的缓存漏匹配,强制Service Worker在PWA启动时加载指定的字体资源。
  • 额外适配优化
    • 现有@font-face配置中单独拆分的eot格式字体规则缺少font-display: swap属性,可合并到同一字体的规则中,避免重复资源请求。
    • 确保所有字体资源和应用为同域资源,跨域资源会被iOS Safari的PWA环境拦截,导致Service Worker无法正常缓存。

内容的提问来源于stack exchange,提问作者Jean-Baptiste THERY

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.30 08:06:03