webpack 生产模式 "build" - 强制浏览器不读取缓存 file/rebuild 新文件
webpack production mode "build" - force browser not to read cached file/rebuild fresh files
我们有一个应用程序(一个网站),其中包含一些 React 组件,css 和使用 webpack 编译的 js。
我们的工作流程是在本地开发时在/src/
文件夹中npm run start
,在/dist/
中生成CSS和JS文件,然后运行npm run build
清除刷新 /dist/
文件夹中的所有文件,然后再部署到实时。无论如何,这就是意图。
问题是,当我们将更改部署到实时环境时,浏览器似乎仍然缓存了以前版本的 CSS/JS 文件,或者无法从新版本中正确读取。这只发生在 hashed/chunked(React 组件)文件(参见下面文件结构中的 **),而不是 main.js 或 main.scss 文件。
我们认为 webpack 会在每次构建时生成新的 'chunks'/files。有没有一种方法可以强制 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"
}
}
为了破坏构建中的缓存,您需要更改静态资产 (js / css) 的 url。
最好的方法是根据文件的内容生成随机字符串(称为散列),这种方法的好处是如果最终文件在部署之间没有改变,它将生成相同的散列=> 客户端将使用缓存的文件。如果确实发生了变化 => 哈希值发生了变化 => 文件名发生了变化 => 客户端将获取一个新文件。
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或创建一个,它支持自定义模板。查看文档。
我们有一个应用程序(一个网站),其中包含一些 React 组件,css 和使用 webpack 编译的 js。
我们的工作流程是在本地开发时在/src/
文件夹中npm run start
,在/dist/
中生成CSS和JS文件,然后运行npm run build
清除刷新 /dist/
文件夹中的所有文件,然后再部署到实时。无论如何,这就是意图。
问题是,当我们将更改部署到实时环境时,浏览器似乎仍然缓存了以前版本的 CSS/JS 文件,或者无法从新版本中正确读取。这只发生在 hashed/chunked(React 组件)文件(参见下面文件结构中的 **),而不是 main.js 或 main.scss 文件。
我们认为 webpack 会在每次构建时生成新的 'chunks'/files。有没有一种方法可以强制 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"
}
}
为了破坏构建中的缓存,您需要更改静态资产 (js / css) 的 url。
最好的方法是根据文件的内容生成随机字符串(称为散列),这种方法的好处是如果最终文件在部署之间没有改变,它将生成相同的散列=> 客户端将使用缓存的文件。如果确实发生了变化 => 哈希值发生了变化 => 文件名发生了变化 => 客户端将获取一个新文件。
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或创建一个,它支持自定义模板。查看文档。