Vue 2 à Vue 3 - Aide à la migration

Arrière-plan

J'avais un cours sur le développement Web, d'une manière ou d'une autre, je ne voulais pas créer une autre boutique en ligne et j'ai décidé d'écrire un assistant de migration de Vue 2 (options-api) vers Vue 3 (composition-api) avec division automatique en compositions utilisant l'algorithme de Kosarayu sur la recherche de régions à forte connectivité.





Pour ceux qui ne sont pas dans le sujet, je vais vous expliquer, voici à quoi ressemble le code avec options-api :





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



et quelque chose comme ça avec la 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,
    };
  }
}
      
      



Division automatique en 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