Yes, really, I used Disqus for this blog from the start but never liked it much. Increased page load time, so many requests to unfamiliar and strange URLs - nothing good for me and the readers, I think.

Now I had a few spare time to implement comments with Github API. I had decided to touch with Vue.js for presentation and learn the way to implement reusable components with it.

Table of contents

Designing comments view component

Let's look at the typical comments view component. It will list all the comments for some page. Each comment has an author, comment text and publishing date.

First of all, we can identify "comment view" component and use it to render individual comments in list:

We can try to divide this component more:

"avatar" component, we can reuse it in comment posting form in future

"relative date" component (22 days ago on image above)

Relative date component

We'd like to pass some date to the component and it should render the string representation of this date relative to current date. Let's require to render date as relative if difference is less than month, and as ISO date otherwise. We'll store passed date in ISO format and in human-readable format in span's attribute just like GitHub does:

*.vue files are single file components, they consists of three sections: template, script and style. Each section is optional. You can specify language of each section with lang attribute. The defaults is lang="html" for template, lang="javascript" for script and lang="css" for style. If you use vue-loader and correctly configure your webpack build script, you'll be able, for example, to override defaults to lang="jade" for template, lang="typescript" for script section, lang="less" for style. Select what suits your needs, it's fully customizable!

The component receives single property date. We declare that property inside props: it should be Date, has default value and the value should be required.

Then we declare computed property relativeDate which is javascript function for calculation date relativeness to string.

Both date property and the relativeDate propery are used in template. Vue.js is smart enough to track changes in date property to update reactiveDate property value properly.

There is no any required properties, so we'll left them without any declaration. We'll just use these properties in template and we need some styling to position avatar in the left. Nothing complicated here.

Github comment view component

Let's compose the avatar view and the relative date components to visualize single comment in GithubCommentView.vue component. I'll use bootstrap list-group-item styles here:

Unfortunately, template section expect the only one child node inside. Therefore we need to wrap multiple nodes in the root <div>.

You can easily insert HTML parts with v-html directive. Note that you should know what you doing - rendering data from user as HTML is potential security risk that allows for XSS attacks. I'll receive HTML data for this component from trusted GitHub API, therefore it's allowed.

This component (like RelativeTime and AvatarView) is "pure" - it just receives some properties and render HTML markup with them. The flow of properties directed from parent component to child.

GitHub comments list view component

We need to request issue comments using Github Issues Comments API. The HTTP request is simple: GET /repos/:owner/:repo/issues/:number/comments, no authentication and authorization needed.

Despite we can use AJAX requests to fetch the data, I decided to find ready convenient JavaScript client library. There is official list of client libraries for many languages, several wrappers written in JavaScript. I've selected octokat.js because it supports promises out of the box. It is small and can easily run in browser. I definitely recommend it to use!

The requirements for GitHub comments list component is:

show spinner icon when there is pending GitHub API request runs

load first portion of comments when component is created

"show more comments" button should load next portions of comments if possible

This component is not pure. It's aggregation root for comments list and holds state: current loaded list of comments.

I've used created lifecycle function to load first portion of comments. It is necessary to configure octokat.js to accept application/vnd.github.v3.html+json MIME type so we can receive plain HTML instead of markdown for comments body.

The addComments method pushes loaded comments to array and resets showLoader flag. If you can load more comments, nextComments will be filled and "Show more comments" button will be shown.

In the template, GitHub comments rendered from array using v-for directive. It worth to mention key attribute which exists to help Vue.js to reuse existing elements if underlying array has been changed.

Why I pass individual properties to github-comment-view not the GitHub's comment? Because of open-closed principle. github-comment-view has well-defined interface and closed for possible changes in Octokat or GitHub API.