09.04.2020

AEM with VUE.JS – Part 1

Author: Tom Peeters

As an Adobe Experience Manager specialised partner and passionate full-stack development team, we are always on the lookout to improve our productivity when building websites with a rich visitor experience on the AEM platform.

Introduction

Deciding on which front-end framework to use can be a complex business for any project. Selecting one that plays well with AEM does not make that decision any easier. Integrating that framework into your day-to-day AEM workflow while retaining a smooth front-end development experience can be equally challenging.

In the past, we have already covered how you can integrate Angular in your favourite CMS, it is high time we talk about some other contenders. Today we are taking a look at how we use VueJs in our AEM projects.

VueJs is a progressive framework for building user interfaces. It is very lightweight and un-intrusive allowing developers to get started with it quickly and grow their skills gradually. It is an excellent fit for AEM and as I will show you, it is easy to integrate and developing components with it is very approachable.

Micro apps

The model we will demonstrate here builds on the Multi-Page Application approach that is the default when integrating AEM. For many websites, this model still has many benefits over the Single-Page Application approach, mostly in terms of content authoring flexibility, out-of-the-box SEO and reduced complexity of the solution in general.

It is a great fit if you want to render most content server-side but want to augment some UI elements with more dynamic front-end behaviour.

Our approach is to use a solid front-end framework to build UI elements that can be used in HTL templates to compose AEM components that provide a rich user experience. These elements can be as simple as a fancy dropdown or as complex as a micro-application that interacts with REST APIs.

Get started

A Git repository is available with the code covered in this article so you can clone this and experiment:

git clone https://github.com/designisdead/aem-vue-demo.git

There is a tag for each step so you can follow along.
To get started, an AEM project was generated using Adobe archetype 23 for AEM 6.5.

GIT TAG: 0-get-started

Front-end build

When developing AEM components, we like to keep related code together as much as possible. This makes it very easy to navigate the code and results in components with high cohesion.

This means we put our VueJs code under the same folder as our HTL templates. And to cater for this in the build process, we will augment the ui.apps module to include a modern front-end development workflow.

For the remainder of this article, we will work from the ui.apps folder. Unless mentioned otherwise, always run terminal commands from this folder.

I initialized a new NPM module inside ui.apps and installed Webpack which will be used to compile the code and package this into AEM clientlibs.

npm init 
npm i --save-dev webpack webpack-cli 

We can start off with a very simple Webpack config:

// webpack.config.js

const CLIENTLIB_ROOT = './src/main/content/jcr_root/apps/aem-vue-demo/clientlibs'
const CLIENTLIB_NAME = 'clientlib-site'

function createConfig(clientlibRoot, clientlib) {
     return {
         entry: {
             bundle: `./${clientlibRoot}/${clientlib}/webpack-entry.js`
         }, 
         output: {
             path: `${__dirname}/${clientlibRoot}/${clientlib}/dist`,
             filename: './[name].js'
         },
     }; 
}

module.exports = createConfig(CLIENTLIB_ROOT, CLIENTLIB_NAME);

And add a build script for it to package.json:

"build": "webpack --mode production"

We also need to create the webpack-entry.js file that is referred to by the Webpack config:

 touch ./src/main/content/jcr_root/apps/aem-vue-demo/clientlibs/clientlib-site/webpack- entry.js

We will get back to this file later.
At this point we can run webpack which will create a bundle.js file in clientlib-site:

npm run build
Add Typescript

As full-stack developers, we are used to using strongly typed languages. It allows us to catch errors early on in the development process instead of having to face obscure bugs at runtime. Another big advantage is that it helps our IDE to provide us with extra hints and code completion.

Typescript allows us to get all these benefits in our front-end code.

Typescript is not a requirement to use VueJs. You can opt-out and use vanilla ES6, but you will need to setup transpilation anyway to make this work on browsers that do not support it. Because Typescript is a superset of ES6, you might as well use it to do the transpilation. The types are completely optional and you can gradually start using them as your code gets complex… or not.

Let’s add typescript to our front-end build:

 npm i --save-dev typescript ts-node @types/node @types/webpack

And add a simple configuration for it:

// tsconfig.json
{
   "compilerOptions": {
     "target": "es5",
     "module": "commonjs",
     "moduleResolution": "node",
     "strict": true,
     "esModuleInterop": true,
     "allowSyntheticDefaultImports": true,
     "forceConsistentCasingInFileNames": true,
     "noImplicitReturns": true,
     "sourceMap": true
   } 
}

