Force change vue js/css assets hash (fingerprint) - vue.js

Building vue app in production as in:
NODE_ENV=production vue-cli-service build
Adds the static assets hash fingerprints as below:
dist/js/chunk-vendors.d710a916.js 986.81 KiB 297.69 KiB
dist/js/app.ad3f94f2.js 231.00 KiB 56.02 KiB
dist/3115008e.worker.js 30.59 KiB 9.98 KiB
dist/css/app.7eecdb26.css 174.87 KiB 24.16 KiB
dist/css/chunk-vendors.565b13d4.css 42.77 KiB 6.81 KiB
The assets are served with a high maxage cache. Due to some issues involving the headers of these static assets, I want to force renaming all assets, preferably via changing the length of the hash fingerprint (or the algorithm).
So in vue.config.js I added:
module.exports = {
configureWebpack: {
output: {
hashFunction: 'sha256',
hashDigestLength: 8,
},
...
}
The problem is that it affects only dist/3115008e.worker.js (the hash changes, and length increases to 8). All other assets name remain unchanged.
What changes are required to modify the hash fingerprint length (or algorithm) for the chunk-vendors and app assets?

Update:
I ended up forcing assets hash change by modifying the loader's hashType option in vue.config.js(which seem to default to 'md5'):
module.exports = {
...
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
options.hashType = `sha1`
return options
})
},
I wasn't able to change the hash length. It seems to be hardcoded in #vue/cli-service": "^4.1.2"

Related

How to enable vue lazy loading when the app is served from a CDN?

