Vue 2 a Vue 3 - Asistente de migración

Fondo

Tenía un curso sobre desarrollo web, de alguna manera no quería hacer otra tienda en línea y decidí escribir un asistente de migración de Vue 2 (options-api) a Vue 3 (composición-api) con división automática en composiciones utilizando el algoritmo de Kosarayu en la búsqueda de regiones de fuerte conectividad.





Para aquellos que no están en el tema, explicaré, así es como se ve el código con options-api:





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



y algo como esto con la composición-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,
    };
  }
}
      
      



División automática en composiciones

, 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