VueJS/browser caching productie builds

Ik heb een VueJS-app. Telkens wanneer ik npm run builduitvoer, wordt er een nieuwe set dist/*-bestanden gemaakt, echter wanneer ik ze op de server laad (na het verwijderen van de oude build) en open de pagina in de browser, het laadt de oude build (van cache neem ik aan). Als ik de pagina ververs, laadt het de nieuwe code, geen probleem.

Dit is mijn index.html:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
        <meta http-equiv="cache-control" content="max-age=0" />
        <meta http-equiv="cache-control" content="no-cache" />
        <meta http-equiv="expires" content="-1" />
        <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
        <meta http-equiv="pragma" content="no-cache" />
        <link rel="stylesheet" href="/static/css/bootstrap.min.css"/>
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>

Is er een manier om het te dwingen elke keer nieuwe code te laden of (idealiter) om te controleren of de oude bestanden van de server zijn verdwenen, en dan de browser te vernieuwen?


Antwoord 1, autoriteit 100%

We worstelden met hetzelfde probleem en ontdekten dat de browsers van sommige mensen niet eens de nieuwste versie zouden ophalen, tenzij ze handmatig werden vernieuwd. We hadden problemen met caching op verschillende lagen, waaronder het CDN waar we bestanden hebben gehost.

We hadden ook moeite met het onderhouden van versies en het snel opnieuw kunnen implementeren van een eerdere versie als er iets misgaat.

Onze oplossing (met project gebaseerd op vue-cli Webpack):

1) We bouwen de distributie om een ​​versiespecifieke map te hebben in plaats van ‘statisch’. Dit helpt ons ook om builds te volgen en indien nodig een implementatie ongedaan te maken. Om de ‘statische’ map te wijzigen, wijzigt u ‘assetsSubDirectory’ onder ‘build’ in index.js en wijzigt u ‘assetsPublicPath’ in uw CDN-pad.

2) We gebruiken Webpack Assets Manifestom een ​​manifest.json-bestand te maken dat verwijst naar alle activa. Ons manifest bevat een hash van alle bestanden, omdat het een zeer goed beveiligde applicatie is.

3) We uploaden de versiemap (met de js en css) naar ons CDN.

4) (Optioneel) We hosten een dynamisch index.html-bestand op de backend-server. De links naar de stylesheet en scripts worden ingevuld door de backend-server met behulp van een sjabloonsysteem dat is opgehaald uit de gegevens op de manifest.json (zie #5). Dit is optioneel omdat je de optie force-reload kunt gebruiken zoals in de onderstaande opmerking, wat geen geweldige ervaring is, maar wel werkt.

5) Om een ​​nieuwe versie te publiceren, plaatsen we manifest.json op de backend-server. We doen dit via een GraphQL-eindpunt, maar je zou het json-bestand handmatig ergens kunnen plaatsen. We slaan dit op in de database en gebruiken het om de index.html te vullen en gebruiken het ook om bestanden te verifiëren met behulp van de bestandshash (om te valideren dat ons CDN niet is gehackt).

Resultaat: onmiddellijke updates en een gemakkelijke mogelijkheid om uw versies te volgen en te wijzigen. We ontdekten dat het de nieuwe versie onmiddellijk in bijna alle browsers van de gebruiker zal ophalen.

Nog een bonus: we bouwen een applicatie die een hoge mate van beveiliging vereist en door de index.html op onze (reeds beveiligde) backend te hosten, konden we onze beveiligingsaudits gemakkelijker doorstaan.


Bewerken 17-2-19

We ontdekten dat bedrijfsnetwerken proxy-caching uitvoerden, ondanks headers zonder cache. IE 11 lijkt ook cache-headers te negeren. Daardoor kregen sommige gebruikers niet de meest up-to-date versies.

We hebben een version.json die tijdens het bouwen wordt verhoogd/gedefinieerd. Versienummer is opgenomen in manifest.json. De buildbundel wordt automatisch geüpload naar S3. Vervolgens geven we het manifest.json door aan de backend (dit doen we op een invoerpagina in het beheerdersgedeelte). Vervolgens stellen we de “actieve” versie in op die gebruikersinterface. Dit stelt ons in staat om versies gemakkelijk te wijzigen/terug te draaien.

De backend plaatst de “currentVersion” als een Response Header op alle verzoeken. Als currentVersion !== version (zoals gedefinieerd in version.json), dan vragen we de gebruiker om te klikken om zijn browser te vernieuwen (in plaats van het hem op te dringen).


Antwoord 2, autoriteit 55%

Op basis van dit uitgebreide antwoord op cache-headerskun je dit het beste aan de serverkant oplossen als je hebt er controle over, aangezien alles in de <meta>-tags wordt overschreven door headers die door de server zijn ingesteld.

De opmerkingen bij de vraag geven aan dat je deze app met nginx bedient. Met behulp van het bovenstaande gekoppelde antwoord kon ik de headers Cache-Control, Expiresen Pragmainstellen voor verzoeken om bestanden die eindigen op .htmlop deze manier in mijn nginx-configuratie:

server {
  ...other config
  location ~* \.html?$ {
    expires -1;
    add_header Pragma "no-cache";
    add_header Cache-Control "no-store, must-revalidate";
  }
}

Dit dwingt de browser om de nieuwste index.htmlop te vragen bij elke herlaadbeurt van de pagina, maar gebruikt nog steeds de gecachte middelen (js/css/fonts/images) tenzij er nieuwe verwijzingen zijn in de laatste html-reactie.


Antwoord 3, autoriteit 32%

Dit probleem is ongetwijfeld vervelend. Moest het oplossen met behulp van een aangepast eindpunt van de front-endversie dat in de koptekst is verzonden. Ik gebruik een rails-backend en Vue + Axios als front-end. Opmerking Ik heb geen servicemedewerker nodig en gebruik er daarom ook geen.

In wezen ben ik gewoon aan het herladen wanneer er een get-verzoek is en dat de versie van de applicatie is gewijzigd (de server kan hetzelfde informeren)

axiosConfig.js

axios.interceptors.response.use(
  (resp) => {
    const fe_version = resp.headers['fe-version'] || 'default'
    if(fe_version !== localStorage.getItem('fe-version') && resp.config.method == 'get'){
      localStorage.setItem('fe-version', fe_version)
      window.location.reload() // For new version, simply reload on any get
    }
    return Promise.resolve(resp)
  },
)

Rails-backend

application_controller.rb

after_action :set_version_header
def set_version_header
  response.set_header('fe-version', Setting.key_values['fe-version'] || 'default')
end

application.rb(CORS-configuratie ervan uitgaande dat Vue op poort 8080 draait)

config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins ['localhost:8080', '127.0.0.1:8080']
    resource '*', expose: ['fe-version'], headers: :any, methods: [:get, :post, :delete, :patch], credentials: true
  end
end if Rails.env.development?

Hier een gedetailleerd artikel geschreven: https://blog.francium.tech/vue-js-cache-not-getting-cleared-in-production-on-deploy-656fcc5a85fe


Antwoord 4, autoriteit 14%

Om de cache te verwijderen, kunt u rm -rf node_modules/.cache

uitvoeren

Hiermee wordt je cache verwijderd. U kunt een nieuwe build uitvoeren voordat u deze implementeert.

Ik had hetzelfde probleem waarbij ik een productie-build uitvoerde, maar zelfs als ik lokaal draaide, zou mijn code verwijzen naar de productie-build in plaats van naar mijn laatste wijzigingen.

Ik denk dat dit een gerelateerd probleem is: https://github.com/vuejs /vue-cli/issues/2450

Other episodes