Разрушающие кэш файлы page-data.json в Гэтсби - PullRequest
0 голосов
/ 03 октября 2019

У меня есть сайт, сгенерированный Gatsby, на котором я заменил содержимое домашней страницы.

К сожалению, предыдущая версия обслуживала /page-data/index/page-data.json с неправильными заголовками управления кэшем, в результате чего /page-data/index/page-data.json кэшировалось в клиентских браузерах (и устаревшие данные показывались, если не было принудительного обновления). Я также обнаружил, что файлы page-data.json не хэшируются (см. https://github.com/gatsbyjs/gatsby/issues/15080).

. Я обновил заголовки контроля кэша, чтобы версии отныне не кэшировались, но это не помогаетклиенты с кешированной версией сейчас.

Что можно сделать, чтобы заставить клиентов запрашивать последнюю версию этого файла?

Ответы [ 3 ]

1 голос
/ 05 октября 2019

Ознакомьтесь с этим руководством, это решение, которое я использовал.

https://examsworld.co.in/programming/javascript/how-to-cache-bust-a-react-app/

Это в основном компонент-обертка, который проверяет, соответствует ли кэшированная версия браузера версии сборкиномер версии в package.json. Если этого не произойдет, он очищает кэш и перезагружает страницу.

Вот как я его использую.

gatsby-browser.js

    export const wrapRootElement = ({ element }) => (
     <CacheBuster>
        {({ loading, isLatestVersion, refreshCacheAndReload }) => {
          if (loading) return null
          if (!loading && !isLatestVersion) {
            // You can decide how and when you want to force reload
            refreshCacheAndReload()
          }
          return <AppProvider>{element}</AppProvider>
        }}
      </CacheBuster>
    )

CacheBuster.js

import React from 'react'
import packageJson from '../../package.json'

global.appVersion = packageJson.version

// version from response - first param, local version second param
const semverGreaterThan = (versionA, versionB) => {
  const versionsA = versionA.split(/\./g)

  const versionsB = versionB.split(/\./g)
  while (versionsA.length || versionsB.length) {
    const a = Number(versionsA.shift())

    const b = Number(versionsB.shift())
    // eslint-disable-next-line no-continue
    if (a === b) continue
    // eslint-disable-next-line no-restricted-globals
    return a > b || isNaN(b)
  }
  return false
}

class CacheBuster extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      loading: true,
      isLatestVersion: false,
      refreshCacheAndReload: () => {
        console.info('Clearing cache and hard reloading...')
        if (caches) {
          // Service worker cache should be cleared with caches.delete()
          caches.keys().then(function(names) {
            for (const name of names) caches.delete(name)
          })
        }

        // delete browser cache and hard reload
        window.location.reload(true)
      },
    }
  }

  componentDidMount() {
    fetch('/meta.json')
      .then(response => response.json())
      .then(meta => {
        const latestVersion = meta.version
        const currentVersion = global.appVersion

        const shouldForceRefresh = semverGreaterThan(
          latestVersion,
          currentVersion
        )
        if (shouldForceRefresh) {
          console.info(
            `We have a new version - ${latestVersion}. Should force refresh`
          )
          this.setState({ loading: false, isLatestVersion: false })
        } else {
          console.info(
            `You already have the latest version - ${latestVersion}. No cache refresh needed.`
          )
          this.setState({ loading: false, isLatestVersion: true })
        }
      })
  }

  render() {
    const { loading, isLatestVersion, refreshCacheAndReload } = this.state
    const { children } = this.props
    return children({ loading, isLatestVersion, refreshCacheAndReload })
  }
}

export default CacheBuster

generate-build-version.js

const fs = require('fs')
const packageJson = require('./package.json')

const appVersion = packageJson.version

const jsonData = {
  version: appVersion,
}

const jsonContent = JSON.stringify(jsonData)

fs.writeFile('./static/meta.json', jsonContent, 'utf8', function(err) {
  if (err) {
    console.log('An error occured while writing JSON Object to meta.json')
    return console.log(err)
  }

  console.log('meta.json file has been saved with latest version number')
})

и в вашем package.json добавить эти сценарии

 "generate-build-version": "node generate-build-version",
 "prebuild": "npm run generate-build-version"

1 голос
/ 03 октября 2019

За исключением того, что вы обращаетесь к каждому клиентскому браузеру индивидуально и очищаете его кеш, нет никаких других способов аннулировать все кеши вашего клиента. Если ваша веб-страница находится за CDN, которым вы можете управлять, возможно, вы сможете принудительно сделать аннулирование на уровне CDN, поэтому новые клиенты всегда будут перенаправлены на обновленную веб-страницу, даже если в CDN уже была кеширована ранее существовавшая устаревшая копия.

0 голосов
/ 04 октября 2019

Я попал туда в конце концов ... Это у меня gatsby-node.js

const hash = md5(`${new Date().getTime()}`)

const addPageDataVersion = async file => {
  const stats = await util.promisify(fs.stat)(file)
  if (stats.isFile()) {
    console.log(`Adding version to page-data.json in ${file}..`)
    let content = await util.promisify(fs.readFile)(file, 'utf8')
    const result = content.replace(
      /page-data.json(\?v=[a-f0-9]{32})?/g,
      `page-data.json?v=${hash}`
    )
    await util.promisify(fs.writeFile)(file, result, 'utf8')
  }
}

exports.onPostBootstrap = async () => {
  const loader = path.join(__dirname, 'node_modules/gatsby/cache-dir/loader.js')
  await addPageDataVersion(loader)
}

exports.onPostBuild = async () => {
  const publicPath = path.join(__dirname, 'public')
  const htmlAndJSFiles = glob.sync(`${publicPath}/**/*.{html,js}`)
  for (let file of htmlAndJSFiles) {
    await addPageDataVersion(file)
  }
}
...