vuejs/rfcs

<script setup> : Make it possible to call defineProps many times

Slokilla opened this issue · 10 comments

What problem does this feature solve?

As a Vue user, what i love the most from Vue 3 and composition API is the way we can arrange the code by logical unit. The component becomes pleasant to read.

To improve this feeling, it would be great to define some feature related props, in each logical unit. So we don't have to scroll to the document top to check props parameters.

What does the proposed API look like?

I actually don't know Vue API enough to implement a feature, so if this feature is judged interesting, I will first have to learn how Vue is internally designed.

Maybe we will have to expose another method, registerProps().

We can imagine that registerProps will aggregate all the props definitions, before calling the usual defineProps.
I think the final api should look like :

registerProps(['a', 'b'])

/* Code that uses a and b */

registerProps(['c'])

/* Code that uses c */
sxzz commented

Seems to be duplicate of vuejs/core#6400

See also vuejs/core#6400 (comment)

You think so ? My proposition looks far less ambitious. I mean, the function could only be called at root level, as defineProps.

I think that you should see it as a syntaxic sugar for defineProps(['a', 'b', 'c']), but distributed among logical units in your code.

sxzz commented

Could you please provide a more detailed SFC example (<script setup>) of the proposal?

I think it would look like this :

<template>
    <p>Hello {{ username }}</p>
    <p>{{ counter }} is {{ parity }}</p>
</template>

<script setup>
import { computed, registerProps, created } from 'vue'

// User name logic
const userProps = registerProps({ userId : {type: Number, required: true } })
const username = ref('')
onBeforeMount(() => {
    username.value = fetchUsernameById(userProps.userId) 
})

// Counter logic
const counterProps = registerProps({ counter: { type: Number, required: true } })
const parity = computed(() => { return counterProps.counter % 2 === 0 ? 'even' : 'odd' })
</script>

and it should be equivalent to something close to :

<template>
    <p>Hello {{ username }}</p>
    <p>{{ counter }} is {{ parity }}</p>
</template>

<script setup>
import { computed, defineProps, created } from 'vue'

const props = defineProps(
{ userId : {type: Number, required: true } },
{ counter: { type: Number, required: true } }
)

// User name logic
const userProps = { userId: props.userId } 
const username = ref('')
onBeforeMount(() => {
    username.value = fetchUsernameById(userProps.userId) 
})

// Counter logic
const counterProps = { counter: props.counter }
const parity = computed(() => { return counterProps.counter % 2 === 0 ? 'even' : 'odd' })
</script>
sxzz commented

🤔 To be honest, I don't like this proposal personally. IMHO it's unnecessary. Maybe that's not the way that Vue recommended? I'm not sure. More opinions are welcome.

You should manage in a unified place, not scattered anywhere, and the statements in<script setup>are static, so your approach seems meaningless

posva commented

I don't think this is a good idea either because it brings little benefit while probably adding a performance impact on tools.

Differently from other composables, props usually form one business logic unit and make sense together as the component arguments

This would help in ways to create utility methods like twoWayProp("x") would create an "x" prop and define "update:x" emit

This is my opinion but I like this idea, but I guess would be better to define single props & emits, like the example below.

// Props

const count = defineProp(1) // default value 1

const count = defineProp<number>() // prop type number

// Emits

const updateCount = defineEmit('update:count')

const updateCount = defineEmit<number>('update:count') // emit type arg number

const updateCount= defineEmit('update:count', (value: number) => value < 3)) // validation

In this way we could organize the code even better by “Logical Concern”

// Count

const count = defineProp(1)

const updateCount = defineEmit('update:count')

// Multiply count

const multipleBy = defineProp(2)

const result = computed(() => count.value * multipleBy.value)