This Website

September 12, 2018

Categories: Code , Tags: Software, Vue, Vuepress, Markdown

This website is built using Vuepress (opens new window) and a little bit of Vue magic. The entire source (opens new window) for this site is on GitHub, and the config.js file has been set to build the site to the /docs folder. In GitHub this repository has been set to publish the site at /docs and has been given a custom domain name - mark.honeychurch.org.

GitHub Pages

This solution allows me to have both the source and build for the website in the one GitHub repo, which is clean and simple. All that's needed is for Vuepress to be installed globally, along with the markdown-it extensions I use (video, fontawesome, abbr, sup, sub and attrs), and then for the NODE_PATH environment variable to point to the global node_modules folder.

A development version of the site, with hot reloading, can be launched by running:

vuepress dev

To build the static files for the production website, all I need to run is:

vuepress build

Then I commit the code, push it to GitHub, and the new version of my site is live.

# Vue

Vue components can be created to display content. I've written custom components for listing posts, displaying a single post, etc, as well as writing components.

Components can be dropped into any markdown page by adding the component's HTML tag. For a component at .vuepress/components/code/rotx/decode.vue, for example, the HTML tag would be <code-rotx-decode />.

For a Vue component layout template that displays a single page, a <Content /> tag needs to be added where the markdown content of the page should be displayed.

# Metadata

# $page

For Vue components, there's metadata available for a single page in the $page object:

	
		{
	"title": "This Website",
	"frontmatter": {
		"title": "This Website",
		"slug": "website",
		"date": "2018-09-12T00:00:00.000Z",
		"layout": "Post",
		"categories": [
			"Code"
		],
		"tags": [
			"Software",
			"Vue",
			"Vuepress",
			"Markdown"
		],
		"readingShow": "top",
		"meta": [
			{
				"property": "article:published_time",
				"content": "2018-09-12T00:00:00.000Z"
			},
			{
				"property": "og:site_name",
				"content": "Mark Honeychurch"
			},
			{
				"property": "og:title",
				"content": "This Website"
			},
			{
				"property": "og:description",
				"content": "This website is built using Vuepress and a little bit of Vue magic. The entire source for this site is on GitHub, and the config.js file has been set to build the site to the /docs folder. In GitHub this repository has been set to publish the site at /docs and has been given a custom domain name - mark.honeychurch.org."
			},
			{
				"property": "og:type",
				"content": "article"
			},
			{
				"property": "og:url",
				"content": "/blog/code/website/"
			},
			{
				"property": "og:image",
				"content": "/media/images/blog/code/website.jpg"
			},
			{
				"name": "twitter:title",
				"content": "This Website"
			},
			{
				"name": "twitter:description",
				"content": "This website is built using Vuepress and a little bit of Vue magic. The entire source for this site is on GitHub, and the config.js file has been set to build the site to the /docs folder. In GitHub this repository has been set to publish the site at /docs and has been given a custom domain name - mark.honeychurch.org."
			},
			{
				"name": "twitter:url",
				"content": "/blog/code/website/"
			},
			{
				"name": "twitter:card",
				"content": "summary_large_image"
			},
			{
				"name": "twitter:image",
				"content": "/media/images/blog/code/website.jpg"
			},
			{
				"name": "twitter:label1",
				"content": "Written by"
			},
			{
				"name": "twitter:creator",
				"content": "@markhoney"
			},
			{
				"name": "twitter:label2",
				"content": "Filed under"
			},
			{
				"name": "twitter:data2",
				"content": "Software, Vue, Vuepress, Markdown"
			},
			{
				"property": "article:tag",
				"content": "Software"
			},
			{
				"property": "article:tag",
				"content": "Vue"
			},
			{
				"property": "article:tag",
				"content": "Vuepress"
			},
			{
				"property": "article:tag",
				"content": "Markdown"
			}
		],
		"datestring": "September 12, 2018"
	},
	"regularPath": "/blog/code/website/",
	"relativePath": "blog/code/website/index.md",
	"key": "v-52d06844",
	"path": "/blog/code/website/",
	"headers": [
		{
			"level": 2,
			"title": "Vue",
			"slug": "vue"
		},
		{
			"level": 2,
			"title": "Metadata",
			"slug": "metadata"
		},
		{
			"level": 3,
			"title": "$page",
			"slug": "page"
		},
		{
			"level": 3,
			"title": "$site",
			"slug": "site"
		},
		{
			"level": 3,
			"title": "window.location",
			"slug": "window-location"
		},
		{
			"level": 3,
			"title": "location.vue",
			"slug": "location-vue"
		},
		{
			"level": 3,
			"title": "Category Page",
			"slug": "category-page"
		}
	],
	"excerpt": "<p>This website is built using <a href=\"https://vuepress.vuejs.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Vuepress<OutboundLink/></a> and a little bit of Vue magic. The <a href=\"https://github.com/markhoney/mark.honeychurch.org\" target=\"_blank\" rel=\"noopener noreferrer\">entire source<OutboundLink/></a> for this site is on GitHub, and the config.js file has been set to build the site to the /docs folder. In GitHub this repository has been set to publish the site at /docs and has been given a custom domain name - mark.honeychurch.org.</p>\n",
	"readingTime": {
		"text": "3 min read",
		"minutes": 2.65,
		"time": 159000,
		"words": 530
	}
}
	

# $site

There's a $site object which holds metadata for the entire site. I've created a separate page for it, as it's pretty long:

$site

# window.location

We also have all the usual Javascript data available to us, such as window.location (but bear in mind the gotcha described below):

	
		null
	

This can come in handy for clever hacks like using window.location.search to offer a dynamic page that lists posts in a single category, for example. However, because the window object is only available in the browser, we have to make sure that the code is only run on the client side. This can be done by creating an empty reactive data object, and then populating it on mount with the frontend data.

# location.vue

Here's the component I used above to get the window.location object:

<template>
	<pre>
		<code class="language-json">
			{{JSON.stringify(location, null, '\t')}}
		</code>
	</pre>
</template>

<script>
	export default {
		data() {
			return {
				location: null
			};
		},
		mounted() {
			this.location = window.location;
		}
	};
</script>

# Category Page

For example, to list a single category of posts on this site, I have a reactive data object called "category" which is initialised as null. Then, on mounted(), I get the URL query from window.location.search to find out what category I need to show, and dynamically compute a list of pages that match that category:

<template>
	<span v-if="categories">
		<router-link v-for="(category, index) in categories" :key="index" :to="'/blog/category/?name=' + category">{{category}}<span v-if="index + 1 < categories.length">, </span></router-link>
	</span>
</template>

<script>
	export default {
		props: ["categories"]
	};
</script>