Unify/simplify `Vue.component` and `Vue.extend`
See original GitHub issueLet’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:
-
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'))({ /* … */ })
-
Put the constructor in a variable first. Still verbose, but clearer:
// app.js var PluginComponent = Vue.component('plugin-component') var app = new PluginComponent({ /* … */ })
-
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:
- Created 10 years ago
- Comments:17 (9 by maintainers)
Top GitHub Comments
Yes it could make sense, since a
Component
is (and will be?) aViewModel
, 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()
:And the
.create()
equivalents:Some thoughts after playing with some options:
Vue.component
andVue.extend
in fact serve different purposes. This becomes more obvious when we have an extended constructor, sayMyComponent
. This extended constructor also has its owncomponent
andextend
methods, but in this case they mean different things: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 withnew 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.