How do I extend another VueJS component in a single-file component? (ES6 vue-loader)
Asked Answered
L

5

42

I am using vue-loader to construct my *.vue single-file components, but I am having trouble with the process of extending a single-file component from another.

If one component follows the spec to export default { [component "Foo" definition] }, I would think it is just a matter of importing this component (as I would with any child component) and then export default Foo.extend({ [extended component definition] })

Unfortunately this does not work. Can anyone please offer advice?

Lavena answered 12/3, 2016 at 22:30 Comment(0)
W
20

The proper way to do this would be to use mixins: http://vuejs.org/guide/mixins.html

Think of mixins as abstract components, which you can extend. So you could create a mixin with any functionality you wanted to have in both, and then just apply it to each of your components.

Whitehorse answered 12/3, 2016 at 22:43 Comment(1)
Word of warning: if you're using TypeScript instead of Javascript in your Vue project, mixins won't compile correctly out-of-the-box. Fortunately, someone wrote a package to solve this problem: npmjs.com/package/vue-typed-mixinsHellgrammite
L
51

After some testing, the simple solution was to be sure to export a Vue.extend() object rather than a plain object for any component being extended.

In my case, the base component:

import Vue from 'vue'

export default Vue.extend({ [component "Foo" definition] })

and the extended component:

import Foo from './Foo'

export default Foo.extend({ [extended component definition] })
Lavena answered 12/3, 2016 at 22:43 Comment(4)
This is a cool idea, glad you got it figured out. I can imagine in some places this would be preferable to mixinsWhitehorse
Exactly what I was looking for, thanks. This is better answer than accepted solution IMO, because a whole component cannot be made mixin as it would highly increase the chance of name collision. Mixin should be limited to smaller shared code.Veraveracious
This worked for me but I had to be careful about overriding functions. For instance lifecycle calls like mounted did not get overridden, oddly both got called : it and its super. But functions declared in methods did get overwritten. So i moved logic that needed to be overridden into methods and called them from the lifecycle call.Newby
I would have preferred to use this approach instead of mixins because this feels like a more traditional inheritance pattern, but unfortunately this doesn't entirely work if you're using TypeScript instead of Javascript because the "child" components won't inherit the "parent" component's data properties. Frustratingly, mixins don't work out-of-the-box in TypeScript either. The way I finally managed to get component inheritance working for TypeScript was by using this plugin to fix mixins for TypeScript: npmjs.com/package/vue-typed-mixinsHellgrammite
C
29

Another possibility is the extends option:

import Foo from './Foo'

export default { extends: Foo }
Colleen answered 30/5, 2016 at 2:16 Comment(4)
Is there anyway we can extend multiple objects like export default { extends: Foo, Bar, Baz }Pulido
@Pulido mixins.Scheller
is there any useful documentation on this? how are templates merged? can I add a second event handlers? etcKelleher
THAT IS THE SOLUTION! :)Weaponeer
W
20

The proper way to do this would be to use mixins: http://vuejs.org/guide/mixins.html

Think of mixins as abstract components, which you can extend. So you could create a mixin with any functionality you wanted to have in both, and then just apply it to each of your components.

Whitehorse answered 12/3, 2016 at 22:43 Comment(1)
Word of warning: if you're using TypeScript instead of Javascript in your Vue project, mixins won't compile correctly out-of-the-box. Fortunately, someone wrote a package to solve this problem: npmjs.com/package/vue-typed-mixinsHellgrammite
M
5

I'd avoid the "extends" feature of Vue, simply because it is a poorly named method of Vue. It doesn't really extend anything, not in the case of inheritance. What it does is exactly what the mixin does, it merges the two components together. It has nothing to do with the template code, which isn't extensible either. The "extend" Vue method should have been called "merge".

At any rate, Vue must work with the hierarchy of the DOM and thus it composes to the DOM. That same thinking should rule your SFC building. Use component mixins for base behavior and add the mixins to your components as you need that behavior, while composing together the smallest common parts into bigger parts, all at the same time keeping your template code to a minimum. You should think "thin views, think models" while composing your SFCs. :)

Mccarthyism answered 30/10, 2018 at 4:15 Comment(0)
M
1

As of Vue 3, the Composition API is introduced, making it very easy to share state across components.

Rather than defining your component with properties on the component definition object e.g. data, computed, methods, etc, the Composition API allows you to instead create a setup function where you declare and return these.

An example:

file: useCounter.js

import { reactive, computed } from "vue";

export default function {
  const state = reactive({
    count: 0,
    double: computed(() => state.count * 2)
  });

  function increment() {
    state.count++
  }

  return {
    count,
    double,
    increment
  }
}

Now the counter feature can be seamlessly introduced into any Vue component using it's setup function:

file: MyComponent.vue

<template>
   <button @click="increment">
      Count is: {{ count }}, double is: {{ double }}
   </button>
</template>

<script>
import useCounter from "./useCounter";

export default {
  setup() {
    const { count, double, increment } = useCounter();
    return {
      count,
      double,
      increment
    }
  }
}
</script>

One of the main benefits of declaring a component using the Composition API is that it makes logic reuse and extraction very easy. Composition functions are the most straightforward and cost-free way of extending a component by making its features modular and reusable.

Materialist answered 12/8, 2020 at 7:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.