I am working on a project with an isolated frontend and backend. The backend is a Laravel app serving the APIs and the frontend is a VueJS app consuming those APIs.
The backend app is deployed on a digitalocean droplet, and the frontend app is deployed on netlify. However, the end-users do not hit the Vue app. The Laravel app has an env variable called ASSET_URL where I've assigned the netlify URL. I've then added the script and link tags pointing to the resources (/js, /css) of /dist directory of the Vue app to my main Laravel view file that gets returned from the Laravel app:
// app.blade.php
...
<link rel="stylesheet" href="{{config('app.asset_url') . '/css/app.css'}}">
...
<script src="{{config('app.asset_url') . '/js/app.js'}}"></script>
<script src="{{config('app.asset_url') . '/js/app.chunk.js'}}"></script>
...
In the Vue app, I have overridden the default webpack configuration in the vue.config.js file so that webpack does not insert the [contentHash] in the filenames which makes it easier to inject the static resources into the Laravel app:
// vue.config.js
module.exports = {
devServer: {
host: "0.0.0.0",
disableHostCheck: true,
port: '8080'
},
chainWebpack: config => {
if (config.plugins.has('extract-css')) {
const extractCSSPlugin = config.plugin('extract-css')
extractCSSPlugin && extractCSSPlugin.tap(() => [{
filename: 'css/app.css',
chunkFilename: 'css/app.chunk.css'
}])
}
},
configureWebpack: {
optimization: {
splitChunks: false
},
output: {
filename: 'js/app.js',
chunkFilename: 'js/app.chunk.js'
}
}
}
Long story short, from the end-users perspective, the Vue app does not exist.
But the problem is, now I have lost the ability to lazy load the routes, because if I use the /* webpackChunkname: "product" */ magic comment in my route definition like this:
component: () => import(/* webpackChunkName: "product" */ "#/views/product/ProductList.vue")
Then Webpack will throw an error:
conflict: Multiple chunks emit assets to the same filename (app.chunk.js)
So I've gone ahead and made the following changes to my vue.config.js file:
module.exports = {
devServer: {
host: "0.0.0.0",
disableHostCheck: true,
port: '8080'
},
chainWebpack: config => {
if (config.plugins.has('extract-css')) {
const extractCSSPlugin = config.plugin('extract-css')
extractCSSPlugin && extractCSSPlugin.tap(() => [{
filename: 'css/app.css',
chunkFilename: 'css/[name].css'
}])
}
},
configureWebpack: {
output: {
filename: 'js/app.js',
chunkFilename: 'js/[name].js'
}
}
}
This is what the build files look like:
dist/js/chunk-vendors.js 2083.74 KiB 607.00 KiB
dist/js/app.js 1548.29 KiB 259.22 KiB
dist/js/chunk-b0a634c6.js 287.19 KiB 96.31 KiB
dist/js/product.js 7.51 KiB 2.43 KiB
dist/precache-manifest.78cd95684ece42546415aacd7f68cced.js 2.43 KiB 0.86 KiB
dist/service-worker.js 1.04 KiB 0.61 KiB
dist/css/app.css 419.79 KiB 60.69 KiB
dist/css/chunk-vendors.css
And I am injecting the resources to my laravel app like this:
<head>
<link rel="stylesheet" href="{{config('app.asset_url') . '/css/app.css'}}">
<link rel="stylesheet" href="{{config('app.asset_url') . '/css/chunk-vendors.css'}}">
</head>
…
<script src="{{config('app.asset_url') . '/js/app.js'}}"></script>
<script src="{{config('app.asset_url') . '/js/chunk-vendors.js'}}"></script>
<script src="{{config('app.asset_url') . '/js/chunk-b0a634c6.js'}}"></script>
<script src="{{config('app.asset_url') . '/js/product.js'}}"></script>
To test this set up locally, my laravel app is being served from
test.myapp.test, and the vue app is being served from localhost:8080. So I have assigned http://localhost:8080 to the ASSET_URL.
I have two problems now:
On initial load, all the the resources including product.js are loading even when I am not on that route. That is because I've hard-coded the script/link tags. And it defeats the purpose of lazy loading (load only when I'm on that route)
When I visit the /product route, the application does look for the product.js file lazily, but instead of looking for http://localhost/8080/js/product.js, it is looking for http://test.myapp.test/js/product.js, so I end up getting the following error:
vue-router.esm.js?8c4f:2257 ChunkLoadError: Loading chunk product failed.
(missing: http://test.myapp.test/js/product.js)
How do I lazy load routes or what modifications do I need in my webpack config so that the lazy loaded components are searched in the ASSET_URL instead of the current host?
[Even if you have a better idea to lazy load routes when using a CDN, it would be extremely helpful if you please share it]
I recommend you to take a look on the below package
https://github.com/hilongjw/vue-lazyload
OR
https://www.npmjs.com/package/vue-clazy-load
wherein for images all you need to do is pass the local/cloud path and the rest is handled by the package itself

How to disable splitting of chunks into different file with vuejs in webpack?

I have laravel-vue stack for my project. While building with mix it splits codes into two main files
vendor.js
app.js
Apart from that there are around 60 files which gets created as chunks like
0.js
1.js
2.js
...
61.js
How to tell webpack to bind these chunks into single file. Does the webpack breaks into chunks for each new component in cue?
You can modify how many chunks you want in your webpack configuration file (example is from a vue.config.js file) like this:
module.exports = {
configureWebpack: {
optimization: {
splitChunks: {
minSize: 10000,
maxSize: 250000,
}
}
},
};
I don't know your filesize, but if you increase maxSize as needed, you can have a single file.

CopyWebpackPlugin isn't copying static files to output directory

My files show as being emitted but they are not actually being copied to dist folder.
Excerpt of compilation log:
Version: webpack 4.15.1 Time: 1824ms Built at: 01/29/2020 3:51:51 PM
Asset Size Chunks Chunk Names
main.js 116 KiB 0 [emitted] main
CSS/main.css 76 bytes [emitted] Images/Uplink_Logo_Horiz.jpg 651 KiB [emitted] [big]
CSS/adminlte.css 708 KiB [emitted] [big]
index.html 6.7 KiB [emitted]
And my webpack config plugin looks like this:
plugins: [
new CopyWebpackPlugin([
{
from: './Images/**',
to: path.join(__dirname, 'dist'),
logLevel: 'trace'
},
{
from: './CSS/**',
to: path.join(__dirname, 'dist'),
logLevel: 'trace'
}
]),
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
I tried changing the order of CopyWebpackPlugin & HtmlWebpackPlugin to no luck. Did you do any other thing to solve this other than just changing the order.
Versions:
"vue": "^2.6.11", "webpack": "^4.15.0", "webpack-cli": "^3.0.8",
"copy-webpack-plugin": "^5.1.1", "html-webpack-plugin": "^3.2.0",
Updated Image:
Full project structure:
Found out the issue. The static files were not included in the VS project. Once I included them it started working. Thanks, for the help :)

HotModuleReplacement not connecting

I am currently trying to use webpack for the first time in a project and have set up a ASP.NET Core project with the following in the Startup.cs file
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
I have Styles folder which contains some .less files too and I have a webpack.config.js in the root of the project, containing:
const path = require("path");
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = (env) => {
return [{
mode: 'development', //TODO Make configurable
entry: { 'main': './app.js' },
output: {
path: path.resolve(__dirname, "wwwroot"),
filename: "js/[name].js",
publicPath: "/"
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
],
module: {
rules: [
{
test: /\.(less)$/,
include: [
path.resolve(__dirname, "Styles")
],
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader'
]
}
]
}
}];
}
When I run my application, I can see in the output that it is compiling the webpack bundles and I see my files in the js and css folder of wwwroot, which suggests it is finding the webpack config file, I also see lines like below which suggests it is attempting something with the webpack-hot-middleware plugin:
[./node_modules/html-entities/index.js] 231 bytes {main} [built]
[./node_modules/querystring-es3/decode.js] 2.45 KiB {main} [built]
[./node_modules/querystring-es3/encode.js] 2.48 KiB {main} [built]
[./node_modules/querystring-es3/index.js] 127 bytes {main} [built]
[./node_modules/webpack-hot-middleware/client-overlay.js] (webpack)-hot-middleware/client-overlay.js 2.17 KiB {main} [built]
[./node_modules/webpack-hot-middleware/client.js?path=__webpack_hmr&dynamicPublicPath=true] (webpack)-hot-middleware/client.js?path=__webpack_hmr&dynamicPublicPath=true 7.68 KiB {main} [built]
but then I don't see any other reference to HMR, and I don't get the [HMR] connected line in the console to signify it has linked up.
Is there anything I am missing in this setup?
I found the problem.....I was being stupid and had forgotten to actually include the is file that is output by webpack, because all I was using for at that point was to compile my less files into CSS!

