Vue Routing with Jekyll

Apr 5, 2017
Tags: Web Development Vue Jekyll

I’ve been continuing to play around with an isomorphic-ish approach to building sites with Jekyll and Vue—this week is routing. After we’ve pulled down the first page from the server, and begun relying on Vue, we still want to be able to move through the site while updating the URL, preserving the window’s history, and keeping things (components, sections, pages, etc.) organized in a way that makes sense—without resetting the whole DOM, obviously.

Luckily, the built in router for V2 let’s us do all this stuff. Of course, we don’t have to use the official router, we could drop in our own, or use one off the shelf—but theirs seems pretty great honestly.

Dynamic Matching & the History API

Once we’ve imported the module import VueRouter from 'vue-router' and told Vue to use it Vue.use(VueRouter), defining the routes is straightforward:

const routes = [
  { path: '/about', component: Page },
  { path: '/ship/:name', component: Ship }
]

We can define the routes manually, or use dynamic matching as you see above. We get back the matched value at $route.params.name so we can use the variable to do things like conditionally loading a block. This is how we’ll navigate around the site, after the first page load—instead of asking the server for a new page, we’ll either swap in a new component or grab the paramater from the route to update an existing one.

If we set mode: 'history' on the router object, we can use the history.pushState() method introduced with HTML5 to preserve our browsing history and reflect the changes we’re making to the DOM, in the URL.

const router = new VueRouter({
  mode: 'history',
  routes 
})

So, looking at a ship entry for Sanctioned Parts List the URL will read /ship/santioned-parts-list. Because we’re doing this in an isomorphic-ish way, we don’t have to worry about direct requests to that URL getting 404’d—a static version of the page already exists on the server thanks to Jekyll.

Programatic Navigation

We can use <router-link to='/about'>About</router-link> to explicitly link to a route—or we can use the router’s instance methods to navigate programatically router.push('/ship/little-gravitas'). If, for instance we wanted to generate a random link to a page, we could abstract that logic out into a separate method and then call it with an v-on:click directive <button @click="getShip">Generate Ship</button>.

Moving on from Here

Next up, is to figure out how to build templates for both Jekyll and Vue that leverage as much of the same code as possible. As a single source of data drives both views, the templates should also share as much as possible.


Vue Filters

Mar 16, 2017
Tags: Web Development Vue

In Vue, we can call filters on text just like we would when templating in Liquid with Jekyll. For instance, if we wanted to transform some Markdown into HTML we could do something like this:


...
message: "**The boy stood on the burning deck**, whence all but he had fled; The flame that lit the battle's wreck _Shone round him o'er the dead_."
...
{{ message | markdownify }}
...

Giving us:

<p><strong>The boy stood on the burning deck</strong>, whence all but he had fled; The flame that lit the battle&#39;s wreck <em>Shone round him o&#39;er the dead</em>.</p>

However, in Vue 2.0 pre-set filters were removed, and the use of filters was restricted to text interpolation and v-bind directives. Adding them back in is pretty painless though. We just include a filters: object and define our function:

import marked from 'marked'

var app = new Vue({
  el: '#app',
  data: {
    message: "**The boy stood on the burning deck**, whence all but he had fled; The flame that lit the battle's wreck _Shone round him o'er the dead_."
  },
  filters: {
     markdownify: function (val) {
          return marked(val);
     }
  }
})

In this case, we’re bringing in Marked and letting it do the heavy lifting. The only problem here is that our mustache tags are going to embed what was returned by the filter as plain text on the page—when what we want, is to embed the HTML. To embed HTML we need to use the v-html directive. It would be great if we could do something like <p v-html="message | markdownify"></p> but we can only call filters in the mustache and v-bind syntax. So, there’s a couple things here we could do. We could turn our filter into a method and then call it from within the v-html directive:

import marked from 'marked'

var app = new Vue({
  el: '#app',
  data: {
    message: "**The boy stood on the burning deck**, whence all but he had fled; The flame that lit the battle's wreck _Shone round him o'er the dead_."
  },
  methods: {
     markdownify: function (val) {
          return marked(val);
     }
  }
})
<div id="app">
<p v-html="markdownify(message)"></p>
</div>

Or, if we want to take advantage of caching—we could store the processed string in a computed property. This way we can process it once, and reuse it whenever we want, without having to call marked each time:

