сборка производственного режима веб-пакета - заставить браузер не читать кешированный файл / перестраивать свежие файлы

У нас есть приложение (веб-сайт) с некоторыми компонентами React, css и js, скомпилированными с помощью webpack.

Наш рабочий процесс заключается в npm run start в папке /src/ при локальной разработке, которая генерирует файлы CSS и JS в /dist/, а затем запускает npm run build для очистки и обновления всех файлов в папке /dist/ перед развертыванием в реальном времени. Таково намерение, во всяком случае.

Проблема в том, что когда мы внедряем изменение в живую среду, кажется, что в браузере все еще кэшированы предыдущие версии файлов CSS/JS или они неправильно считываются из новых версий. Это происходит только с хешированными/разбитыми (компонент React) файлами (см. ** в файловой структуре ниже), а не с файлами main.js или main.scss.

Мы думали, что webpack создает новые «фрагменты»/файлы с каждой сборкой. Есть ли способ заставить веб-пакет сделать это, чтобы файлы читались как новые, когда они изменяются, или имена файлов разные? Я хочу, чтобы браузер кэшировал файлы, но я также хочу, чтобы учитывались новые изменения.

Пример структуры файла

--/src/
----/scss/
------main.scss
----/js/
------main.js (imports js components)
------/components/
--------banner.js
--------ReactComponent.jsx (imports ReactComponent.scss)
--------ReactComponent.scss
--/dist/
----/css/
------main.css
------2.css (react component css) (**)
------6.css (react component css) (**)
----/js/
------main.js
------0_39cd0323ec029f4edc2f.js (react component js) (**)
------1_c03b31c54dc165cb590e.js (react component js) (**)

** это файлы, которые кэшируются или не читаются должным образом при внесении изменений.

webpack.config.js

const webpack = require("webpack");
const path = require("path");
const autoprefixer = require("autoprefixer");
const TerserJSPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

module.exports = {
  entry: {
    main: ["./js/main.js", "./scss/main.scss"],
  },
  output: {
    filename: "js/[name].js",
    chunkFilename: "js/[name]_[chunkhash].js",
    path: path.resolve(__dirname, "../dist/"),
    publicPath: "/app/themes/[package]/dist/",
    jsonpFunction: "o3iv79tz90732goag"
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /(node_modules)/,
        use: {
          loader: "babel-loader"
        }
      },
      {
        test: /\.(sass|scss|css)$/,
        exclude: "/node_modules/",
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: "css-loader",
            options: {
              importLoaders: 2,
              sourceMap: true
            }
          },
          {
            loader: "postcss-loader",
            options: {
              plugins: () => [require("precss"), require("autoprefixer")],
              sourceMap: true
            }
          },
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
              includePaths: [path.resolve(__dirname, "../src/scss")]
            }
          }
        ]
      },
      {
        test: /\.(png|svg|jpg|gif)$/,
        use: ["file-loader"]
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        use: ["file-loader"]
      }
    ]
  },
  optimization: {
    minimizer: [
      new TerserJSPlugin({
        cache: true,
        parallel: true,
        sourceMap: true
      }),
      new OptimizeCSSAssetsPlugin({
        cssProcessorOptions: {
          safe: true,
          zindex: false,
          discardComments: {
            removeAll: true
          }
        },
        canPrint: true
      })
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      chunkFilename: "css/[id].css"
    })
  ]
};

