Vue 2 to Vue 3 - Migration Helper

Background

I had a coursework on web development, somehow I didn't want to make another online store, and I decided to write a migration assistant from Vue 2 (options-api) to Vue 3 (composition-api) with auto-division into compositions using the Kosarayu algorithm on the search for regions of strong connectivity.





For those who are not in the subject, I will explain, this is how the code with options-api looks like :





export default {
  data () {
    return {
      foo: 0,
      bar: 'hello',
    }
  },
  watch: {
    ...
  },
  methods: {
    log(v) {
      console.log(v);
    },
  },
  mounted () {
    this.log('Hello');
  }
}
      
      



and something like this with the composition-api:





export default {
  setup (props) {
    const foo = reactive(0);
    const bar = reactive('hello');

    watch(...);

    const log = (v) => { console.log(v); };

    onMounted(() => { log('hello'); });

    return {
      foo,
      bar,
      log,
    };
  }
}
      
      



Automatic division into compositions

, composition-api, , . ?





, ? :





– , , . !





: data, , , , , Vue.





. Vue :





  • computed, method, hook, provide ,





  • ,





  • :)





data: () => ({
  array: ['Hello', 'World'], // block 1
}),
watch: {
  array() { // block 2 (watch handler) depends on block 1
    console.log('array changed');
  },
},
computed: {
  arrayCount() { // block 3
    return this.array.length; // block 3 depends on block 1
  },
},
methods: {
  arrayToString() { // block 4
    return this.array.join(' '); // block 4 depends on block 1
  }
},

      
      



, - . ?





Vue, , .. .





, , - . !





    . .





, C TS :)





. , , . , , – , .





: options-api this







, .js :





const splitter = /this.[0-9a-zA-Z]{0,}/
const splitterThis = 'this.'

export const findDepsByString = (
  vueExpression: string,
  instanceDeps: InstanceDeps
): ConnectionsType | undefined => {
  return vueExpression
    .match(splitter)
    ?.map((match) => match.split(splitterThis)[1])
    .filter((value) => instanceDeps[value])
    .map((value) => value)
      
      



, , this.



:(





, :





export const findDeps = (
  vueExpression: Noop,
  instanceDeps: InstanceDeps
): ConnectionsType | undefined => {
  const target = {}
  const proxy = new Proxy(target, {
  // ,       
    get(target: any, name) {
      target[name] = 'get'
      return true
    },
    set(target: any, name) {
      target[name] = 'set'
      return true
    }
  })
  try {
    vueExpression.bind(proxy)() //     
    return Object.keys(target) || [] //      this.
  } catch (e) { //      
    return findDepsByString(vueExpression.toString(), instanceDeps) || []
  }
}
      
      



:









  • – ?





: .





, , composition-api, , .





, , , , . :





const toString = (item: any): string => {
  if (Array.isArray(item)) {
    // array
    const builder: string[] = []
    item.forEach((_) => {
      builder.push(toString(_)) // wow, it's recursion!
    })
    return `[${builder.join(',')}]`
  }

  if (typeof item === 'object' && item !== null) {
    // object
    const builder: string[] = []
    Object.keys(item).forEach((name) => {
      builder.push(`${name}: ${toString(item[name])}`) // wow, it's recursion!
    })
    return `{${builder.join(',')}}`
  }

  if (typeof item === 'string') {
    // string
    return `'${item}'`
  }

  return item // number, float, boolean
}

// Example
console.log(toString([{ foo: { bar: 'hello', baz: 'hello', }}, 1]);
// [{foo:{bar: 'hello',baz: 'hello'}},1] – ..   ,    
      
      



:)





fs.writeFile()







, , .





vue2-to-3 ( ) !





HelloWorld.js



:





export default {
  name: 'HelloWorld',
  data: () => ({
    some: 0,
    another: 0,
    foo: ['potato'],
  }),
  methods: {
    somePlus() {
      this.some++;
    },
    anotherPlus() {
      this.another++;
    },
  },
};
      
      



: migrate ./HelloWorld.js



3 :





// CompositionSome.js
import { reactive } from 'vue';

export const CompositionSome = () => {
  const some = reactive(0);
  const somePlus = () => { some++ };
  return {
    some,
    somePlus,
  };
};

// CompositionAnother.js
import { reactive } from 'vue';

export const CompositionAnother = () => {
  const another = reactive(0);
  const anotherPlus = () => { another++ };
  return {
    another,
    anotherPlus,
  };
};

// HelloWorld.js
import { reactive } from 'vue';

import { CompositionSome } from './CompositionSome.js'
import { CompositionAnother } from './CompositionAnother.js'

export default {
  name: 'HelloWorld',
  setup() {
    const _CompositionSome = CompositionSome();
    const _CompositionAnother = CompositionAnother();
    const foo = reactive(['potato']);
    return {
      foo,
      some: _CompositionSome.some,
      somePlus: _CompositionSome.somePlus,
      another: _CompositionAnother.another,
      anotherPlus: _CompositionAnother.anotherPlus,
    };
  },
};
      
      



, ( linux )





single-file-components



.ts



( .js



)





!





npm, git








All Articles