AEM and VueJS integration part 2

April 19, 2018    aem vue javascript integration tutorial


Previously …

In the previous blog post I have explained a simple (dummy) way of how we can add Vue to an Adobe Experience based project. Now it is time to add a bit more complexity.

Step 1 - change CDN to NPM

  • Let’s get rid of our hardcoded inclusion of Vue js from CDN.
  • I have prepared a package.json file that will be needed
{
  "name": "aem-ktln",
  "version": "1.0.0",
  "description": "",
  "main": "script.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "vue": "^2.5.16"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.4",
    "babel-preset-env": "^1.6.1",
    "webpack": "^4.5.0",
    "webpack-cli": "^2.0.14"
  }
}

We will have only one dependency here and it is "vue": "^2.5.16". Additionally, there will be a bunch of devDependencies:

  • Webpack - builds our js file
  • Babel - compiles our js to the desired version

Below is the content of the webpack.config.js file

const path = require('path');

module.exports = {
    mode: 'development',
    entry: {
        main: ['./src/main/content/jcr_root/etc/designs/aem-ktln/clientlib-site/js/script.js'] // initial script, entry point, starts out app
    },
    output: {
        filename: '[name].js',
        path: path.resolve(__dirname, './target/classes/etc/designs/aem-ktln/clientlib-site/js') // destination path
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['es2015']
                    }
                }
            }
        ]
    },
    resolve: {
        alias: {
            'vue': 'vue/dist/vue.js' // to use vue with compiler
        }
    },
    devtool: 'inline-source-map'

};

Quite good at this point, but we need to integrate the Webpack build into our Maven build.

Step 2 - updated pom.xml

We need to add one more plug-in to run NPM and Webpack commands

  <build>
      <plugins>
            <plugin>
                <groupId>com.github.eirslett</groupId>
                <artifactId>frontend-maven-plugin</artifactId>
                <version>1.6</version>
                <executions>
                    <execution>
                        <id>install node and npm</id>
                        <goals>
                            <goal>install-node-and-npm</goal>
                        </goals>
                        <configuration>
                            <nodeVersion>v8.11.1</nodeVersion>
                            <npmVersion>5.8.0</npmVersion>
                        </configuration>
                    </execution>
                    <execution>
                        <id>npm install</id> 
                        <goals>
                            <goal>npm</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>webpack build</id>
                        <goals>
                            <goal>webpack</goal>
                        </goals>
                    </execution>

                </executions>
            </plugin>
       </plugins>
   </build>
  1. Install all required modules
  2. Execute Webpack build command to build our final js file

We are done with the app building (boring) part and ready to modify our application code.

Step 3 - Entry point

We have introduced a js entry point in the Step 1 script.js

Now we are ready to look inside.

import Vue from 'vue'
import components from './components.js' //1

function startApp() {

    components.forEach(func => func()); //2

    const elements = document.querySelectorAll('[data-component]'); //3
    ([]).forEach.call(elements, (el) => {

        el.dataset.initialized = 'true';

        new Vue({el: el}); //4
    });
}

document.addEventListener("DOMContentLoaded", startApp); //5
  1. Components -> array of functions (each function registers a component on its execution)
  2. Executing functions - > registering components
  3. Searching for all elements with the data-component attribute. That’s how we mark our Vue components
  4. Creating one new instance of Vue ‘around’ one element with the data-component attribute
  5. Listener which will trigger our startApp function on the DOMContentLoaded event

Not rocket science at all :)

Step 4 - Components.js

import counter from '../../../../../apps/aem-ktln/components/content/counter/clientlib/js/counter.js'

const components = [];

components.push(...[
    counter]);

export default components;
  1. Import the function to register the counter component
  2. Push this function to the array of components
  3. Export array of components

Step 5 - Counter.js

import Vue from 'vue';

function counter() {
    Vue.component('counter', {
        props: ['number', 'buttonText'],
        template: `<div>
                 <button @click="increment">{{buttonText}}</button>
                 <h3>{{counter}}</h3>
               </div>`,
        methods: {
            increment() {
                this.counter++;
            }
        },
        data() {
            return {
                counter: Number.parseInt(this.number)
            }
        }

    })
}

export default counter

  1. Import Vue
  2. Define component
  3. Wrap it with the ‘register function’
  4. Export register function

That’s it! The last thing we have to do is simply deploy and check live.

Conclusion

It is working but it’s not a production-ready solution. Instead you should think about it as a starting point.

Lots of things can be improved: * How to register components * How to define templates * inline * x-template * string * State management if required * etc

If you have any thoughts and ideas about it do not hesitate to share them in the comments below, e-mail me or report it as an issue at github




comments powered by Disqus