how to prevent mini-css-extract-plugin from creating a js entrypint

I am relatively new to express + webpack, so i am unclear wether this is intended or not, and if not, how to properly configure it. the question is around the additional asset & entry point created when using the mini-css-extract-plugin.
webpack config:
Extract = require('mini-css-extract-plugin');
path = require('path');
Write = require('write-file-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
demo_scripts: path.resolve('server', 'scripts', 'demo.js'),
demo_styles: path.resolve('server', 'styles', 'demo.css')
},
output: {
path: path.resolve('.tmp'),
filename: '[name].js'
},
plugins: [new Write(), new Extract()],
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
presets: ['babel-preset-env']
}
}
]
},
{
test: /\.css/,
use: [
{
loader: Extract.loader
},
{
loader: 'css-loader'
}
]
}
]
}
};
webpack output
Asset Size Chunks Chunk Names
demo_scripts.js 3.91 KiB demo_scripts [emitted] demo_scripts
demo_styles.css 36 bytes demo_styles [emitted] demo_styles
demo_styles.js 3.89 KiB demo_styles [emitted] demo_styles
Entrypoint demo_scripts = demo_scripts.js
Entrypoint demo_styles = demo_styles.css demo_styles.js
my question is, why is demo_styles.js being created? although the css is being extracted, it almost seems like webpack is still creating a bundled js with css, but when i view that file, the only line in it is
eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./server/styles/demo.css?");
can anyone help explain what is going on here?
UPDATE
if i remove the demo_styles entry point, and configure it via the plugin init, no css asset is built.
({
plugins: [
new Write(),
new Extract({
filename: 'demo_styles.css'
})
]
});
Asset Size Chunks Chunk Names
demo_scripts.js 3.91 KiB demo_scripts [emitted] demo_scripts
Entrypoint demo_scripts = demo_scripts.js
the repo for this is here (note the express branch) https://github.com/brewster1134/bumper/tree/express
There are two workarounds for your problem. For both of them, you need to change the entry point of the Webpack configuration file. I, personally, prefer the first option.
Option 1:
Change the entry to the following:
entry: {
demo: [
path.resolve('server', 'scripts', 'demo.js'),
path.resolve('server', 'styles', 'demo.css'),
]
}
This will generate the following outputs (based on the filename you provided for Extract class and output section:
demo.js
demo_styles.css
Option 2:
For this option, you need to remove the CSS file from the entry point and import it inside the JS file:
webpack.config.js
...
entry: path.resolve('server', 'scripts', 'demo.js'),
...
demo.js
import './../styles.demo.css'
//rest of your JS codes
This solution will generate the same output as Option1
Webpack pulls everything into a js file, then MiniCssExtractPlugin takes it out of that file, leaving a blank js file with // extracted by mini-css-extract-plugin.
My solution is to group your css and js in the entry section of webpack.config.js
entry: {
demo: {
import: [ path.join("server", "scripts", "demo.js"), path.join("server", "styles", "demo.css") ],
filename: "demo.js", // outputs demo.js, demo.css to your output directory
},
main: {
import: [ path.join("server", "scripts", "main.js"), path.join("server", "styles", "main.css") ],
filename: "main.js", // outputs main.js, main.css to your output directory
},
}
Also, so naming works well, use this for your plugins section:
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css"
}),
],
Adjust the bundles "demo" and "main", as well as paths accordingly.
Please remove demo_styles from your entry point this is creating demo_styles.js.
instead you can inject css file like this:
plugins: [
new MiniCssExtractPlugin({
filename: 'demo_styles.css',
}),
Let me know if the issue still persists, Happy to help