We can write the Webpack config in Typescript as well, simply by renaming it and adding the type definitions:

// webpack.config.ts


import * as webpack from 'webpack'


const CLIENTLIB_ROOT = './src/main/content/jcr_root/apps/aem-vue-demo/clientlibs'
const CLIENTLIB_NAME = 'clientlib-site'


function createConfig(clientlibRoot: string, clientlib: string): webpack.Configuration
{
     return {
         entry: {
             bundle: `./${clientlibRoot}/${clientlib}/webpack-entry.js`
         },
         output: {
             path: `${__dirname}/${clientlibRoot}/${clientlib}/dist`,
             filename: './[name].js'
         }, 
     };
}

module.exports = createConfig(CLIENTLIB_ROOT, CLIENTLIB_NAME);

GIT TAG: 2-add-typescript

Add VueJs to the build

To compile VueJs code and deliver it to the browser, we need to add some more libs:

 npm i --save vue
npm i --save-dev ts-loader vue-loader vue-template-compiler

I like to code VueJs components as class-based components with decorators so I also add some libs to facilitate this:

npm i --save-dev vue-property-decorator

Now we also need to update the Webpack config so that it processes the VueJs code.

I have added ts-loader to support .ts files containing regular Typescript code and vue-loader to support single-file components in .vue files. I’ve also added a resolve section so we can write import statements without having to specify the file extension:

// webpack.config.ts

import { VueLoaderPlugin } from 'vue-loader'

...

module: {
     rules: [
         {
             test: /\.vue$/,
             loader: 'vue-loader'
         },
         {
             test: /\.tsx?$/,
             loader: 'ts-loader',
             exclude: /node_modules/,
             options: {
                 appendTsSuffixTo: [/\.vue$/],
             }
         }, 
     ]
}, 
resolve: {
     alias: {
         vue$: 'vue/dist/vue.esm.js'
     },
     extensions: ['.ts', '.tsx', '.vue', '.json']
},
plugins: [
     new VueLoaderPlugin()

GIT TAG: 3-add-vuejs

Load VueJs on the page

Now we can compile VueJs code and package it in the clientlib. We will also need to bootstrap VueJs on our pages.

To do this, I have adapted the body.html file of the AEM page component to add a top-level div with an attribute id=”app” so we can use VueJs components in the entire page:

<!--/* src/main/content/jcr_root/apps/aem-vue-demo/components/page/body.html */-->

<div id="app">
     <p>{{message}}</p>
     <sly data-sly-
use.templatedContainer="com.day.cq.wcm.foundation.TemplatedContainer"
          data-sly-repeat.child="${templatedContainer.structureResources}"
          data-sly-resource="${child.path @ resourceType=child.resourceType,
decorationTagName='div'}"></sly>
     <sly data-sly-resource="${'cookiedisclaimer' @
resourceType='/apps/voo/components/hiddens/cookiedisclaimer',
decorationTagName='div'}"> </sly>
</div>

And I also added a bootstrap.ts file that loads VueJs onto this DOM element:

// src/main/content/jcr_root/apps/aem-vue-demo/components/page/vue/bootstrap.ts

import Vue from 'vue'

new Vue({
     el: '#app',
     data: {
         message: 'Hello Vue!'
     } 
});

I have also included a small test to check that everything works by adding the {{message}} placeholder which, if everything is set up correctly, will be replaced by the “Hello Vue!” message that is defined as part of the VueJs bootstrap data.

Now we need to tell Webpack which folders it should scan to pick up the VueJs code of our components. We use the webpack-entry.js file to accomplish this:

 const components = require.context('../../components', true, /\.scss$|\.tsx?$/);
components.keys().forEach(components);

This indicates to Webpack that it needs to recursively scan the path under components to find .scss and .ts(x) files and include these in the package.

Before we can deploy this to AEM, there is one thing left to do and that is to include the output of our Webpack build in the clientlibs-site clientlib:

 # src/main/content/jcr_root/apps/aem-vue-demo/clientlibs/clientlib-site/js.txt
#base=dist
bundle.js

Now we are ready to build our clientside code and run the project’s Maven build with the autoInstallBundle and autoInstallPackage profiles enabled to deploy this to a local AEM instance:

 npm run build
cd ..
mvn -PautoInstallBundle -PautoInstallPackage -Padobe-public install

After deployment is completed, we can open the default page and confirm VueJs is loaded correctly:

GIT TAG: 4-load-vuejs

Stay tuned for part 2 of this blog in which we will show you how to build a custom AEM VueJs component!

gallery image