This is a simple enough one, but I'm not getting the answer I need from the documentation. I'm in the process of learning Vue.js, and I don't really understand where Vue.extend fits in. I get Vue.component, but I don't see what Vue.extend does which Vue.component doesn't. Is it just a legacy feature from 1.0? If not, where is it useful in Vue 2.0?
5 Answers
Their guide in the previous Vuejs.org site had a document.
Copied from http://optimizely.github.io/vuejs.org/guide/composition.html which is forked from vuejs/Vuejs.org at version 0.10.6 of Vuejs.
It is important to understand the difference between
Vue.extend()andVue.component(). SinceVueitself is a constructor,Vue.extend()is a class inheritance method. Its task is to create a sub-class ofVueand return the constructor.Vue.component(), on the other hand, is an asset registration method similar toVue.directive()andVue.filter(). Its task is to associate a given constructor with a string ID so Vue.js can pick it up in templates. When directly passing in options toVue.component(), it callsVue.extend()under the hood.Vue.js supports two different API paradigms: the class-based, imperative, Backbone style API, and the markup-based, declarative, Web Components style API. If you are confused, think about how you can create an image element with
new Image(), or with an<img>tag. Each is useful in its own right and Vue.js tries to provide both for maximum flexibility.
Comments
In addition to the answers provided, there is a practical use case for calling Vue.extend() directly if you are using TypeScript.
It is recommended in most cases to import component definitions from other files when they are needed, instead of registering them globally with Vue.component(). It keeps things organized in larger projects and makes it easier to trace problems.
With the right library, TypeScript can look at your component definition and figure out what the type of the component will be when it's initialized, meaning it can then check the functions you've defined to see if they make sense (i.e. if you reference this.foo there is actually a prop, data field, computed value, or method named foo). If you use Vue.component() it will be able to do this, but not if you use the other typical way of making components available, which is exporting object literals.
However, another option is to export your component definitions wrapped in Vue.extend(). TypeScript will then be able to recognize it as a Vue component and run its type checking accordingly, but you won't have to abandon the best-practice pattern of exporting components rather than registering them globally.
4 Comments
this.$refs and I couldn't figure it out why. Wrapping my plain object with Vue.extend({ ..... }) solved the problem.Vue.extend does helped me understand what I was missing about why it solved all my type errors when trying to access $vuetify or $store or $route in a nuxt+ts+vuetify project. I Was able to solve it with Vue.extend but did not see clear explenation in the docs to why this is the way to go. So thanks. Maybe this will help more people in the future hereThe documentation for Vue v2 is a bit sparse on Vue.extends but in short:
You use Vue.extend to create component definition (called "component constructor" in old documentation) and Vue.component to register it so it can be used in template to actually create component instance.
<div id="example">
<my-component></my-component>
</div>
// define
var MyComponent = Vue.extend({
template: '<div>A custom component!</div>'
})
// register
Vue.component('my-component', MyComponent)
// create a root instance
new Vue({
el: '#example'
})
However, everywhere in Vue API where you can pass "component constructor" created by Vue.extend you can pass plain object instead and it will call Vue.extend for you.
So the previous example can be written as
// define
var MyComponent = {
template: '<div>A custom component!</div>'
}
// register
Vue.component('my-component', MyComponent)
// create a root instance
new Vue({
el: '#example'
})
It seams that people now prefer to define components just as plain JS object (called options object in Vue world).
You can see that in current documentation for Vue v2 you rarely see Vue.extends mentioned while in docs for v1 it is clearly explained in one of the first chapters that describes Vue Instance (compare it with v2) and very well explained in Components chapter. It is explicitly stated that passing options object directly is just a sugar.
My guess is, to keep it simple and easier to learn, the new documentation just use plain objects everywhere. Why explain additional concept like "Vue constructor function", "component constructor" and what is Vue.extend when 99% of time you don't need to understand it? You can define all your app's components with plain JS options objects and register them without using Vue.extends. The only place when you must use vue constructor function is when creating root Vue instance with new Vue({...}). This uses base Vue constructor function. You don't need to know that Vue constructor can be extended to create reusable component constructors with pre-defined options because although you can create extended instances imperatively, in most cases you will be registering a component constructor as a custom element and composing them in templates declaratively.(source)
So documentation authors decided you don't need to learn about Vue.extends. But are there any use case where it's worth to use Vue.extend instead of plain options object?
Yes.
Typescript
If you want to use Typescript (and don't like class-style components) then to have proper type inference you should use Vue.extend instead of plain objects. It uses ThisType<T> feature to get proper inference for this object.
Component inheritance (and Typescript again)
You can use .extend not only on base Vue constructor function, but also on your custom component. This is less flexible than mixins because you can use many mixins in component and .extend approach works just with one component. However, it works better with Typescript for which mixins seems to not play well. With .extend Typescript support is probably "good enough" for many cases. You can access fields from inherited component but if your component have some names collision with "base" component then it doesn't work well (for example I get a type of never for conflicting props' names).
<div id="appBar">
{{fooMessage}} || {{barMessage}} || {{ additionalData}}
<button v-on:click="changeAllMessages">Change</button>
</div>
var FooComponent = Vue.extend({
data: () => ({
fooMessage: 'Foo message'
}),
methods: {
changeFooMessage() {
this.fooMessage = "Foo message changed";
}
}
})
var BarComponent = FooComponent.extend({
data: () => ({
barMessage: "Bar message",
}),
methods: {
changeAllMessages() {
this.barMessage = "Bar message changes";
this.changeFooMessage();
}
}
});
new BarComponent({ el: "#appBar", data: { additionalData: "additional data passed" } });
Here I also showed that you can use you custom component to create root Vue instance (new BarComponent({...})). Same like you can do with new Vue({...}). Maybe it's not that useful but it shows that BarComponent and FooComponent are just extended base Vue constructor functions and you can use it the same way as you would use Vue.
1 Comment
I think the confusion is because extend & component are closely connected. To create a component Vue calls extend internally:
// register an options object (automatically call Vue.extend)
Vue.component('my-component', { /* ... */ })
Therefore you can use extend to mount a subclass of the base Vue constructor (a component constructor): https://v2.vuejs.org/v2/api/#Vue-extend
However you are unlikely to come across or need to go down this road. Instead you'll be using components to get named subclasses which you can easily reference within your application.
Therefore Extend isn't really a "legacy" feature, it is central to Vue components, but the added sugar that components provide make them the default way you'll be working.
Comments
An easy way to understand Vue.extend is that it creates an object that extends the Vue class.
Typically, to create a new instance of Vue,
var vm = new Vue( {/* ...*/})
Vue.extend also creates an instance of Vue ie
var page = Vue.extend ({ /* ... */});
page is as much an instance of Vue as vm above.
To mount page, you have to mount it on a div ie,
new page().$mount('#page-div')
Where #page-div is is the I'd of a div in your page
Also, to programmers with knowledge of object oriented programming, let's say
var ext = Vue.extend({/ *...* /})
Then we can interpret ext as
class ext extends Vue{
}
Note that when you create a component, ie Vue.component('my-component', { /* ... */ }), It actually does the following : Vue.component('my-component', Vue.extend({ /* ... */})), thus Vue.component calls Vue.extend under the hood.
If you're still confused, think of Vue.extend as an utility function for creating components
