I support the idea of having a codebase that is as lean as possible, so I've created a simple example code below (you can also find it at https://codesandbox.io/embed/64j8pypr4k).
While I am not an expert in Vue, I have explored three options:
- dynamic imports,
- requireJS,
- including old-school JS generated
<script src />
.
It seems like the last option is the easiest and requires the least effort :D However, it may not be the best practice and could become obsolete soon with dynamic import support.
Please note: This example is optimized for modern browsers (with native Promises, Fetch, Arrow functions...). To test it properly, use the latest version of Chrome or Firefox :) Supporting older browsers might require polyfills and refactoring, but it will significantly increase the codebase...
So, the focus here is on dynamically loading components when needed (rather than including them upfront):
index.html
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Vue lazyload test</title>
<style>
html,body{
margin:5px;
padding:0;
font-family: sans-serif;
}
nav a{
display:block;
margin: 5px 0;
}
nav, main{
border:1px solid;
padding: 10px;
margin-top:5px;
}
.output {
font-weight: bold;
}
</style>
</head>
<body>
<div id="app">
<nav>
<router-link to="/">Home</router-link>
<router-link to="/simple">Simple component</router-link>
<router-link to="/complex">Not sooo simple component</router-link>
</nav>
<main>
<router-view></router-view>
</main>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/2.0.1/vue-router.min.js"></script>
<script>
function loadComponent(componentName, path) {
return new Promise(function(resolve, reject) {
var script = document.createElement('script');
script.src = path;
script.async = true;
script.onload = function() {
var component = Vue.component(componentName);
if (component) {
resolve(component);
} else {
reject();
}
};
script.onerror = reject;
document.body.appendChild(script);
});
}
var router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
component: {
template: '<div>Home page</div>'
},
},
{
path: '/simple',
component: function(resolve, reject) {
loadComponent('simple', 'simple.js').then(resolve, reject);
}
},
{ path: '/complex', component: function(resolve, reject) { loadComponent('complex', 'complex.js').then(resolve, reject); }
}
]
});
var app = new Vue({
el: '#app',
router: router,
});
</script>
</body>
</html>
simple.js:
Vue.component("simple", {
template: "<div>Simple template page loaded from external file</div>"
});
complex.js:
Vue.component("complex", {
template:
"<div class='complex-content'>Complex template page loaded from external file<br /><br />SubPage path: <i>{{path}}</i><hr /><b>Externally loaded data with some delay:</b><br /> <span class='output' v-html='msg'></span></div>",
data: function() {
return {
path: this.$route.path,
msg: '<p style="color: yellow;">Please wait...</p>'
};
},
methods: {
fetchData() {
var that = this;
setTimeout(() => {
/* a bit delay to simulate latency :D */
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then(response => response.json())
.then(json => {
console.log(json);
that.msg =
'<p style="color: green;">' + JSON.stringify(json) + "</p>";
})
.catch(error => {
console.log(error);
that.msg =
'<p style="color: red;">Error fetching: ' + error + "</p>";
});
}, 2000);
}
},
created() {
this.fetchData();
}
});
The function loadComponent()
handles the "magic" of loading components in this scenario.
While this approach works, it may not be the most optimal solution due to potential issues such as:
- injecting tags using JavaScript could pose security risks in the future,
- synchronously loading files can block the thread and affect performance later on,
- lack of testing for caching, which could lead to problems in production,
- missing out on the benefits of Vue components, like scoped CSS, HTML, and
automated bundling with tools like Webpack,
- omitting Babel compilation/transpilation, affecting cross-browser compatibility,
- loss of features like Hot Module Replacement and state persistence,
- potentially overlooking other issues that experienced developers might catch.
Hopefully, this information proves helpful nevertheless! :D