Learning Vue.js as a WordPress Developer Part 1 – Getting Started

This post is part of a series called “Learning Vue.js as a WordPress Developer”

My frontend development skills are getting dated. While attempting to build out a concept for a new idea (see video demo below), I hit a performance wall. My custom WordPress template was taking over 30 seconds to load due to excessive jQuery usage combined with a rather large data set. I knew it was overdue time for me to learn one of the modern JS frameworks. I decided on Vue.js as I really like their focus on modularity. I feel it’s the best option for dropping into an existing WordPress project. Spoiler alert, Vue.js is awesome. In my case it dramatically improved performance. Load time went from over 30 seconds to less than 5 seconds.

CDN drop-ins won’t break your workflow

Vue.js has its own ecosystem which can be a little overwhelming if you aren’t primarily working in JavaScript. The good news is it’s really easy to get started by enqueuing Vue.js from a CDN. Learning all of the other modern JS tools can come later.

function my_theme_scripts() {
	wp_enqueue_script( 'vuejs', 'https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.js' );
}
add_action( 'wp_enqueue_scripts', 'my_theme_scripts' );

Keep your existing WordPress PHP template and output content as javascript.

If you do a search for “Vue.js and WordPress” you’ll find lots of advanced articles taking about the Rest API. While I think Rest API is pure awesome, it’s not the quickest way to start learning Vue.js. Instead I found it easiest to adapt my custom post type queries to output everything on the page as a JavaScript variable and then have Vue.js handle the presentation.

<?php

$arguments = [
	'post_type'      => 'captcore_website',
	'posts_per_page' => '-1',
	'order'          => 'asc',
	'orderby'        => 'title',
];

// Loads websites
$websites = get_posts( $arguments );

if ( $websites ) :
	foreach ( $websites as $website ) :

		// Previous HTML output went here

	endforeach;
endif;

The above is what you typically use when outputting a custom post type. I adapting this to manually to a JavaScript variable named sites as shown below.

<?php

$arguments = [
	'post_type'      => 'captcore_website',
	'posts_per_page' => '-1',
	'order'          => 'asc',
	'orderby'        => 'title',
];

// Loads websites
$websites = get_posts( $arguments );

if ( $websites ) : ?>
<script>

ajaxurl = "/wp-admin/admin-ajax.php";

var sites = [
<?php
foreach ( $websites as $website ) {
	$plugins = get_field( 'plugins', $website->ID );
	$themes  = get_field( 'themes', $website->ID );
	if ( $plugins && $themes ) { ?>{
		"id": <?php echo $website->ID; ?>,
		"name": "<?php echo get_the_title( $website->ID ); ?>",
		<?php	if ( $plugins ) { ?>"plugins": <?php echo $plugins; ?>,<?php } ?>
		<?php	if ( $themes ) { ?>"themes": <?php echo $themes; ?>,<?php } ?>
		"core": "<?php echo get_field( 'core', $website->ID ); ?>",
		"visible": true,
		"selected": false
		},
	<?php
	} // ends if ( $plugins && $themes )
} // ends foreach ( $websites as $website )
?>
];
</script>
<?php endif; ?>

That might be a little hard to read so here is a sample of what is outputted from my custom page template.

<script>

ajaxurl = "/wp-admin/admin-ajax.php";

