Nuxt: how to explicitly name JS chunks? - vue.js

I'm using Nuxt in static site generation mode. One requirement in my project is to deploy only certain routes, each with their respective assets.
But Nuxt gives chunks random names like 925446d.js.
So I created a manual router.js and specified chunk names while importing my components:
component: () => import(/* webpackChunkName: "about" */ '~/pages/about.vue').then(m => m.default || m)
But Nuxt doesn't take my chunk names into account and continues to give chunks random names, making it super difficult to single out which chunk goes with which route.
Any suggestion?

If you want to be able to identify chunks by name, Nuxt actually has a built-in mechanism do to that, using the property filename of the build config.
You can add the following in your nuxt.config.js file:
{
// ...
build: { filenames: { chunk: () => '[name].js' } }
}
Some additional options do exist, see documentation.

Related

Static page within dynamic nested route not generated on NuxtJS

My goal is to have the following page structure
pages
--/users/
--/users/:id/
--/users/:id/edit/
edit should be generated for every :id. Right now my setup works only in development mode. On nuxt generate only the first /users/:id/edit/ is generated. All other routes get a 404 error on production!
Current setup:
- /users
- index.vue
/_id
- index.vue
- edit.vue
I also tried to add another _id.vue which i found in the documentation and here
/users
- _id.vue
- /id
[...]
I checked this as well.
In nuxt.config.js I have
ssr: true,
target: 'static',
Do I need to add something in router property in nuxt.config.js? I am not sure why it is not generated in production.
If you are using static site generation you have to define your static routes, because Nuxt does not know how many users do you have in your data base.
You can set a generate method into your nuxt.config.js file:
generate: {
fallback: true,
async routes() {
const routes = await _getRoutes()
return routes
}
}
And then declare a function called _getRoutes to generate a list of dynamic routes:
async function _getRoutes($content) {
const usersList = [1, 2, 3] // get from your DB
const paths = []
usersList.forEach((userId) => {
paths.push(`/users/${userId}`)
paths.push(`/users/${userId}/edit`)
})
return paths
}
this is a bug with nuxt 2 in vue 2.
for dynamically generated routes, and also not-english nested routes ,
(or maybe just generally non-english routes) when vue hydrates the component , it will face a problem and restarts it . so the data that you cached in data(){} with fetch(), will be empty again .
currently you should use static and/or english-only routes .

How can I ensure this vue application doesn't exceed the recommended 244kb in production?

