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
)
!