I recently started making an app to help me out in PC Building Simulator. I’m not the first one to make something like it (my favorite tool has been this calculator by PUC_Snakeman on Steam) but sometimes you just have to make things yourself, you know?
Anyway, this gave me the opportunity to practice a few things. Here are a couple goals I wanted to achieve right off the bat:
- Have the app auto-deploy to Github Pages
- Make a plugin to manage analytics and update the page title (also don’t do any analytics in development because that’s silly)
This post will be about how I got those two things set up in my application, in the guise of a walkthrough. I’m sneaky like that.
Oh and all of this will be based on the app I’m working on, which you can find on my Github repo here.
Getting started
So first things first, you’ll need to get a Vue project set up along with git. I’ll just assume you have that taken care. If not and you’re not sure where to begin you should check out Vue CLI; they’ll help you out with that part far better than I.
After that, get your repository on Github and you’re ready to go!
Adding a Github action for deployment and setting up gh-pages
Setting up the deployment script
First off, if you haven’t checked out Github actions you definitely should. Project workflows, Continuous Integration, Continuous Deployment, all that stuff is really awesome and I’m just starting to really dive in to it all. It makes life so much easier.
For my use case I only needed to add in one file to my project.
name: Deploy to GH Pages
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.1
with:
persist-credentials: false
- name: Install and Build
run: |
npm install
npm run build
- name: Deploy
uses: JamesIves/github-pages-deploy-action@3.7.1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH: gh-pages
FOLDER: dist
CLEAN: true
If you’re using Vue CLI like I am, the actual build process is very simple and amounts to running the build
command through npm
. Using Github Actions means that I can make use of some built steps other people have made, which I have done via the uses:
config option.
Once you have pushed this file to your repo, any time a change is made on the main
branch (if you want any other branch you can change the top part accordingly) it’ll run the action and update the gh-pages
branch. If said branch doesn’t exist yet, no problem, it’ll make it.
Setting up gh-pages
Once the action has run and you now have a gh-pages branch you can get it set up in Github. You can go to the settings tab in your Github repo (https://github.com/your-user-name/your-repo/settings) and scroll down to the Github Pages section. Here, just turn it on, select gh-pages
as your branch and root
as the folder and you’re all set!
Well, not quite. If you build the app all links will be broken because the app builds with the assumption you are on the root URL. Meaning that a gh-pages link will look something like https://alyxmoon.github.io/lunar-pc-build-sim-companion/
but the app will assume the base url is https://alyxmoon.github.io/
which consequently messes everything up. Fortunately the fix is simple.
If you don’t have one, create a vue-config.js
file in the root of your repo. To it, add this code:
module.exports = {
publicPath: process.env.NODE_ENV === 'production'
? '/your-repo-name-here/'
: '/',
}
With that, all linked will be generated correctly when the bundles are built.
Setting up a property in Google Analytics and adding the script
If you haven’t worked with Google Analytics before I can’t really offer a great guide. Instead, check out what Google has to offer, such as this getting started guide. Once you have it set up, you can either copy the provided ‘Global Site Tag’ code or get the measurement-id and use that in the following code snippets.
If you set up a project with Vue CLI your index.html should have a head area that looks like this:
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
<script
async
src="https://www.googletagmanager.com/gtag/js?id=your-code-here">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'your-code-here');
</script>
</head>
Getting it all working in the Vue app
There’s a problem though with simply putting the script tags in like that: this is a Single Page App. Which normally is awesome but it does play weird with SEO and analytics. Fortunately there are ways around this.
The issue is simple: we technically only have one page. So no matter what ‘page’ we go to afterwards the analytics only ever registers the page however it is in the initial load. If you were to change the page and consequently change the page title, it wouldn’t consider that a different page and you would never have that tracked. That’s no good.
The fix is also simple: whenever we change the current route we need to send a notification that it’s a new page being loaded. There exist solutions for this already but I decided to make one myself as it was fairly simple.
If you want to do some reading on the subject, what I did was make a plugin for Vue. To get started, I created a file src/plugins/pageTracker.js
class PageTracker {
install (app, {
gtag,
useComponentNameAsFallback = true,
titlePrefix = '',
changePageTitle = true,
} = {}) {
if (!gtag || !app.config.globalProperties.$router) return
this.gtag = gtag
this.setUpRouteWatcher(
app.config.globalProperties.$router,
{ changePageTitle, titlePrefix, useComponentNameAsFallback },
)
app.provide('gtag', gtag)
}
setUpRouteWatcher (router, {
changePageTitle,
titlePrefix,
useComponentNameAsFallback,
}) {
router.afterEach(to => {
const pageName = titlePrefix + (
to.meta.title ||
to[useComponentNameAsFallback ? 'name' : 'path']
)
if (changePageTitle) {
document.title = pageName
}
this.gtag('set', 'page', pageName)
this.gtag('send', 'pageview')
})
}
}
export default PageTracker
The gist of that code:
- Check that I remembered to include the ‘gtag’ function and whether the router is loaded. If either of those aren’t true skip because this plugin won’t work otherwise.
- Add a watcher so that every time the router changes, two things happen:
- If desired, update the title that shows up in the tab.
- Send a pageview event to gtab, which will update the page based on the title of the tab
- Add the
gtag
variable as an instance object, which means that every single component in the app can access it viathis.$gtag
. That’s just in case I want to have any dynamic page titles after content loads. I probably won’t but I like to add a little extra for the fun of it.
Next up, to actually use this, we need to add a little to the src/main.js
file. After changes, it now looks something like this:
import { createApp } from 'vue'
import pageTracker from './plugins/Gtag'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App)
.use(store)
.use(router)
.use(pageTracker, {
gtag: window.gtag,
prefix: 'Lunar\'s PCBS Companion | ',
})
.mount('#app')
With the config I passed to my pageTracker plugin, every time a new route is loaded, the page title will change to Lunar's PCBS Companion | whatever-component-name-is
.
Ensuring it’s only loaded in production
If you’ve got the project set up through Vue CLI then you already have the html-webpack-plugin which builds the bundled HTML files and allows for interpolation in your html files. Using this, we can set up the Gtag script code to only be included conditionally.
A simple check would be if the mode was ‘production’ but I wanted to go a step further. After all, somebody could simply download the built files and run things locally. Having the gtag script be loaded for them just feels tacky. To that end, I added in a check for an environment variable VUE_APP_ENABLE_ANALYTICS
would I would set in the github actions deployment workflow.
As a quick note, you can use environment variables when working with Vue apps but they have to be prefixed with VUE_APP
or they will not be included in the bundle. You can ready more about it in the docs.
I added two variables:
VUE_APP_ENABLE_ANALYTICS:
This I have set to ‘true’ when I want to load the scriptVUE_APP_ANALYTICS_CODE
: This is the gtag id code
I could have gotten away with only using one which would contain the analytics id code. If I did that then I could just check for the existence of said env variable. But I like to go a little extra so one acts as a boolean flag and the other is the analytics id code.
Here’s how those variables are used in the index.html file
<% if (process.env.VUE_APP_ENABLE_ANALYTICS === 'true') { %>
<script
async
src="https://www.googletagmanager.com/gtag/js?id=<%= process.env.VUE_APP_ANALYTICS_CODE %>">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '<%= process.env.VUE_APP_ANALYTICS_CODE %>');
</script>
<% } %>
Now the relevant gtag scripts have been wrapped around a conditional block. If VUE_APP_ENABLE_ANALYTICS
is not true then the script won’t even exist. Nice.
Now just to set those. I updated the ‘Install and Build’ step of the Github action workflow like so:
- name: Install and Build
run: |
npm install
npm run build
env:
VUE_APP_ENABLE_ANALYTICS: true
VUE_APP_ANALYTICS_CODE: G-WQ2GPKCBVL
With this, by default, only the deployment to the live site will have the gtag script loaded. Which is just the way want it.