Skip to content

feat(compiler-sfc): withDefaults supports the use of local variables #8592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

zqran
Copy link
Contributor

@zqran zqran commented Jun 17, 2023

close: #8517


Usage:

<script setup lang="ts">
  import { ref } from 'vue'
  const defaultsObject = { name: "Evan You" }
  const props = withDefaults(defineProps<{name: string}>(), defaultsObject) // support local variable
</script>

Copy link
Member

@sxzz sxzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zqran
Copy link
Contributor Author

zqran commented Jun 17, 2023

defaults should not be hoisted if it's used by other assignments.

Ok, with this situation in mind, I think there might be a few things to confirm:

  1. defaults.foo = 'xxx' -> set property
  2. defaults = {} -> set new value
  3. defaults.xxx -> read property
  4. someFunc(defaults) -> as parameter

@netlify
Copy link

netlify bot commented Jun 18, 2023

Deploy Preview for vue-sfc-playground failed.

Name Link
🔨 Latest commit 1c14795
🔍 Latest deploy log https://app.netlify.com/sites/vue-sfc-playground/deploys/648f060bee3fe400083fc8c1

@netlify
Copy link

netlify bot commented Jun 19, 2023

Deploy Preview for vue-next-template-explorer failed.

Name Link
🔨 Latest commit 1c14795
🔍 Latest deploy log https://app.netlify.com/sites/vue-next-template-explorer/deploys/648f060b0cb03c00088d4068

@sxzz
Copy link
Member

sxzz commented Jun 22, 2023

@zqran No, if there's any reference not inside of withDefaults, then we can just hoist the variable (const only).

@zqran
Copy link
Contributor Author

zqran commented Jun 22, 2023

@sxzz

There are two different cases depending on the operation:

  1. get
  2. set

For the set case, as long as the value is modified it cannot be hoisted (explicitly).
For the get case, the following scenarios are possible:

  • const (explicitly)
// hoist
const defaults = { baz: false }
const props = withDefaults(defineProps<{
  baz: boolean
}>(), defaults)

// If there are still the following operations:

// hoist
console.log(defaults)
someFunc(defaults)
  • let (not explicitly)
// hoist
let defaults = { baz: false }
const props = withDefaults(defineProps<{
  baz: boolean
}>(), defaults)

// If there are still the following operations:

// need to confirm
console.log(defaults) // Special case? need to be excluded?
someFunc(defaults)

These different rules(scenarios that need to be confirmed above) may confuse users.

Therefore, since it is used as the default value, it is agreed that it cannot be modified. Then can we agree: If you want to use local variables as parameters of withDefault, you must use const declarations. This might be more explicit.

@sxzz
Copy link
Member

sxzz commented Jun 23, 2023

Even tough it's const, we can still modify the property value of the object.

const obj = { a : 1 }
obj.a = 2

I think the better way for avoiding confusing is that local variables as parameters of withDefault should never used outside of withDefault.
For example we only support this case

const defaults = {}
withDefaults(defineProps(), defaults)

// defaults is never be used.
// console.log(defaults) // ❌
// defaults.add() // ❌
// someFunc(defaults) // ❌

@zqran
Copy link
Contributor Author

zqran commented Jun 23, 2023

It is worth mentioning that hoist will take effect when using reference:

// foo.ts
export const defaults = { baz: false }
import { defaults } from './foo'
const props = withDefaults(defineProps<{bar: boolean}>(), defaults)

// defaults is used:
console.log(defaults)
defaults.baz = true

If so, it may behave inconsistently with local variables.

@sxzz
Copy link
Member

sxzz commented Jun 23, 2023

@zqran Cause exported variable is top level and always inited once, but in <script setup> (without this feature), it's inited every time. We must ensure there's no side effect if we hoist inner variable to the top level.

@zqran
Copy link
Contributor Author

zqran commented Jun 23, 2023

Thank you for your explanation, I will modify it according to the above content.

Copy link
Member

@sxzz sxzz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

node: Statement
needHoist?: boolean
}
> = {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> = {}
> = Object.create(null)

@sxzz
Copy link
Member

sxzz commented Aug 13, 2023

I think it's very hard to figure out which variable should be hoisted without scope stack (like we did in Reactivity Transform package). But if we introduce scope stack, it will greatly increase the complexity of the SFC compiler.

Maybe we need a complete refactor, I'm considering.

@zqran
Copy link
Contributor Author

zqran commented Aug 14, 2023

I think it's very hard to figure out which variable should be hoisted without scope stack (like we did in Reactivity Transform package). But if we introduce scope stack, it will greatly increase the complexity of the SFC compiler.

Maybe we need a complete refactor, I'm considering.

It's awesome! Looking forward to its progress.

@mosinve
Copy link

mosinve commented Dec 4, 2023

Hi, @zqran is this PR planning to merge? Looks like i need it very much)

@zqran
Copy link
Contributor Author

zqran commented Dec 5, 2023

Hi, @zqran is this PR planning to merge? Looks like i need it very much)

@mosinve
It may not work now. For specific reasons, please see the explanation of @sxzz above.

#8592 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Waiting
Development

Successfully merging this pull request may close these issues.

withDefaults should work with an object literal reference
4 participants