var app = new Vue({
  el: '#app',
  data: {
    message: "**The boy stood on the burning deck**, whence all but he had fled; The flame that lit the battle's wreck _Shone round him o'er the dead_."
  },
  methods: {
     markdownify: function (val) {
          return marked(val);
     }
  },
  computed: {
    rawmessage: function(){
      return marked(this.message)
    }
  }
})
<div id="app">
{{ rawmessage }}
</div>

Still lot’s to learn about Vue, and I wouldn’t be surprised if in a month or two I come to realize that I’m doing it all wrong here—but for now, this seems to work. If I’m going to be rendering Markdown via JSON via Jekyll, I imagine I’ll be using a mix of these approaches. For body text where I don’t have to worry about caching for the next page, I would probably opt for the method option—for headers and navigation, maybe computed properties would make more sense. Or not. I won’t be surprised if I come back in a month and completely refactor all this stuff, but I guess that’s just how I learn.


Vue and Jekyll

Mar 15, 2017
Tags: Jekyll Web Development Vue

I’ve been using Vue a bit lately, and I love it. If you’re not familiar with it, you should really check it out. In a nut: it’s a client-side JS framework, much like Ember, Angular, React, etc. It borrows conventions from all those other frameworks and ends up with—what I think anyways—is the simplest, most elegant, client-side framework around. Have a look at the docs and see for yourself.

Now, I’m also a big Jekyll fan, and love what static sites offer—so I thought why not combine them for the best of both worlds?

Isomorphic blah blah blah

I know, bear with me. Let’s say a user requests a URL from our site and we ship them the pre-rendered static page that’s sitting on our CDN. Great, we’ve used our blinged out CDN with HTTP2/Server Push, extreme caching, asset compression, double cup holders, and cloud bleed to get to first paint in record time—we also sent down a small bit of JSON while we we’re at it. Now, if JS is supported, what if the next request didn’t have to go to the server at all? What if instead, we used Vue—and a small bit of JSON to build the next page locally, in the browser? Spoiler: It would be wicked fast.

Isomorphic Demo

The trick then, is to be able to build the same page, with the same data on both the server and the client—an isomorphic view if you will. I’m probably a bit late to the game on all this isomorphic stuff, but better late than never.

I’ve been playing around with this pattern a bit and put together this bare-bones template for experiements. Have a look, and check out the demo—turn off Javascript to get the statically rendered page, re-enable JS to see the Vue view. The project relies on Webpack, and NPM to pull everything together.

I’ll be posting more about Vue as I dig in and learn more about it. So far I think it’s pretty fabulous. Single file components neatly separate template code from component methods and data, no JSX here. Angular-like directives make doing things like <p v-for="post in posts"></p> dead simple. That same kind of simplicty goes for event handling <button @click="submit.prevent">Submit</button>, conditional rendering <div v-if="logged-in">...</div>, attribute and class binding, and more. Check it out, it’s pretty great.



The Where Expression Filter in Jekyll 3.2

Oct 1, 2016
Tags: Jekyll Web Development

If, like me, you’ve been distracted by all the changes to gem based themes in the last two releases of Jekyll you may have missed the addition of this humble new filter: where_exp.

Spoiler: It’s awesome

Now the where filter has been around for a while—it lets you pre-filter an array before iterating over its contents, for instance:


{% assign fascists = site.fascists | where:”era”,21st Century” %}
{% for fascist in fascists %}
    {{ fascist.name}}, {{ fascist.country }}<br>
{% endfor %}


=> Donald Trump, United States
   Vladimir Putin, Russia
   Nikolaos Michaloliakos, Greece

That’s great, but what if you want to filter for a single value in an array or hash. Let’s say each document in our fascist collection has a properties field that enumerates their fascist ideology. With the where_exp filter then, we could do something like this:


{% assign fascists = site.fascists | where_exp: "fascist", "fascist.properties contains 'Contempt for the Weak'" %}

{% for fascist in fascists %}
    {{ fascist.name }}<br>
{% endfor %}

=> Donald Trump
   Adolf Hitler

This makes things so much easier than it used to be. Rather than having to iterate over each item in the collection to check for the right fascist ideology—this is also a pain when having to deal with empty sets, setting headers, etc.—we can simply use the filter. Note: both the where and the where_exp filters are unique to Jekyll and are not actually part of Liquid.

Maybe it’s just me, but the pace of change in Jekyll seems to have picked up quite a bit. As more and more people move to decoupled systems with a static component it’s good to see Jekyll modernize to meet the new demand.

The Distress Signal is the personal blog of Bryan Schuetz. Bryan has been complaining on the Internet since the 90s. If you'd like to get in touch with Bryan, you can find him on twitter.