question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unify/simplify `Vue.component` and `Vue.extend`

See original GitHub issue

Let’s say I have a plugin providing a component which is meant to be instantiated with new (not from a template):

// plugin.js
module.exports = function(Vue) {
  Vue.component('plugin-component', {
    create: function() {
      window.alert('I am a very annoying plugin')
    }
  })
}
// app.js
var plugin = require('./plugin')
Vue.use(plugin)

Now, I want to use it for my main VM. Some solutions:

  1. Direct instantiation on the constructor returned by Vue.component(name). Seems verbose and unintuitive:

    // app.js
    // These extra parenthesis are necessary
    // because `new` takes precedence on a function call
    var app = new (Vue.component('plugin-component'))({
      /* … */
    })
    
  2. Put the constructor in a variable first. Still verbose, but clearer:

    // app.js
    var PluginComponent = Vue.component('plugin-component')
    var app = new PluginComponent({ /* … */ })
    
  3. Provide a specific solution in my plugin. Clearer app.js, but proprietary API, and adds a lot of cruft in plugin.js.

    // plugin.js
    var component = {
        create: function() {
          window.alert('I am a very annoying plugin')
        }
    }
    module.exports = function(Vue) {
      Vue.component('plugin-component', component)
    }
    module.exports.vmFrom = function(Vue, options) {
      var VM = Vue.component('plugin-component')
      if (!VM) VM = Vue.extend(component)
      return new VM(options)
    }
    module.exports.component = component
    
    // app.js
    var app = plugin.vmFrom(Vue, { /* … */ })
    

Proposed solution

Please ignore if I missed some already existing mechanism.

A solution could be to introduce a new global method: Vue.create([String | VM], options). It’s really the same than the #149 proposal, but for instantiation purposes.

The reason to not add this parameter on the Vue constructor itself is because it would feel weird to provide a constructor as a parameter of another constructor.

It would allow to do this:

var app = Vue.create('plugin-component', { /* … */ })

It would also add a new syntax for classic instantiations:

var app = Vue.create({ /* … */ })

Which could have the positive effect of pleasing people who don’t want new in their code no matter what, thanks to the “new considered harmful” articles… I know, it’s a stupid reason 😃

Extend-only plugin API

Somewhat related question: what if a plugin wants to provide an extended VM only, without registering a component? Should this be taken into consideration by the plugin API, or should it be implemented by every plugin the way they want?

Example:

// plugin.js
module.exports = function(Vue) {
  // Components, directives etc. registered by the plugin:
  Vue.component('foo', {})
  Vue.directive('bar', {})
}
module.exports.getA = function(Vue) {
  return Vue.extend({ /* … */ })
}
module.exports.getB = function(Vue) {
  return Vue.extend({ /* … */ })
}
// app.js
var Vue = require('vue')
var plugin = require('./plugin')
var A = plugin.getA(Vue)

What do you think?

Issue Analytics

  • State:closed
  • Created 10 years ago
  • Comments:17 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
bpierrecommented, Mar 4, 2014

Yes it could make sense, since a Component is (and will be?) a ViewModel, and both terms appears everywhere. The only difference I see is that a Component is attached to another ViewModel (or a ViewModel instance) with an ID, and can be called from the template.

<element v-component=""> could be replaced by <element v-viewmodel="">, but it does look confusing.

Conversely, the term ViewModel could be replaced with the term Component everywhere (except in the Getting Started section), and .extend() could be replaced by a non-chainable .component():

// Same as the current .extend()
var ExtendedComponent = Vue.component({})

// Get a component, same as the current .component('name')
var ExtendedComponent = Vue.component('extended-component')

// Still the same, except it returns the extended component
var ExtendedComponent = Vue.component('some-registered-component', {})

// Inheritance
var ExtendedComponent = Vue.component('extended-component', 'base-component', {})

// And now that becomes weird…
ExtendedComponent.component('another-extended-component', 'another-base-component', { /* ... */ })

And the .create() equivalents:

// Same as `new Vue({})`
Vue.create({})

// Same as `new (Vue.component('some-registered-component'))({})`
Vue.component('some-registered-component').create({})

// Same as above
Vue.create('some-registered-component', {})
1reaction
yyx990803commented, Mar 7, 2014

Some thoughts after playing with some options:

Vue.component and Vue.extend in fact serve different purposes. This becomes more obvious when we have an extended constructor, say MyComponent. This extended constructor also has its own component and extend methods, but in this case they mean different things:

// sub-component is using Vue as the base class
// and encapsulated inside MyComponent
MyComponent.component('sub-component', { /* ... */ })

// ExtendedComponent is using MyComponent as the base class
// and isn't necessarily part of MyComponent
var ExtendedComponent = MyComponent.extend({ /* ... */ })

In this case unifying the two would actually cause confusion (as noted in @bpierre 's “now that becomes weird…” example).

Back to the original problem, I think a core issue here is Vue.js is trying to support two different paradigms - the first is Backbone-style, class & inheritance based API, and the second is Web Component style, declarative, markup-based API. Think about how you can use a <img> tag and also create an image node with new Image(). I think we do need to support both because this gives greater flexibility, and a little verbosity in this case is outweighed by the benefits.

I think it would still help to provide some sugar for some common tasks, but I’d prefer to do so without affecting the underlying semantics.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Extending Vue.js Components
Vue provides a number of different APIs and patterns you can use for extending a component. In this article, I'll give you a...
Read more >
Extending Vue - vue.js
Extend with Mixins. Mixins are a flexible way to distribute reusable functionalities for Vue components. You can write a mixin just like a...
Read more >
What is Vue.extend for? - Stack Overflow
Its task is to associate a given constructor with a string ID so Vue.js can pick it up in templates. When directly passing...
Read more >
Extending Vue Components for Cleaner Code | Littlelines
There is a caveat with markup (aka the template part of a vue single file component), however. The component that extends a base...
Read more >
How to use the vue.extend function in vue - Snyk
component ('settings-dialog', vue.extend({ template: '#settings-dialog', props: { visible: { type: ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found