Make your Vue apps Lighter
by being lazy
Amsterdam 15th Feb 2019
credits to @callumacrae
Eduardo
San Martin Morote
π Vue core team
π¨βπ» Freelance
πParis
Prototyping is fun
ReBuild plugins in Vue
Code Splitting
You should split your code
It all starts with webpack
Dynamic Import
import('a_package').then(module => module.default)
import('./utils.js').then(module => module.log)
const aPackage = () => import('a_package')
const utils = () => import('./utils.js')
Configure Webpack
Sean / Tobias / Johannes
Me
type="module"
<script type="module">
const utils = () => import('./utils.js')
utils().then(({ log }) => {
log('hey')
})
</script>
Lazy-Loading components
β¨Anywhere
import Calendar from '@/components/Calendar.vue'
const Calendar = () => import('@/components/Calendar.vue')
Per-Page
// router.js
const Home = () => import('@/views/Home')
const User = () => import('@/views/User')
const routes = [
{ path: '/', component: Home },
{ path: '/users/:id', component: User },
]
in-componenT
// App.vue
const Search = () => import('@/components/Search')
export default {
components: { Search }
}
Chunks
Search.vue
The real deal
βοΈ vendors
π Online
π Fast 3G
π’ Slow 3G
Don't lazy load what is needed on initial render
Safe Lazy load Recipee π₯
- Local component registration
- Lazy load routes
PROFIT π₯
Prefetch
import(/* webpackPrefetch: true */ 'LoginModal')
<link rel="prefetch" href="login-modal-chunk.js" />
Preload
import(/* webpackPreload: true */ 'ChartingLibrary')
<link rel="preload" href="chunk.js" />
β οΈ Prefetch / Preload
// SomePage.vue
() => import(/* webpackPrefetch: true */ 'LoginModal')
() => import(/* webpackPreload: true */ 'ChartingLibrary')
const SomePage = () => import('./SomePage.vue')
Only works for lazy loaded chunks:
Dynamic Components
in-template
<component :is="dynamicComponent"/>
// inside of a component method
// local or global component
this.dynamicComponent = 'UserCard'
// component descriptor
// import UserCard from './UserCard'
this.dynamicComponent = UserCard
In-Render functions
export default {
render (h) {
return h(this.dynamicComponent)
},
methods: {
changeDisplayedComponent () {
this.dynamicComponent = 'UserCard'
// or
this.dynamicComponent = UserCard
}
}
}
β¨Anywhere
import Calendar from '@/components/Calendar.vue'
const Calendar = () => import('@/components/Calendar.vue')
Dynamic Lazy components
<component :is="dynamicComponent"/>
// inside of a component method
this.dynamicComponent = () => import('./UserCard')
render(h) { return h(this.dynamicComponnet) }
Dynamic Imports with Expressions
import(`@/components/async/${name}.vue`)
let webpack creates chunks π
Careful...
import('@/components/' + myComponentPath) // π± β οΈ
your app
Provide Webpack a more strict filter
import('@/components/' + myComponentPath) // π± β οΈ
import(`@/components/Lazy${name}.vue`) // π
import(`@/components/async/${name}.vue`) // π―
Lazy loading Dynamic Components
USage
<ExamplePreview name="counter"/>
dynamic components + computed = π
computed: {
demoComponent() {
// make demoComponent depend on name
const name = this.name
return () =>
import(`@docs/examples/${name}/index.js`)
}
}
<component :is="demoComponent"/>
Analyzing your app
Monster
when your app is unmaintainable but you still care
vue ui
Lazy Load
- Local components registration
- Always lazy load routes
- Lazy load anything not need up-front
Dynamic import expressions
- As strict as possible
- Prefetch flag (can wait)
- Preload (needed now)
Handling Errors
β Error
// in a component method
this.error = null
return import('./Component.vue').catch(err => {
this.error = err
})
<p class="danger" v-if="error">{{ error }}</p>
β³ LOading
this.error = null
this.pending = true
return import('./Component.vue').then(module => {
this.isPending = false
return module
}).catch(err => this.error = err)
<p class="danger" v-if="error">{{ error }}</p>
<p class="info" v-else-if="isPending">Loading...</p>
β³ Delay loader
<div>
<p v-if="error">Error: {{ error.message }}</p>
<p v-else-if="isPending && isDelayElapsed">Loading...</p>
<ul v-else>
<li v-for="user in data">{{ user.name }}</li>
</ul>
</div>
fetchUsers() {
this.error = null
this.isPending = true
this.isDelayElapsed = false
getUsers()
.then(users => { this.data = users })
.catch(error => { this.error = error })
.finally(() => { this.isPending = false })
setTimeout(() => {
this.isDelayElapsed = true
}, 200)
}
handling async components
me
Async component factory
const AsyncComponent = () => ({
component: import('./MyComponent.vue'),
loading: LoadingComponent,
error: ErrorComponent,
delay: 200,
timeout: 3000
})
π Vue-promised
<Promised
:promise="componentPromise"
#combined="{ isPending, data, error }"
>
<p v-if="error">
{{ error.message}}
</p>
<p v-else-if="isPending">Loading component</p>
<component
v-else
:is="data.default"
/>
</Promised>
π Github repo
Patreons π
patreon.com/posva
Thanks! π
Making your Vue apps faster by being lazy
By Eduardo San Martin Morote
Making your Vue apps faster by being lazy
We keep shipping more and more features in our Web applications, and as a result, we ship heavier apps that take more time to load. So we use bundlers like webpack to split our application code into multiple bundles and load them asynchronously. As a result, we end up having asynchronous code pretty much everywhere in our apps. This means that we need to handle unexpected errors that were otherwise impossible, like network ones, handle loading state and make sure the application is able to recover from these errors. During this talk, we will take a look at different patterns about handling asynchronous request correctly in Vue applications in order to make our Apps feel light, fast and reliable everywhere.
- 7,615