Reduce Blocking time of Nuxt Applications #9061
-
--Disclaimer-- Ok, let's get started! On the Backend, we are using AdonisJS Proxied by Nginx and cached for 24 hours so the backend response is lightning fast ⚡️ But the problem we are facing right now, is the FE speed. The profiler looks like this x4 slowdown on a MAC (1.4 GHz Quad-Core Intel Core i5): You can download the profiler from here: As you can see, the main thread doesn't take a rest until later, so the user can not interact with the app. The direction where I wanted to go is explained by Google's Web.dev If blocking time is anything above 50ms then instead of having a task that executes a JS for 2 secs, where TBT (Total blocking time) = 2000 - 50 = 1950 ms, you can split that file into 40 files where each executes at 50ms and your total blocking time will be from 1950 ms to 0ms! So what causes this long JS task? Now my idea or question is, can we break down app.js into multiple tasks that execute around 50ms, how can we do it? |
Beta Was this translation helpful? Give feedback.
Replies: 9 comments
-
Plus one here! We have exactly the same problem! |
Beta Was this translation helpful? Give feedback.
-
Facing the same issue with my website. |
Beta Was this translation helpful? Give feedback.
-
@ricardobt, no. I'm hoping things will get better with Webpack5 and Vue3. |
Beta Was this translation helpful? Give feedback.
-
I'm guessing that this first blocking task is vue hydration process. With this plugin: https://github.com/maoberlehner/vue-lazy-hydration you can lower it down significantly (if properly used - read the specs). |
Beta Was this translation helpful? Give feedback.
-
I second @voltane idea to leverage lazy-hydration, reducing JS execution time and Chunk splitting is mainly wepack's task so nothing much to do (hopefully would be improved by WP5) One important tip worth mentioning is that nuxt plugins are usually main reason of blocking render/hydration since nuxt awaits on them before start rendering on client-side and also their dependencies will be added to main (or vendors) chunk which both are necessary to bootstrap too. (NOTE: All examples below are for Example 1: Defer plugins with a background task Bad practice: export default async function(ctx, inject) {
await ...
} Doing task in parallel to render: async function task(ctx) {}
export default function(ctx, inject) {
task(ctx).catch(console.error)
} Using async function task(ctx) {}
export default function(ctx, inject) {
window.onNuxtReady(() => task(ctx).catch(console.error))
} Example 2: Lazy importing dependencies Bad practice: import bigDep from 'big-dep'
export default function (ctx, inject) {} Create a chunk to reduce execution time (and also preload in parallel) but still blocks render: export default async function (ctx, inject) {
const bigDep = await import('big-dep' /* webpackChunkName: 'big-dep' */)
} Do logic in background: async function task(ctx) {
const bigDep = await import('big-dep' /* webpackChunkName: 'big-dep' */)
}
export default function (ctx, inject) {
task(ctx).catch(console.error)
} Example 3: Lazy import by usage: Bad practice: (usage: import getbigDep from 'big-dep'
export default function (ctx, inject) {
inject('util', {
async foo() {
// some logic depending on bigDep
}
})
} Lazy import by usage: (same usage) const getbigDep = () => import('big-dep' /* webpackChunkName: 'big-dep' */)
const createUtils = ctx => ({
async foo() {
const bigDep = await getbigDep()
// some logic depending on bigDep
}
})
export default function (ctx, inject) {
inject('util', createUtils(ctx))
} Lazy import entire utils if utils themselfe are big: (Usage // plugins/foo.utils.js
// we can either directly import or utilize lazy import too
export default (ctx) {
return { ... }
} // plugins/foo.client.js
export default (ctx, inject) {
inject(utils => import('./foo.utils').then(createUtils => createUtils(ctx))
} |
Beta Was this translation helpful? Give feedback.
-
is this in the docs ? I haven't seen this info before, maybe add them to docs ? they were really helpful to me, I wasn't aware of it |
Beta Was this translation helpful? Give feedback.
-
@jd1378 Docs PR covering this would definitely be welcome ❤️ |
Beta Was this translation helpful? Give feedback.
-
Hope this article can help you |
Beta Was this translation helpful? Give feedback.
-
@danielroe, @pi0 I've got TBT very high in my app using NUXT-BRIDGE, do you guys say that is not possible to reduce it due to how webpack works? |
Beta Was this translation helpful? Give feedback.
I second @voltane idea to leverage lazy-hydration, reducing JS execution time and Chunk splitting is mainly wepack's task so nothing much to do (hopefully would be improved by WP5)
One important tip worth mentioning is that nuxt plugins are usually main reason of blocking render/hydration since nuxt awaits on them before start rendering on client-side and also their dependencies will be added to main (or vendors) chunk which both are necessary to bootstrap too.
(NOTE: All examples below are for
.client
plugins)Example 1: Defer plugins with a background task
Bad practice:
Doing task in parallel to render: