February 11, 2019
Usually, when you think of Vue.js, you think of a dynamic, rich internet application. This article shows how Vue.js can be used in a static website to maintain commons elements that appear throughout a set of pages. This is not a single-page application. Rather, the demo is a collection of .html pages that share an app instance definition to allow the maintenance of a header and a footer at a single point.
This screenshot shows Page A in a file. This is an HTML file, pageA.html. Without Vue.js, there would be a header, followed by content, followed by a footer. The colored header and footer bands sppear throughout the site.
There is an HTML file, pageB.html, with a similar structure. The content in pageA.html differs from that in pageB.html, but there common elements like the header and the footer which are duplicated. This presents a maintenance problem as additional pages (pageC.html, pageD.html) will require editing multiple documents to affect the same change. The screenshot from Page B is below.
To view the pages for yourself, visit http://www.bekwam.org/slot-demo-project/pageA.html.
In the Vue.js version, the common header and footer are factored out and put into Vue.js. Changing these elements -- things like the brand logo and copyright notice -- site-wide now involves a single edit to a single file (main.js). Moreover, the header can slightly vary between pages. This is parameterized using a <slot>.
The following pair of component definitions and Vue app instance are in a single main.js file.
The HeaderComponent contains a shared top-level page header, a shared navigation, and a page-specific sub-header. The page-specific sub-header is parameterized with a Vue <slot> tag. This will swap in the contents of the <header-component> tag in the individual pages (pageA.html and pageB.html). For help during debugging, you might specify a default value within the slot tag. However, I feel that this is more fault tolerant because if it is omitted, the <h2;> will be empty.
const HeaderComponent = {
template : `
<header>
<h1>My Site Brand</h1>
<nav>
<ul>
<li><a href="pageA.html">Page A</a></li>
<li><a href="pageB.html">Page B</a></li>
</ul>
</nav>
<h2><slot></slot></h2>
</header>`
}
The FooterComponent is will be exactly the same for each page. There's no need for the slot tag.
const FooterComponent = {
template: `
<div id="ftr">
<hr>
<footer>
<p>© 2019 My Site Brand, Inc</p>
<p>Questions?
Contact the <a href="mailto:webmaster@example.com">
webmaster@example.com</a>.
</p>
</footer>
</div>
`
}
The Vue app instance registers a pair of component, HeaderComponent and FooterComponent. These are aliased to kebab-case to keep the names readable in the W3C case insensitivity guidelines.
const app = new Vue({
el: '#app',
components : {
'header-component': HeaderComponent,
'footer-component': FooterComponent
}
})
pageA.html builds out the document in the "app" div which is bound to the Vue app instance. It uses the header-component and footer-component tags. header-component has a title that will be used in the HeaderComponent definition. Notice that the <head> title and keywords are also fixed in this page.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="keywords" content="pagea">
<title>Slot Demo - Page A</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div id="app">
<header-component>Page A</header-component>
<p>AAAA AAAA AAAA AAAA</p>
<p>My Content for Page A</p>
<footer-component></footer-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="main.js"></script>
</body>
</html>
Page B has an identical structure but has its own values in the head content in the body.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="keywords" content="pageb">
<title>Slot Demo - Page B</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div id="app">
<header-component>Page B</header-component>
<h2>Page B</h2>
<p>BBBB BBBB BBBB BBBB</p>
<p>My Content for Page B</p>
<footer-component></footer-component>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="main.js"></script>
</body>
</html>
There are some minor CSS styles used in the presentation to highlight the shared elements.
header {
background-color: lightcoral
}
#ftr {
background-color: lightblue
}
When you have a large web site, you should consider a Content Management System (CMS). The CMS provides a streamlined workflow for managing content. However, a static website is easy to set up and will be more secure because there's no back-end to compromise. Additionally the static site is very scalable with services like CloudFlare able to replicate the static content around the globe. If you find yourself maintaining a static site, the ideas in this article may help you deal with shared items so that maintenance can be kept to a minimum by cutting down on duplication.
By Carl Walker
President and Principal Consultant of Bekwam, Inc