Difference between revisions of "VueJS"

From Organic Design wiki
(See also: AutobahnJS)
(Debugging Reactivity: Add notes and code examples on reactivity issues.)
 
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
It's becoming more and more critical in recent months that I get up to speed with a decent [[JavaScript]] framework that supports [[JavaScript#Single-page applications|Single Page Applications]]. I've built a few simple SPA's before one without a framework, and the others using [http://backbonejs.org/ Backbone]. Backbone is getting a bit dated now, and I need to build an SPA that is considerably more complicated than those I've made before.
+
It's becoming more and more critical in recent months that I get up to speed with a decent [[JavaScript]] framework that supports [[JavaScript#Single-page applications|Single Page Applications]]. I've built a few simple SPA's before, one without a framework, and the others using [http://backbonejs.org/ Backbone]. Backbone is getting a bit dated now (2017), and I need to build an SPA that is considerably more complicated than those I've made before.
  
After a lot of looking around I've settled on the [https://vuejs.org/v2/guide Vue2] framework which is similar to [https://facebook.github.io/react/ React] but has a simpler learning curve (it's practically as simple as [[jQuery]] to get started with by just including the script and writing code, or the next step, building with the [https://github.com/vuejs-templates/simple simple] template) while also scaling very well - in fact in virtually all tests it's shown to be faster and more scalable than React. Another popular option is [https://angularjs.org/ AngularJS] which shares a lot of similarities with Vue - both React and Angular have served as strong inspiration for the development of Vue. As is the case for React, Angular has a steep learning curve which is one of the main reasons for me to prefer Vue - I like the idea of an efficient minimalist system that is very easy to get started with and yet also very scalable. Angular is also very "opinionated" which means that it dictates a lot about the way you should structure you're application. Vue is much more flexible, but also offers the [https://github.com/vuejs-templates/webpack Webpack] template if you want an entire tool-chain and structure predefined for you. Angular2 is much more efficient, but Vue still beats it, and in terms of size, Vue's 23KB is hard to beat (actually it is beatable though - RiotJS is only 10KB, but I feel that the massive efficiency gains of Vue are worth the extra few KB).
+
After a lot of looking around I've settled on the [https://vuejs.org/v2/guide Vue2] framework which is similar to [https://facebook.github.io/react/ React] but has a simpler learning curve (it's practically as simple as [[jQuery]] to get started with by just including the script and writing code, or the next step, building with the [https://github.com/vuejs-templates/simple simple] template) while also scaling very well - in fact in virtually all tests it's shown to be faster and more scalable than React. Another popular option is [https://angularjs.org/ AngularJS] which shares a lot of similarities with Vue - both React and Angular have served as strong inspiration for the development of Vue. As is the case for React, Angular has a steep learning curve which is one of the main reasons for me to prefer Vue - I like the idea of an efficient minimalist system that is very easy to get started with and yet also very scalable. Angular is also very "opinionated" which means that it dictates a lot about the way you should structure you're application. Vue is much more flexible, but also offers the [https://github.com/vuejs-templates/webpack Webpack] template if you want an entire tool-chain and structure predefined for you. Angular2 is much more efficient, but Vue still beats it, and in terms of size, Vue's 23KB (or Vue 3 can be as little as ~10KB!) is hard to beat (actually it is beatable though - RiotJS is only 10KB, but I feel that the massive efficiency gains of Vue are worth the extra few KB).
  
 
== General ==
 
== General ==
Line 57: Line 57:
 
</source>
 
</source>
  
== The VueRouter extension ==
+
== Vue 3's Composition API vs Options API ==
The [https://router.vuejs.org/en/essentials/getting-started.html Vue Router] is used for making [[Single Page Application]]s. A ''VueRouter'' instance is created containing a map of all the paths and sub-paths for the application structure and which components they link to. It provides a method for creating links that will activate a path change and provides events for performing various actions in response to these changes such as transitions.
+
Vue 3 added a [https://v3.vuejs.org/api/composition-api.html composition API] syntax as an alternative to (or used in conjunction) with the standard options API.
 +
 
 +
 
 +
Reasons to use composition of options:
 +
* Composition allows splitting very complex components into parts by feature.
 +
* Composition also provides better way to create headless components.
 +
* A lighter alternative to Vuex - more freedom but harder to debug.
 +
* Much better typescript support.
 +
 
 +
 
 +
Simple example taken from the [https://v3.vuejs.org/api/composition-api.html#setup Vuejs Composition Page]:
 +
<source lang="vue">
 +
<template>
 +
<div>{{ readersNumber }} {{ book.title }}</div>
 +
</template>
 +
 
 +
<script>
 +
import { ref, reactive } from "vue";
 +
 
 +
export default {
 +
setup() {
 +
const readersNumber = ref(0);
 +
const book = reactive({ title: 'Vue 3 Guide' });
 +
 
 +
// expose to template
 +
return {
 +
readersNumber,
 +
book
 +
};
 +
}
 +
};
 +
</script>
 +
</source>
 +
 
 +
=== Composition as a Vuex Replacement ===
 +
'''store.js'''
 +
<source lang="javascript">
 +
import { reactive, readonly } from "vue";
 +
 
 +
const state = reactive({
 +
count: 0
 +
});
 +
 
 +
const increment = () => {
 +
state.count++;
 +
}
 +
 
 +
export default { state: readonly(state), increment };
 +
</source>
 +
 
 +
'''App.js'''
 +
<source lang="javascript">
 +
import { createApp } from "vue";
 +
import store from "@/store";
 +
 
 +
const app = createApp({
 +
provide: { store }
 +
});
 +
</source>
 +
 
 +
'''some-component.js'''
 +
<source lang="vue">
 +
<template>
 +
<div>{{ state.state.count }}
 +
<button @click="state.increment">Increment</button>
 +
</template>
 +
 
 +
<script>
 +
export default {
 +
inject: ["state"]
 +
}
 +
</script>
 +
</source>
 +
 
 +
== Debugging Reactivity ==
 +
If you are having trouble with Vue (3)'s reactivity I have found there are a few things to check:
  
The components that are mapped to in the routes can be either be an actual component constructor created via ''Vue.extend()'', or just a '''component options''' object. The association of the components with custom elements is not necessary since in this context they're always applied to ''&lt;router-view&gt;'' custom-elements, and the instantiation of them onto the DOM is handled automatically by the router mechanism. This is the reason that ''Vue.extend'' or even just an options object is all that is needed for the router components.
+
* The data failing to update is contained in a class that extends a class or subclass that is not a standard Object, Set or Map (e.g. EventTarget), at the time of writing these classes cannot be made reactive.
  
If the component needs to be used or referred to elsewhere in the application then ''Vue.extend'' is needed, otherwise simply passing the options object is all that's necessary and the router will take care of all the registration and instantiation internally.
+
<source lang="javascript">
 +
// Instances of 'A' cannot be made reactive since it extends EventTarget instead of Object, Map or Set.
 +
class A extends EventTarget {}
  
== Merging state ==
+
// Instances of 'B' cannot be made reactive since it extends A which extends EventTarget instead of Object, Map or Set.
Vue comes with the ''Vue.set'', ''Vue.add'' and ''Vue.delete'' methods which allow objects to be updated such that any watchers will be triggered and also the setting of items deep within structures doesn't interfere with watchers at higher levels by overwriting the containers that are referred to be other watchers.
+
class B extends A {}
  
However there is a problem that I was not able to find any solution to involving watching lists. Perhaps I should really be using [https://skyronic.com/2016/01/03/vuex-basics-tutorial Vuex] for my state management which might somehow overcome this issue, but in the meantime I've come up with a solution that works for the native Vue system.
+
// Instances of 'C' can be made reactive since it extends Object.
 +
class C {}
  
The problem is that when an item in a list changes, any watch methods that are called are passed the entire list in the changed-item parameter even if only a single item was changed. The entire list must be processed to either rebuild/update all the items or figure out which ones have changed and need updating.
+
// Instances of 'D' can be made reactive since it extends Map.
 +
class D extends Map {}
  
To get around this I made the following recursive merge function which allows single items or entire structures to be merged into the application state preserving all existing references and thus leaving all watchers intact. By working recursively the function can keep track of the keys whenever various parts of the state are changed, it stores these keys in a ''queue'' parameter in the state so that the next time a watch function is activated it has access to all the keys that have been updated since the last watch call, or since the merge was started.
+
// Instances of 'E' can be made reactive since it extends D which extends Map.
<source lang="js">
+
class E extends D {}
state.merge = function(o) {
 
  
// Traverse the o structure passing a path array and val to a sub-function
+
// Class 'F' can be made reactive but 'F.emitter' will not be reactive since it extends EventTarget instead of Object, Map or Set.
function t(o, p, f) {
+
class F {
for(var i in o) {
+
constructor() {
var q = p.concat(i);
+
this.emitter = new EventTarget();
f(q, o[i]);
 
if(o[i] !== null && typeof(o[i]) === 'object') t(o[i], q, f);
 
}
 
 
}
 
}
 +
}
 +
</source>
  
// Create any path elements from o that don't already exist in state
+
* The data is being updated internally - 'this' refers to the old object if it is referenced before the object is made reactive which means if you have asynchronous code inside the constructor then 'this' will refer to the non-reactive version of the object and therefore not trigger updates on changes.
// - v will either a value to be added (leaf node) or a container (path element)
 
// - array type objects are considered to be values not containers
 
t(o, [], function(p, v) {
 
var i,c = (typeof v === 'object') && !$.isArray(v);
 
var k = c ? false : p.pop();
 
if($.isNumeric(k)) return;
 
var r = state;
 
for(i in p) {
 
i = p[i];
 
if($.isNumeric(i)) return;
 
if(!(i in r)) {!Vue.set(r, i, {})!};
 
r = r[i];
 
}
 
  
// If the value is not a container, set it in the state structure
+
<source lang="javascript">
if(!c) {
+
// Instances of A can be made reactive but the reactive object will not have A.msg value update since 'this' refers to the old non-reactive object.
 +
class A {
 +
constructor () {
 +
this.msg = "";
  
// If it's an array, don't merge null values into the state
+
setTimeout(() => {
if($.isArray(v) && $.isArray(r[k])) {
+
this.msg = "done";
for(i in v) if(v[i] === null && i in r[k]) v[i] = r[k][i];
+
}, 1000);
}
+
}
 +
}
 +
 
 +
// Instance of B will have the B.msg value update if the B.start call is made on the reactive object.
 +
class B {
 +
constructor () {
 +
this.msg = "";
 +
}
  
// Make the path available to any watchers and set the value in the state
+
 
p = p.join('.');
+
start () {
if(!(p in state.queue)) state.queue[p] = [];
+
setTimeout(() => {
state.queue[p].push(k);
+
this.msg = "done";
{!Vue.set(r, k, v)!};
+
}, 1000);
}
+
}
});
+
}
};
 
 
</source>
 
</source>
Now you can merge compatible structures that arrive from the server, or update single items as long as they're in a mergeable form containing the whole object path. ''Null'' values can be used to leave some values in the state unchanged, for example here I'm updated a single location's title and position, but leaving other values unchanged.
+
* The data is being updated by another object that references 'this' before the reactive call - this is much the same as above but much more difficult to spot, check you are not passing 'this' or a method that references 'this' to another object before the reactive call is made.
<source lang="js">
+
 
state.merge({
+
<source lang="javascript">
locations: {
+
// Here A is just a simple class that will execute an external update method...
'50c4f5': [-36.825, 174.807, "2 Foo Avenue, Barville, Santa Baza", null, null, null]
+
class A {
 +
constructor (update) {
 +
this.update = update;
 +
}
 +
}
 +
 
 +
// Instances of class B will not have the B.msg value update even if the B.update method is called on the reactive version - this is because 'this' is in a method in the constructor that still refers to the old non-reactive object.
 +
class B {
 +
constructor () {
 +
this.msg = "";
 +
this.a = new A(() => {
 +
this.msg = "done";
 +
});
 +
}
 +
 
 +
update () {
 +
this.a.update();
 +
}
 +
}
 +
 
 +
// Instances of class C will have the C.msg value update only if the C.start method was called on the reactive version of the object before the C.update method is called (on the reactive object).
 +
class C {
 +
constructor () {
 +
this.msg = "";
 +
}
 +
 
 +
start () {
 +
this.a = new A(() => {
 +
this.msg = "done";
 +
});
 +
}
 +
 
 +
update () {
 +
this.a.update();
 
}
 
}
});
+
}
 
</source>
 
</source>
  
A typical watch function may look something like the following example. Here map markers are updated whenever items in a ''locations'' list are changed (but it's not called for every change in a merge call, usually just once at the end of the whole merge). Rather than perform updates based on the new and old values that are available as parameters to the function, we call our update function by popping items off the ''queue''. The queued changes are separated into keys named by their dot-separated path into he state structure so that different data merges can occur simultaneously without interfering with each others queues.
+
* The data is getting retrieved from a non-reactive object - this again can be similar to the last two points but is a little different again. Check for instantiations of classes within another - these instances will not be made reactive, one way to get around it is to pass the creation method or instance externally so a developer can wrap it in a reactive call before it is added.
<source lang="js">
+
 
watch: {
+
<source lang="javascript">
'state.locations': {
+
// Instances of class A will not have the A.obj value reactive unless the objCreator method makes it reactive externally.
handler: function() {
+
class A {
var id;
+
constructor (objCreator) {
while(id = app.locations.queue.pop()) { updateMarker(id); }
+
this.obj = objCreator();
},
+
}
deep: true
+
}
 +
 
 +
// Instances of class B still will not have the V.obj value reactibe unless the objCreator method makes it reactive externally.
 +
class B {
 +
constructor (objCreator) {
 +
this.objCreator = objCreator;
 +
}
 +
 
 +
start () {
 +
this.obj = this.objCreator();
 
}
 
}
 
}
 
}
 
</source>
 
</source>
 +
 +
== The VueRouter extension ==
 +
The [https://router.vuejs.org/en/essentials/getting-started.html Vue Router] is used for making [[Single Page Application]]s. A ''VueRouter'' instance is created containing a map of all the paths and sub-paths for the application structure and which components they link to. It provides a method for creating links that will activate a path change and provides events for performing various actions in response to these changes such as transitions.
 +
 +
The components that are mapped to in the routes can be either be an actual component constructor created via ''Vue.extend()'', or just a '''component options''' object. The association of the components with custom elements is not necessary since in this context they're always applied to ''&lt;router-view&gt;'' custom-elements, and the instantiation of them onto the DOM is handled automatically by the router mechanism. This is the reason that ''Vue.extend'' or even just an options object is all that is needed for the router components.
 +
 +
If the component needs to be used or referred to elsewhere in the application then ''Vue.extend'' is needed, otherwise simply passing the options object is all that's necessary and the router will take care of all the registration and instantiation internally.
  
 
== Vue-cli and Web-pack ==
 
== Vue-cli and Web-pack ==
Line 170: Line 292:
 
npm run dev.
 
npm run dev.
 
</source>
 
</source>
 +
 +
You can then make the ''src'' directory into a repo and begin refining your project to your own needs running ''npm run dev'' each time changes are committed, and ''npm build'' to compile the project into its minified state for production.
  
 
== Notes ==
 
== Notes ==
Line 178: Line 302:
  
 
== See also ==
 
== See also ==
 +
*[[Our SPA]]
 +
*[https://www.vuemastery.com/courses/intro-to-vue-js/vue-instance/ Free Vue mastery course]
 
*[https://github.com/vuejs/awesome-vue Awesome Vue] ''- big list of examples and tutorials''
 
*[https://github.com/vuejs/awesome-vue Awesome Vue] ''- big list of examples and tutorials''
 
*[https://vuecomponents.com/ VueComponents.com] ''- a lot of nice clear examples''
 
*[https://vuecomponents.com/ VueComponents.com] ''- a lot of nice clear examples''
Line 183: Line 309:
 
*[https://v1.vuejs.org/guide/application.html Building large-scale apps]
 
*[https://v1.vuejs.org/guide/application.html Building large-scale apps]
 
*[https://github.com/MetinSeylan/Vue-Socket.io?ref=madewithvuejs Vue-Socket.io] ''- and [https://medium.com/@michaelmangial1/getting-started-with-vue-js-socket-io-8d385ffb9782 this tutorial], also [https://github.com/crossbario/autobahn-js AutobahnJS]''
 
*[https://github.com/MetinSeylan/Vue-Socket.io?ref=madewithvuejs Vue-Socket.io] ''- and [https://medium.com/@michaelmangial1/getting-started-with-vue-js-socket-io-8d385ffb9782 this tutorial], also [https://github.com/crossbario/autobahn-js AutobahnJS]''
 +
*[https://skyronic.com/2016/01/03/vuex-basics-tutorial Simple Vuex tutorial]
 +
*[https://www.nativescript.org/ NativeScript] ''- supports Vue for building native Android and iOS apps''
 +
*[https://vuetifyjs.com/en/ Vuetify.com] ''- Material Design Component Framework''
 +
*[https://www.mediawiki.org/wiki/Vue.js/Migration_Project_Charter MediaWiki VueJS migration project]
 
[[Category:JavaScript]]
 
[[Category:JavaScript]]

Latest revision as of 00:09, 14 February 2023

It's becoming more and more critical in recent months that I get up to speed with a decent JavaScript framework that supports Single Page Applications. I've built a few simple SPA's before, one without a framework, and the others using Backbone. Backbone is getting a bit dated now (2017), and I need to build an SPA that is considerably more complicated than those I've made before.

After a lot of looking around I've settled on the Vue2 framework which is similar to React but has a simpler learning curve (it's practically as simple as jQuery to get started with by just including the script and writing code, or the next step, building with the simple template) while also scaling very well - in fact in virtually all tests it's shown to be faster and more scalable than React. Another popular option is AngularJS which shares a lot of similarities with Vue - both React and Angular have served as strong inspiration for the development of Vue. As is the case for React, Angular has a steep learning curve which is one of the main reasons for me to prefer Vue - I like the idea of an efficient minimalist system that is very easy to get started with and yet also very scalable. Angular is also very "opinionated" which means that it dictates a lot about the way you should structure you're application. Vue is much more flexible, but also offers the Webpack template if you want an entire tool-chain and structure predefined for you. Angular2 is much more efficient, but Vue still beats it, and in terms of size, Vue's 23KB (or Vue 3 can be as little as ~10KB!) is hard to beat (actually it is beatable though - RiotJS is only 10KB, but I feel that the massive efficiency gains of Vue are worth the extra few KB).

General

The general idea behind Vue is to "vueify" elements by constructing a new Vue instance with the el property selecting the node(s), which similar to jQuery's $("#foo") concept.

new Vue({
	el: "#foo"
});

This is not a very useful example though, the most simple functional "Hello World!" example is:

<div id="foo">{{bar}}</div>
var app = new Vue({
	el: "#foo"
	data: {
		bar: "Hello World!"
	}
});

Here the element that the Vue instance binds to has a mustache-style template parameter specified in it called bar. The Vue instance binds to it with the #foo selector in its el property, and creates a persistent connection between the bar parameter in the element and the bar sub-property of its data property. The bar property of the view instance can be accessed or changed via the instance with app.bar and any updates to it will be immediately reflected in the HTML element.

Component model

Vue components allow this idea to be encapsulated into a re-usable package, the usual usage being to register the component with a custom element as follows:

<div id="baz">
	<foo></foo>
</div>
// Register the component with "foo" custom-elements
Vue.component('foo', {
	template: '<div>This is the Foo component!</div>'
})

// Instantiate the component by "vueifying" the element containing the custom-element
new Vue({
	el: '#baz'
})

A component is actually a sub-class of the Vue object created by internally calling the Vue.extend method which adds all the passed options to the base Vue class and returns a callable constructor function reference that's used to create new instances of the component via the new directive. Components can be further sub-classed by calling their extend method in the same way.

The Vue.component method is then used to register a component with a custom element as follows. For comvenience, the Vue.component method allows these two separate steps to be combined into one by providing the options object along with the custom-element name which is what was done in the above example.

// Create the new component as an extension of the base Vue class
var newComponent = Vue.extend({
	template: '<div>This is the Foo component!</div>'
})

// Register the component with "foo" custom-elements
Vue.component('foo', newComponent);

Vue 3's Composition API vs Options API

Vue 3 added a composition API syntax as an alternative to (or used in conjunction) with the standard options API.


Reasons to use composition of options:

  • Composition allows splitting very complex components into parts by feature.
  • Composition also provides better way to create headless components.
  • A lighter alternative to Vuex - more freedom but harder to debug.
  • Much better typescript support.


Simple example taken from the Vuejs Composition Page:

<template>
	<div>{{ readersNumber }} {{ book.title }}</div>
</template>

<script>
import { ref, reactive } from "vue";

export default {
	setup() {
		const readersNumber = ref(0);
		const book = reactive({ title: 'Vue 3 Guide' });

		// expose to template
		return {
			readersNumber,
			book
		};
	}
};
</script>

Composition as a Vuex Replacement

store.js

import { reactive, readonly } from "vue";

const state = reactive({
	count: 0
});

const increment = () => {
	state.count++;
}

export default { state: readonly(state), increment };

App.js

import { createApp } from "vue";
import store from "@/store";

const app = createApp({
	provide: { store }
});

some-component.js

<template>
	<div>{{ state.state.count }}
	<button @click="state.increment">Increment</button>
</template>

<script>
export default {
	inject: ["state"]
}
</script>

Debugging Reactivity

If you are having trouble with Vue (3)'s reactivity I have found there are a few things to check:

  • The data failing to update is contained in a class that extends a class or subclass that is not a standard Object, Set or Map (e.g. EventTarget), at the time of writing these classes cannot be made reactive.
// Instances of 'A' cannot be made reactive since it extends EventTarget instead of Object, Map or Set.
class A extends EventTarget {}

// Instances of 'B' cannot be made reactive since it extends A which extends EventTarget instead of Object, Map or Set.
class B extends A {}

// Instances of 'C' can be made reactive since it extends Object.
class C {}

// Instances of 'D' can be made reactive since it extends Map.
class D extends Map {}

// Instances of 'E' can be made reactive since it extends D which extends Map.
class E extends D {}

// Class 'F' can be made reactive but 'F.emitter' will not be reactive since it extends EventTarget instead of Object, Map or Set.
class F {
	constructor() {
		this.emitter = new EventTarget();
	}
}
  • The data is being updated internally - 'this' refers to the old object if it is referenced before the object is made reactive which means if you have asynchronous code inside the constructor then 'this' will refer to the non-reactive version of the object and therefore not trigger updates on changes.
// Instances of A can be made reactive but the reactive object will not have A.msg value update since 'this' refers to the old non-reactive object.
class A {
	constructor () {
		this.msg = "";

		setTimeout(() => {
			this.msg = "done";
		}, 1000);
	}
}

// Instance of B will have the B.msg value update if the B.start call is made on the reactive object.
class B {
	constructor () {
		this.msg = "";
	}


	start () {
		setTimeout(() => {
			this.msg = "done";
		}, 1000);
	}
}
  • The data is being updated by another object that references 'this' before the reactive call - this is much the same as above but much more difficult to spot, check you are not passing 'this' or a method that references 'this' to another object before the reactive call is made.
// Here A is just a simple class that will execute an external update method...
class A {
	constructor (update) {
		this.update = update;
	}
}

// Instances of class B will not have the B.msg value update even if the B.update method is called on the reactive version - this is because 'this' is in a method in the constructor that still refers to the old non-reactive object.
class B {
	constructor () {
		this.msg = "";
		this.a = new A(() => {
			this.msg = "done";
		});
	}

	update () {
		this.a.update();
	}
}

// Instances of class C will have the C.msg value update only if the C.start method was called on the reactive version of the object before the C.update method is called (on the reactive object).
class C {
	constructor () {
		this.msg = "";
	}

	start () {
		this.a = new A(() => {
			this.msg = "done";
		});
	}

	update () {
		this.a.update();
	}
}
  • The data is getting retrieved from a non-reactive object - this again can be similar to the last two points but is a little different again. Check for instantiations of classes within another - these instances will not be made reactive, one way to get around it is to pass the creation method or instance externally so a developer can wrap it in a reactive call before it is added.
// Instances of class A will not have the A.obj value reactive unless the objCreator method makes it reactive externally.
class A {
	constructor (objCreator) {
		this.obj = objCreator();
	}
}

// Instances of class B still will not have the V.obj value reactibe unless the objCreator method makes it reactive externally.
class B {
	constructor (objCreator) {
		this.objCreator = objCreator;
	}

	start () {
		this.obj = this.objCreator();
	}
}

The VueRouter extension

The Vue Router is used for making Single Page Applications. A VueRouter instance is created containing a map of all the paths and sub-paths for the application structure and which components they link to. It provides a method for creating links that will activate a path change and provides events for performing various actions in response to these changes such as transitions.

The components that are mapped to in the routes can be either be an actual component constructor created via Vue.extend(), or just a component options object. The association of the components with custom elements is not necessary since in this context they're always applied to <router-view> custom-elements, and the instantiation of them onto the DOM is handled automatically by the router mechanism. This is the reason that Vue.extend or even just an options object is all that is needed for the router components.

If the component needs to be used or referred to elsewhere in the application then Vue.extend is needed, otherwise simply passing the options object is all that's necessary and the router will take care of all the registration and instantiation internally.

Vue-cli and Web-pack

Vue-cli is a simple CLI for scaffolding Vue.js projects from the shell using simple official or custom project templates to rapidly build working prototypes. Vue-loader (a Webpack loader for Vue) is used to convert the single-file-component syntax into a plain JavaScript module for normal execution in the browser.

The init command creates a new project from a template, and the build command rebuilds it after changes are made.

First NodeJS and the Node package manager must be installed:

apt install nodejs npm

Nodejs must then be brought up to date using their own upgrade system since the operating system package managers are usually well behind what's required by most dependencies.

sudo npm cache clean -f
sudo npm install -g n
sudo n stable
sudo ln -sf /usr/local/n/versions/node/<VERSION>/bin/node /usr/bin/node
sudo ln -sf /usr/local/n/versions/node/<VERSION>/bin/node /usr/bin/nodejs

Now vue-cli can be installed:

npm install -g vue-cli

And then a project created and run, for example:

cd /var/www
vue init webpack-simple my-project
cd my-project
npm install
npm run dev.

You can then make the src directory into a repo and begin refining your project to your own needs running npm run dev each time changes are committed, and npm build to compile the project into its minified state for production.

Notes

  • It may be good to use Weex templates for cross-platform native rendering
  • "Vue has a clearer separation between directives and components. Directives are meant to encapsulate DOM manipulations only, while components are self-contained units that have their own view and data logic. In Angular, there’s a lot of confusion between the two.
  • Cleaning up on transitions
  • Properties in routes

See also