https://codesandbox.io/s/v-model-decomposed-camel-case-lx173
Take this simple app. In Vue.js 2, it will not do the console.log, unless you set immediate: true in watch options.
const App = {
data() {
return {
message: "Hello"
};
},
watch: {
message: {
handler() {
console.log("Immediately Triggered")
}
}
}
}
In Vue.js 3, however, watch triggers immediately by default. That is because the default for the watch hook is { lazy: true } as discussed here under "Lazy Invocation". This means in the current state, this would be a breaking change for Vue.js 2 apps relying on the immediate option defaulting to false.
Also, in Vue 2.x you can do:
data() {
foo: {
bar: {
qux: 'val'
}
}
},
watch: {
'foo.bar.qux' () { /* */ }
}
And watch a deeply nested property. This appears to not work in Vue 3.X - nothing happens.
Behavior same as Vue 2.X, or open RFC to change behavior:
I like the new changes, they rock. Just need to make sure we have either a clear migration path for people relying on old behavior if making changes.
I am happy to make a PR making changes to the code base if needed, or an RFC regarding the breaking changes.
Yes, these are somewhat intended.
The watch in Composition API exhibits the new behavior described. In 3.0, watchers are fired immediately after component mount.
I think it's better for the watch option to be made consistent with the Composition API instead of creating a mismatch between the two. To retain the old behavior, use the { lazy: true } option (basically the opposite of immediate) The downside, obviously, is the migration cost.
We want to reduce the reliance on magic strings. In 2.x the watch option only supported dot-eliminated paths (not full JS expressions), which was somewhat weird in the first place.
created() {
this.$watch(() => this.foo.bar.baz, (value) => {
})
}
I think an RFC would be great to gather more feedback on these changes.
Re: your first point: while it makes sense that having different APIs for watchers between Composition and Object API might be confusing, in my personal experience, I use immediate much less than not. So while the breaking change could be documented (and maybe even addressed by a codemod?), having to explicitly specify lazy: true to all non-immediate watchers would be quite irritating, especially that you wouldn't be able to use the short someProp() { ... } form and need to use the object one someProp: { handler() { ... }, lazy: true }.
@lbogdan I think there is a force of habit here. What I've found is that in a lot of cases people are actually doing this:
created() {
this.fetchData(this.id)
},
watch: {
id: fetchData
},
methods: {
fetchData(id) {
// ...
}
}
Where with immediate by default this becomes:
watch: {
id(id) {
// fetch data...
}
}
Would be great to learn more about cases where lazy: true is absolutely required.
Yes, that's exactly the use-case I use immediate: true for. But pretty much all my other imperative watchers ("do something when some state changes") are non-immediate, and would probably break in subtle ways if suddenly made immediate. I could go through my code bases and come up with examples, if that helps. We can also probably look at popular Vue (UI) libraries and see what the distribution of immediate vs. non-immediate watchers is.
@lbogdan yeah, that would be really helpful.
Want to chime in that currently I often rely on watchers not being triggered immediately because I put client-only code in them when I know that a change wont ever happen on the server (eg because a change can only happen after user interaction).
I opened an RFC as suggested; I'll close this so we can keep discussion in one place.
@pimlie FYI watch in Vue 3 is not fired synchronously (even for the initial call) - they are in fact deferred until the component is mounted - so even with immediate by default they won't fire during SSR unless you explicitly use { flush: 'sync' } in the watcher's options.
@yyx990803
@lbogdan I think there is a force of habit here. What I've found is that in a lot of cases people are actually doing this:
created() { this.fetchData(this.id) }, watch: { id: fetchData }, methods: { fetchData(id) { // ... } }Where with immediate by default this becomes:
watch: { id(id) { // fetch data... } }Would be great to learn more about cases where
lazy: trueis absolutely required.
I do it myself all the time. But it's not out of habit, it's about how you think about the watcher. When I make an algorithm in my head I think something like this:
watcher was created, you expect the handler to not be launched.And the fact that an watcher can launch a handler right at the moment of installation is a little confusing.
I want to say that this is how you perceive the logic of work by its name. Perhaps if this api was called otherwise, say ComputedFunction or effect then such a misunderstanding would not arise.
@cawa-93 that's a good point.
So another option we have is:
watch option;watch in Composition API to:autorun?runAndWatch?effect?@yyx990803 I too think that's a much better compromise.
In regard to naming:
autorun - sounds like something that only runs once, at initializationrunAndWatch - too many words, hard to remember (the order)effect - I think that's the best one: short and expressive; also it's kind of similar to useEffect, so React people will get it right awaywatchImmediate - still two (long) wordsActually, I think that effect is also a better name for composition's API watch, although it's probably too late to rename it. Maybe add effect as an alias of watch, and deprecate watch?
L.E. Nvm, I misunderstood adding a new option to the options API vs. renaming watch in composition API.
@lbogdan I've always disliked useEffect because for some reason its never immediately clear to me what it means. An effect has a cause but what exactly is the cause here? Watch (or observe, listen) is much more clear to me in terms of what to expect.
Eg if you 'watch' your children then you make sure that when they do something its not dangerous. An effect would be that you have to tell them to stop doing something, but that means you had to watch/observe them doing that dangerous thing already.
But Im not a native speaker, so maybe thats just me.
I'm not a fan of effect either - watch is definitely more intuitive to me. I don't think it's too late to change the name - Vue 3 is still in alpha. Other words that come to mind areobserve and perform.
I also think that observe is much more clear in meaning than effect. However watch has less letters so it's faster to type. I'd rathet stay with watch
My reasoning for effect is that you're defining a (side-)effect of state changing. I'm not a native speaker either, but for me, both watch and observe are semantically passive actions, meaning more like "watch state for changes" than actually "do something when state changes". I've been using watch for so long that I've never really thought about semantics until now, though. 馃檪
Please use the RFC for further discussions so that we can have it all in one place, thanks!
Most helpful comment
@lbogdan I think there is a force of habit here. What I've found is that in a lot of cases people are actually doing this:
Where with immediate by default this becomes:
Would be great to learn more about cases where
lazy: trueis absolutely required.