var sites = [{
"id": 123,
"name": "anchorhost1.wpengine.com",
"plugins": [{"name":"advanced-custom-fields","title":"Advanced Custom Fields","status":"inactive","version":"4.4.12"},{"name":"acf-repeater","title":"Advanced Custom Fields: Repeater Field","status":"inactive","version":"1.1.1"},{"name":"advanced-custom-fields-pro","title":"Advanced Custom Fields PRO","status":"active","version":"5.6.10"},{"name":"akismet","title":"Akismet Anti-Spam","status":"active","version":"4.0.3"},{"name":"captaincore-client","title":"CaptainCore Client","status":"active","version":"1.3.0"},{"name":"gmail-smtp","title":"Gmail SMTP","status":"inactive","version":"1.1.7"},{"name":"gravityforms","title":"Gravity Forms","status":"active","version":"2.2.6"},{"name":"jetpack","title":"Jetpack by WordPress.com","status":"active","version":"6.0"},{"name":"mailgun","title":"Mailgun","status":"active","version":"1.5.8.5"},{"name":"worker","title":"ManageWP - Worker","status":"active","version":"4.5.0"},{"name":"wordpress-importer","title":"WordPress Importer","status":"active","version":"0.6.4"},{"name":"wp-cron-cleaner","title":"WP Cron Cleaner","status":"active","version":"1.0.0"},{"name":"wp-force-lowercase-urls","title":"WP Force Lowercase URLs","status":"inactive","version":"2.0.1"},{"name":"slt-force-strong-passwords","title":"","status":"must-use","version":"1.6.4"},{"name":"0-worker","title":"","status":"must-use","version":""},{"name":"stop-long-comments","title":"","status":"must-use","version":"0.0.4"},{"name":"mu-plugin","title":"","status":"must-use","version":"3.2.1"},{"name":"advanced-cache.php","title":"","status":"dropin","version":""}],
"themes": [{"name":"anchor-blank","title":"Anchor Blank","status":"active","version":"1.0"},{"name":"twentyfifteen","title":"Twenty Fifteen","status":"inactive","version":"1.9"},{"name":"twentyfourteen","title":"Twenty Fourteen","status":"inactive","version":"2.1"},{"name":"twentyseventeen","title":"Twenty Seventeen","status":"inactive","version":"1.5"},{"name":"twentysixteen","title":"Twenty Sixteen","status":"inactive","version":"1.4"},{"name":"twentythirteen","title":"Twenty Thirteen","status":"inactive","version":"2.3"}],
"core": "4.9.5",
"visible": true,
"selected": false
},{
"id": 124,
"name": "anchorhost2.wpengine.com",
"plugins": [{"name":"akismet","title":"Akismet Anti-Spam","status":"active","version":"4.0.3"},{"name":"captaincore-client","title":"CaptainCore Client","status":"active","version":"1.3.0"},{"name":"disable-emails","title":"Disable Emails","status":"active","version":"1.3.0"},{"name":"gravityforms","title":"Gravity Forms","status":"active","version":"2.2.6"},{"name":"jetpack","title":"Jetpack by WordPress.com","status":"active","version":"6.0"},{"name":"log-emails","title":"Log Emails","status":"active","version":"1.2.1"},{"name":"worker","title":"ManageWP - Worker","status":"active","version":"4.5.0"},{"name":"slt-force-strong-passwords","title":"","status":"must-use","version":"1.6.4"},{"name":"0-worker","title":"","status":"must-use","version":""},{"name":"stop-long-comments","title":"","status":"must-use","version":"0.0.4"},{"name":"mu-plugin","title":"","status":"must-use","version":"3.2.1"},{"name":"advanced-cache.php","title":"","status":"dropin","version":""}],
"themes": [{"name":"anchor-blank","title":"Anchor Blank","status":"active","version":"1.0"}],
"core": "4.9.5",
"visible": true,
"selected": false
}];
</script>

Any JavaScript data can be passed to Vue.js

The following will take pass the JavaScript variable sites into Vue.js instance.

<script>
new Vue({
	el: '#app',
	data: {
  		sites: sites,
	}
});
</script>

With that we are ready to let Vue.js do it’s magic. The HTML structure under the element #app will determine how Vue fills in the data. A simple loop with Vue.js might look like this.

<div id="app">
<v-app>
    Listing {{ sites.length }} sites
    <v-layout v-for="site in sites" :key="site.id" v-show="site.visible">
      <p><strong>{{ site.name }}</strong> <span>{{ site.plugins.length }} Plugins {{ site.themes.length }} Themes - WordPress {{ site.core }}</span></p>
    </v-layout>
</v-app>
</div>

My Vue.js project overview

This a really quick and dirty way to take an existing WordPress loop and begin working with Vue.js. Here is a quick overview how I’m using Vue.js to build an internal site manage tool.

Site manage WordPress template written in Vue.js
Site manage WordPress template written in Vue.js

Vue.js getting started resources