Migrating to VueJS — Another way

Lucas Katayama
Lucas Katayama
Published in
4 min readNov 19, 2017

--

I found a way that fits better with our reality. In this post I’m going to show how I’m doing it.

In my last post Migrating from Angular to VueJS, I showed how I did to start migrating from AngularJS to VueJS.

That wasn’t the best way for us. We found another answer.

First: Context

For those who didn’t read the first post (Migrating from Angular to VueJS), I’m in a journey to migrate a legacy web platform written in AngularJS.

The first attempt was to put multiples AngularJS routes to multiple VueJS instances, and had Vue code inside Angular.

The major problem with that approach is in some point I’ll need to refactor the code to the Vue way.

Not a good idea.

Solution

The solution is to have ONE entrypoint to VueJS application inside Angular and write and build in VueJS way.

The overall idea is:
1. Create a Vue application with vue-cli
2. Make Webpack build generates file to the webapp folder where Angular resides
3. Put a single path in the Angular router
4. In that router make a Angular controller instantiate a single Vue instance

Creating a Vue application with vue-cli

The project folder structure is something like this:

+ Project
+ web
- index.html(Vue)
+ webapp
- index.jsp (Tomcat + Angular)

In the web folder, we run:

$ vue init webpack .

It will create a Vue project.
Now we need to change the default build configuration. Let’s create a migration build:

Creating a migration build

The migration build is a development build, but for our context it move all the generated files to the folder webapp/dist/, so they can be used by the application

  1. Edit the package.json and add a new script
{
...
"scripts": {
"dev-migration": "node build/build-migration.js"
}
...
}

2. Create a build/build-migration.js with the same content from build/build.js, and edit those following lines:

process.env.NODE_ENV = 'development'; // it was 'production'...const webpackConfig = require('./webpack.migration.conf') // it was webpack.prod.js...const spinner = ora('building for migration...') // it was for production

That’s right, we are using a production build with development scheme.

3. Create a build/webpack.migration.js with the same content of build/webpack.dev.conf.js.

Add watch: true, to the webpack configuration:

// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
watch: true,
// these devServer options should be customized in /config/index.js
devServer: {

And remove and change the module.exports from the Promise to the webpack configuration:

module.exports = devWebpackConfig

4. Change the generated path in config/index.js

Change it to final destination folder, in my case it was webapp/dist beacause we use Tomcat and it reads and server files from webapp

...
dev: {
index: path.resolve(__dirname, '../../webapp/dist/index.html'),
assetsRoot: path.resolve(__dirname, '../../webapp/dist/'),
...

Note the relative path in our folder structure.

5. Now run npm run dev-migration

$ npm run dev-migration

It should build development mode but now copying files to the webapp/dist.

6. Include the generated files in the application

In the webapp/index.jsp you can now include the Vue application.

<script src="./dist/app.js"></script>

Modifying the Vue and Angular to render Vue instances

  1. Configure a Angular controller and the router to render a Vue instance

In your router, do something like this:

{
path: '/vue/*',
controller: 'VueJSController',
template: '<div id="vue-app"></div>'
}

It will handle ALL vue paths inside /vue/something with the VueJSController.

Create the VueJSController:

angular.controller('VueJSController', function($scope) {
// do it for now, I'll explain later
// pay attention to this id, it matches the Angular template
var vueApp = window.initVue('#vue-app');
$scope.$on('$destroy', function(){
vueApp.$el.remove();
vueApp.$destroy();
vueApp = null;
});
})

The destroy avoids recreating the same instance again resulting in memory leak and route problems.

2. Modify VueJS main.js to have the initVue(el) method

This is what vue-cli give:

/* eslint-disable no-new */
new Vue({
el: '#app',
router,
template: '<App/>',
components: { App }
})

We create a function to generate that instance:

window.initVue = (id) => {
new Vue({
el: id,
router,
template: '<App/>',
components: { App }
})
}

Change VueRouter to have the /vue as root

Go to the root route of VueJS router and add the /vue to match the Angular route. That will make the Vue Router work with Angular.

DONE!

Now what happens?

When you navigate to /vue/something, Angular will match that route, create a controller, the controller will instantiate the Vue app in that element, and all will go to Vue.

When you edit the Vue component, Webpack will rebuild and put all the new files into the dist folder. Then, after reloading application all the new Vue code will be inside Tomcat, for instance.

The only negative thing is you don’t have a Hot Module Reload, because is Tomcat that is serving all the files.

Next post I’ll show how to create a library project to create shared components.

Please share, comment, like.

Thanks!!

--

--