VueJS

From Organic Design wiki
Revision as of 13:28, 25 February 2017 by Nad (talk | contribs) (Router specific notes)

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, 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 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).

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."

Router specific notes

One thing I found very difficult to figure out as a Vue noob was how to have router components containing live data such as watchers that respond to changes in the global state. The reason this was difficult to set up is that the component referred to in the router needs to be a proper component (Vue instance) not just an object containing a template instance. This means they're defined with Vue.extend, but that only returns a prototype that needs to be instantiated within the context of a DOM element in the page. To keep the code for each component in one place, we need to have a way of deferring the instantiation until after the routes have all been created so that the elements all exist. This can be done using the router.onReady method, but that comes with its own complication: we need the router instance to exist before the router component prototypes are defined so they can use the router.onReady method. This can be done by creating the router instance before the route components without any routes, and then adding the routes after the component prototypes are created.

Here's an example. A child route (PropertyBuy) can have any part of the main shared state in its template such as {{bids}} which will have a live connection to the corresponding properties in state. Watchers can be set etc without causing "node not found" exceptions when the route is replaced, but keeping the definition of the dynamic component and its instantiation together so they can be defined in the same component file.

// The main state accessible by all components
var state = {
	bids: 'foo',
};

// Instantiate the router with no routes before the components are defined
var router = new VueRouter({
	routes: []
});

// A template-only router component
const Property = {
	template: '#tpl-property'
};

// Another template-only router component
const PropertySell = {
	template: '#tpl-property-sell'
};

// A component with dynamic view depending on global state
const PropertyBuy = Vue.extend({
	template: '#tpl-property-buy',
	data: function() { return state; },
	watch: {
		bids: function( val ) { console.log( 'change detected' ); }
	}
});

// After router is ready we instantiate the dynamic component onto its element
router.onReady(function() {
	new PropertyViewBuy({
		el: '#pv-buy'
	})
});

// Now that the components are defined we add the routes
router.addRoutes([
	{ path: '/property', component: Property,
		children: [
			{ path: 'buy', component: PropertyBuy },
			{ path: 'sell', component: PropertySell }
		]
	}
]);

// Now we can mount the app
var app = new Vue({router}).$mount('#app');

See also