VueJs templating. How to load external templates

I'm new to Vue.js, I've used AngularJS for some time and in angular we used to load templates such as,

template: '/sometemplate.html',
controller: 'someCtrl'

How can we do such a thing in Vue, instead of keeping large HTML templates inside JavaScript like this,

new Vue({
el: '#replace',
template: '<p>replaced</p>'
})

This is OK for small templates but for large templates is this practical?

Is there a way to load external template HTML or use HTML template inside a script tag like in Vue?

<script type="x-template" id="template">HTML template goes here</html>
118957 次浏览

You can use this approach with superagent:

var promise = superagent.get("something.html")
.end(function (error, response) {
if (error) {
console.error("load of something.html failed", error));
return;
}


var parser = new DOMParser()
var doc = parser.parseFromString(response.text, "text/html");
document.body.appendChild(doc.scripts[0]);
});

Just put your <script> tag based template inside of something.html on your server.

If you are using jQuery, .load should work.

Just make sure this completes before the DOM in question is compiled by Vue. Or use $mount to manually set things up.

David, that is a nice example, but what's the best way to make sure the DOM is compiled?

https://jsfiddle.net/q7xcbuxd/35/

When I simulate an async operation, like in the example above, it works. But as soon as I load an external page "on the fly", Vue complains because the DOM is not ready. More specifically: Uncaught TypeError: Cannot set property 'vue' of undefined Is there a better way to do this than to call $compile when the page has loaded? I've tried with $mount, but that didn't help.

UPDATE: Never mind, I finally figured out how to do it:

Vue.component('async-component', function (resolve, reject) {
vue.$http.get('async-component.html', function(data, status, request){
var parser = new DOMParser();
var doc = parser.parseFromString(data, "text/html");
resolve({
template: doc
});
});
});

And in the actual template, I removed the

<script id="someTemplate" type="text/x-template"></script>

tags and only included the html.

(This solution requires the http loader from https://cdnjs.cloudflare.com/ajax/libs/vue-resource/0.1.10/vue-resource.min.js)

Use browserify to bundle everything like this:

//Home.js


import Vue from 'vue';


var Home = Vue.extend({
template: require('./Home.vue')
});


export default Home;


//Home.vue
<h1>Hello</h1>


// And for your browserify bundle use a transform called stringify


... .transform(stringify(['.html', '.svg', '.vue', '.template', '.tmpl']));

You can use the script tag template by just referring to its id.

{
template: '#some-id'
}

Though, I highly recommend using vueify (if you use browserify) or vue-loader (if you use webpack) so you can have your components stored in nice little .vue files like this.

vue file

Also, the author of Vue wrote a nice post about the topic of external template urls:

https://vuejs.org/2015/10/28/why-no-template-url/

You can try this:
for Vue2 : https://github.com/FranckFreiburger/http-vue-loader
for Vue3 : https://github.com/FranckFreiburger/vue3-sfc-loader

示例(VUE2):

 new Vue({
components: {
'my-component': httpVueLoader('my-component.vue')
},
...

Example (Vue3) :

Vue.createApp({
components: {
'my-component': Vue.defineAsyncComponent(() => loadModule('./myComponent.vue', opts))
},
...

1. In Vue 2.x I recommend sticking with convention by using .vue files but instead, inverting the order of imports:

// template.vue
<template>
<div class="helloworld">
<h1>Hello world</h1>
</div>
</template>


<script>
import src from './src'
export default src
</script>

and in a separate file

// src.js
export default {
name: 'helloworld',
props: {},
...
}

Then in your component registration

import helloworld from './helloworld/template.vue'


new Vue({
components: {
'helloworld': helloworld
},
...})

This way you get the best of both worlds and you don't have to force yourself to build templates within a string.

2. If you want to lazy load, apparently there is a way to do so in Vue 2.x

new Vue({
components: {
'helloworld': () => import(/* webpackChunkName: "helloworld" */ './helloworld/template.vue')
},
...})

This will load helloworld.js (which will contain all that component's code) on request of that page in the browser.

Of course, all the above assumes you are using ES6 with import capabilities

I've tried http-vue-loader and it works fine. This library is easy to use and has good documentation and examples

Although you can't load templates from filed directly you still can keep html in separate single-file components. You can even skip <script>...</script> part.

Usage (from loader's documentation)

my-component.vue

<template>
<div class="hello">Hello \{\{who}}</div>
</template>

index.html

<!doctype html>
<html lang="en">
<head>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
</head>


<body>
<div id="my-app">
<my-component></my-component>
</div>


<script type="text/javascript">
new Vue({
el: '#my-app',
components: {
'my-component': httpVueLoader('my-component.vue')
}
});
</script>
</body>
</html>

Both files should be placed in one folder at the same level

there are at least 2 ways to achieve what you want, on of them (x-templates) already mentioned by Bill Criswell, but I think it worth to add an example

  1. Define your component like this

    Vue.component('my-checkbox', { // id of x-template template: '#my-template' });

  2. Add html file with your x-template (id should match the one you specified in the component)

    <script type="text/x-template" id="my-template">...</script>

Another approach (and I like this one better) would be to use inline template

  1. Define your template like this

    Vue.component('my-template', {});

  2. Add html file with your component and template inside it

    <my-template inline-template>place for your html</my-template>

Just don't forget to add inline-template attribute, otherwise it won't work