There is a vue file here that imports a json file that has about 9000 records in it.
How do I ensure that the json file is not compiled with the component?
A simple way would be to put the JSON file you want to access in the public folder (Webpack won't process anything in this folder). Then use AJAX to call the file at run time.
This will avoid expanding your app bundle, but Vue may still show that you're including a large resource. This approach would also allow you to split the data into smaller chunks and load them as needed in your app.
Here's an example, assuming the data file is located at /public/data/mydata.json. Also, I suggest using Axios to make the AJAX stuff easier.
import axios from 'axios'
export default {
name: 'Vue Component',
created() {
this.fetchData();
},
methods: {
fetchData() {
axios.get('/data/mydata.json').then(response => {
// do something with response.data
})
}
}
}
Use dynamic import. Like this:
import(
/* webpackChunkName: "my_json" */
'./src/my.json'
).then(({default: myJson}) => {
// do whatever you like here~
console.log(myJson);
});
doc:
https://webpack.js.org/guides/code-splitting/#dynamic-imports
If the json file is too big, you will still get the size exceeding warning.
But the json file would load async, so it would not cause any performance problem.
🔽 🔽 🔽 if you really don't want to see the warning, try this: 🔽 🔽 🔽
Use copy-webpack-plugin,it can copy your json file to dist folder, which means you can fire a XHR get request to load the json file, like this axios.get('/my.json').
By doing this, you can get the FULL control about when to load the file.

Component dynamic import doesn't work from router.js

With Nuxt.js, in my router.js I'm trying to import my route component thus:
{
path: '/',
component: () => import(/* webpackChunkName: "index" */ '~/pages/index.vue')
}
I get this error:
render function or template not defined in component: anonymous
I came across someone else's Nuxt.js project where they add the following at the end, and with this it works:
{
path: '/',
component: () => import(/* webpackChunkName: "index" */ '~/pages/index.vue').then(m => m.default || m)
}
The returned object of my import() looks like this:
In a another Vue.js, non-Nuxt project, the same kind of import() looks like that:
In both cases, the component is nested in some "default" object. But with Nuxt.js it seems you must import that object explicitly, whereas with regular Vue.js you don't have to specify that.
Why?
Regarding why the module is nested inside a { default } object: it's because Nuxt uses ES6 style modules. ES6 style modules are written the following:
export default {
// some object
};
Default is not the only property you can have. More syntax.
Regarding why vue-router are working without the .default, it's because the code is more versatile than the code used by Nuxt. Nuxt already ships it's own builder and always uses ES6 modules.
On the other hand, vue-router does not know about how it will be built. So to be easier to use, vue-router automatically checks if the module is ES6 or not.
const resolve = once(resolvedDef => {
if (isESModule(resolvedDef)) {
resolvedDef = resolvedDef.default
}
// ...
}
Source: code of vue-router.
As you may already know, it's very uncommon to use a router.js file in a Nuxt project, because Nuxt already has its own routing system which splits your code into chunks for each page. Documentation here.

vue and webpack doesn't do lazy loading in components?

The lazy component in vue/webpack seem to be wrong or I miss confuse about the terms.
To do lazy loading of component I use the keyword import and webpack should split this component to sepeate bundle, and when I need to load this component webpack should take care of it and load the component.
but in fact, webpack does make sperate file, but it loaded anyway when the application is running. which is unexpected.
For example I just create a simple vue application (using the cli) and browse to localhost:8080/ and the about page should be loaded (by default) using the import keyword.
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
So This is by design? I load every time the file I do not need right now (the page about). and if I have 200 pages, when I'll fetch 200 files I dont need. how that ends up? that did I save here by using the import key?
In vuetify for example they uses import key, but the files are loaded anyway and not by demand.
You can also avoid component prefetch using one of the webpack "magic" comments (https://webpack.js.org/guides/code-splitting/#prefetchingpreloading-modules), eg.
components: {
MyComponent: () => import(/* webpackPrefetch: false */ './MyComponent.vue')
}
Feel free to read more about this Vue optimization below:
https://vueschool.io/articles/vuejs-tutorials/lazy-loading-individual-vue-components-and-prefetching/
If you're referring to the default app template from Vue CLI, then you're actually observing the prefetch of the About page, intended to reduce load times for pages the user will likely visit.
If you want to avoid this performance optimization, use this Vue config:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugins.delete('prefetch')
config.plugins.delete('preload')
}
}
For troubleshooting reference, Chrome's Network panel includes an Initiator column, which provides a clickable link to the source file that triggered the network call. In this case of the about.js, the source file looks like this:
try using vue-lazyload maybe it can help and for <script> tags you can try async defer it helps in website speed optimizations

Vue Lazy load refactor

I am trying to lazy load a lot of components in my app, I want to show a loading spinner while any of them is loading, and the same for error; so there is a lot of duplication.
export default c => ({
component: import(`${c}`),
loading: loadingComponent,
timeout: 3000
})
I am trying to refactor this into a single function and using it like that
import lazyload from './lazyload';
Collection: lazyload("./Collection.vue")
But webpack is not extracting the component as it normally does, I know that I am missing something.
You need to be creating an async component factory (meaning function). Also the import module cannot be fully dynamic, there needs to be some prefix to the module path otherwise it could match literally any module and webpack needs to know which subset of modules it could possibly match at runtime to include them in the build.
Fully dynamic statements, such as import(foo), will fail because webpack requires at least some file location information. This is because foo could potentially be any path to any file in your system or project. The import() must contain at least some information about where the module is located, so bundling can be limited to a specific directory or set of files.
I've made some adjustments to your code (untested):
lazyload.js
export default c => () => ({
component: import(`./components/${c}`),
loading: loadingComponent,
timeout: 3000
})
Example usage
import lazyload from './lazyload'
export default {
components: {
Collection: lazyload('collection.vue')
}
}
A better implementation, in my opinion, would be to avoid creating the dynamic import. I prefer webpack to know for certain which modules are definitely needed at build time instead of bundling together a subset of modules inside a directory.
lazyload.js
export default componentFn => () => ({
component: componentFn(),
loading: loadingComponent,
timeout: 3000
})
Example usage
import lazyload from './lazyload'
export default {
components: {
Collection: lazyload(() => import('./collection.vue'))
}
}
Now lazyload has no dependency on any specific component directory and can be used with any component.