package.json

    {
        "name": "packagename",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "test": "echo \"Error: no test specified\" && exit 1",
            "build": "(rm -rf ./../dist/*) & webpack --mode production",
            "start": "webpack --mode development --watch "
        },
        "keywords": [],
        "author": "Sarah",
        "license": "ISC",
        "browserslist": [
            "last 4 versions"
        ],
        "devDependencies": {
            "@babel/core": "^7.9.0",
            "@babel/plugin-proposal-object-rest-spread": "^7.9.5",
            "@babel/plugin-syntax-dynamic-import": "^7.8.3",
            "@babel/plugin-transform-arrow-functions": "^7.2.0",
            "@babel/plugin-transform-classes": "^7.9.5",
            "@babel/plugin-transform-flow-strip-types": "^7.9.0",
            "@babel/plugin-transform-react-jsx": "^7.9.4",
            "@babel/preset-env": "^7.9.5",
            "@babel/preset-flow": "^7.9.0",
            "@babel/preset-react": "^7.0.0",
            "autoprefixer": "^7.1.1",
            "babel-loader": "^8.1.0",
            "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1",
            "babel-preset-env": "^1.7.0",
            "browser-sync": "^2.26.7",
            "browser-sync-webpack-plugin": "^2.2",
            "copy-webpack-plugin": "^4.0.1",
            "css-loader": "^3.5.3",
            "cssnano": "^4.1.10",
            "mini-css-extract-plugin": "^0.8.2",
            "node-sass": "^4.14.0",
            "optimize-css-assets-webpack-plugin": "^5.0.3",
            "postcss-loader": "^2.0.5",
            "precss": "^4.0.0",
            "resolve-url-loader": "^2.0.2",
            "sass-loader": "^6.0.5",
            "terser-webpack-plugin": "^2.3.6",
            "webpack": "^4.43.0",
            "webpack-cli": "^3.3.11"
        },
        "dependencies": {
            "axios": "^0.19.2",
            "body-scroll-lock": "^2.7.1",
            "can-autoplay": "^3.0.0",
            "debounce": "^1.0.2",
            "file-loader": "^5.1.0",
            "lazysizes": "^4.1.8",
            "moment": "^2.24.0",
            "objectFitPolyfill": "^2.3.0",
            "promise-polyfill": "^8.1.3",
            "react": "^16.9.0",
            "react-content-loader": "^5.0.4",
            "react-device-detect": "^1.12.1",
            "react-dom": "^16.9.0",
            "react-html-parser": "^2.0.2",
            "react-intersection-observer": "^8.26.2",
            "react-moment": "^0.9.7",
            "react-pdf": "^4.1.0",
            "scrollmonitor": "^1.2.4",
            "socket.io": "^2.3.0"
        }
    }

person Sarah    schedule 24.06.2020    source источник


Ответы (1)


Чтобы разбить кеш на сборке, вам нужно изменить URL-адрес статического ресурса (js/css).

Лучший способ сделать это — сгенерировать случайную строку на основе содержимого файла (называемого хэшем). Преимущество этого подхода в том, что если окончательный файл не изменился между развертываниями, он будет генерировать тот же хэш => клиенты будут использовать кэшированный файл. Если он изменился => хеш изменился => имя файла изменилось => клиенты получат новый файл.

Для этого в Webpack есть встроенный метод.

// webpack.config.js

module.exports = {
  entry: {
    main: ["./js/main.js", "./scss/main.scss"],
  },
  output: {
    filename: process.env.NODE_ENV === 'production'? "js/[name]-[hash].js": "js/[name].js", // this will attach the hash of the asset to the filename when building for production
    chunkFilename: "js/[name]_[chunkhash].js",
    path: path.resolve(__dirname, "../dist/"),
    publicPath: "/app/themes/[package]/dist/",
    jsonpFunction: "o3iv79tz90732goag"
  },
  ...
}

Изменить: чтобы обновить HTML-файл с новым именем файла (которое теперь будет содержать хэш), вы можете использовать HTMLWebpackPlugin, созданный специально для этой цели.

Он поддерживает настраиваемый шаблон, если вам нужно предоставить свой собственный HTML или создать его. Просмотрите документы.

person felixmosh    schedule 25.06.2020
comment
Я предполагаю, что тогда мой вопрос будет заключаться в том, как управлять обновлением места, где файл включен на HTML-страницу? У нас есть тег <script src="../main.js?v=1.0.1" />. Нам просто нужно будет менять его на <script src="../main-[hash].js" /> каждый раз, когда мы вносим изменения? Я думал, что суффикс тега ?v=1.0.1 даст тот же эффект (мы обновляем номер версии, когда вносим изменения), но, похоже, он не работает. Хотя, возможно, ручная ошибка. - person Sarah; 25.06.2020
comment
Если вы не измените значение ?v=1.0.1 при каждой сборке, вы не измените url => используется кеш. Я добавил раздел об изменениях html - person felixmosh; 25.06.2020