from Angular 1.x to Vue 2.x

How to refactor your application?

Tech Lead @ Meetic

k.lebrun@meetic-corp.com

kevinlebrun

Why?

  • Hype?!
  • Support
  • Performances
  • Architecture
  • Developer UX

Strategy

Top-down

Bottom-up

Inception

An app inside an app

Decouple from Angular

  • $q
  • $http
  • inject
  • angular-translate

Tactics

Upgrade the build

Configure Vue & axios

import angular from 'angular';

export default angular
  .module('vue', [])
  .value('Vue', Vue)
  .factory('axios', function (CONFIG, AuthTokenService) {
    return axios.create({
      baseURL: CONFIG.api_url,
      withCredentials: true,
      timeout: 2000,
      headers: { 
        'X-Client': CONFIG.client_id, 
        'Authorization': `Bearer ${AuthTokenService.getBearer()}` 
      }
    });
  })
  .run(function (Vue, axios) {
    Vue.use((Vue) => {
        Object.defineProperties(Vue.prototype, {
          $http: {
            get() {
              return axios;
            }
          }
        });
      });
  })
  .name;

Hello World!

import angular from 'angular';

import template from './hello-template.html';
import controller from './hello-controller';

export default angular
  .module('hello', [])
  .config($stateProvider => {
    'ngInject';
    $stateProvider.state({
      name: 'hello',
      url: '/hello',
      template,
      controller
    });
  })
  .name;

Hello World! II

<div id="vue-hello">
  <vue-hello></vue-hello>
</div>

<template id="vue-hello-template">
  <p>Hello World!</p>
</template>

<script>
    Vue.component('vue-hello', {
        template: '#vue-hello-template'
    })

    var app = new Vue({
        el: '#vue-hello'
    });
</script>

Hello World! II

export default function(Vue) {
  'ngInject';

    Vue.component('vue-hello', {
        template: '#vue-hello-template'
    });

    var app = new Vue({
        el: '#vue-hello'
    });
}

Hello World! III

{
    "hello": {
      "default": "Bonjour le monde!",
      "named": "Bonjour {NAME}!"
    }
}

Hello World! IV

<div id="vue-hello">
  <vue-hello :name="name"></vue-hello>
</div>

<template id="vue-hello-template">
  <div>
    <p v-translate="'hello.default'"></p>
    <p v-translate:values="{ text: 'hello.named', NAME: name }"></p>
  </div>
</template>

Hello World! V

Vue.component('vue-hello', {
    props: {
      name: {
        type: String,
        default: 'Kevin'
      }
    },
    template: '#vue-hello-template'
});

Hello World! VI

  .run(function (Vue, $translate) {
    const refresh = (el, binding) => {
        let data = {};
        let text = binding.value;

        if (binding.arg === 'values') {
          data = binding.value;
          text = binding.value.text;
        }

        $translate(text, data).then(function (translations) {
          el.innerHTML = translations;
        }, function (translationId) {
          el.innerHTML = translationId;
        });
    };

    Vue.directive('translate', {
      inserted: refresh,
      update: refresh
    });
  })

Filters are the same

const truncate = (text, length) => {
    if (typeof length !== 'number') {
      return text;
    }

    if (length === 0) {
      return '';
    }

    return _.truncate(text, {length, omission: '…'});
}

export { truncate };
  .run(function (Vue) {
    Vue.filter('truncate', truncate);
  })

Listen to $scope changes

    $scope.name = 'Meetic';

    const bus = new Vue();

    $scope.$watch('name', function (newValue) {
      console.log(newValue);
      bus.$emit('hello:change', newValue);
    });

    Vue.component('vue-hello', {
        props: {
          name: {
            type: String,
            default: 'Kevin'
          }
        },
        data() {
          return {
            theName: this.name
          };
        },
        template: '#vue-hello-template',
        mounted() {
          bus.$on('hello:change', name => {
            this.theName = name;
          });
        }
    });

Using ngVue

  .factory('VueHello', function (Vue) {
    return Vue.component('vue-hello', {
        props: {
          name: {
            type: String,
            default: 'Kevin'
          }
        },
        template: '#vue-hello-template'
    });
  })
<vue-component name="VueHello" vprops-name="name"></vue-hello>

It's fine but...

What about the overhead?

Library Size (min) Size (gzip)
Vue 55 ko 20 ko
axios 12 ko 2.5 ko
Promise polyfill 6.47 ko 2.4 ko
ngVue 3.4 ko 1.5 ko
Total 76.87 ko 27.4 ko

What about the CSS?

What about the state?

What about the i18n?

Conclusion

Refactoring is hard!

Decouple your code.

Use FormatJS...

Q/A

from Angular 1.x to Vue 2.x

By Kevin Le Brun

from Angular 1.x to Vue 2.x

How to refactor your entire application?

  • 1,547