vuejs / rfcs

RFCs for substantial changes / feature additions to Vue core
4.87k stars 548 forks source link

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

Closed Slokilla closed 1 year ago

Slokilla commented 1 year ago

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 1 year ago

Seems to be duplicate of https://github.com/vuejs/core/issues/6400

See also https://github.com/vuejs/core/issues/6400#issuecomment-1204321077

Slokilla commented 1 year ago

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 1 year ago

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

Slokilla commented 1 year ago

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 1 year ago

🤔 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.

lishixuan13 commented 1 year ago

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 1 year ago

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

SomeoneIsWorking commented 1 year ago

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

sxzz commented 1 year ago

@SomeoneIsWorking You could try https://vue-macros.sxzz.moe/macros/define-model.html

zzhenryquezz commented 1 year ago

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)