Comments (16)
😍
Vue.observable
– do we want to use the name observable
? As it was already pointed out somewhere else, it might be misleading as it is in 2.6 and might not be a good idea to keep it.
All current lifecycle hooks will have an equivalent useXXX function that can be used inside setup():
onXXX
typo
These options will only be available in the compatibility build of 3.0
- data (replaced by value and value.raw returned from setup())
- computed (replaced by computed returned from setup())
- methods (replaced by plain functions returned from setup())
- watch (replaced by watch)
- provide/inject (replaced by provide and inject)
- mixins (replaced by function composition)
- extends (replaced by function composition)
- All lifecycle hooks (replaced by onXXX functions)
This is huge. What happens to this
? Will it be available inside setup
? Where should we $emit
events? Can this be done inside composition functions by receiving the vm
? But this will probably hide the source of the events?
In the compat build, will it be possible to call composition functions in other places than setup
?
from core.
Where should we $emit events?
I agree this is quite unclear in the current RFC. Maybe something like this:
import { emit } from 'vue'
export function myComposition () {
emit('change', 42)
}
from core.
Motivation > Bundle Size
The returned object in setup
will still keep the original names, wouldn't it? I was wondering if it's possible to mangle those in production with some activable option, it could reduce the size of apps even more.
Detailed Design > State
is state
dropped in favor of always using value
. Is was on vuejs/rfcs#22, so I think people will wonder where did it go and mentioning they got merged would help
Regarding the example with props:
setup(props) {
return {
msg: `hello ${props.name}!`
}
},
Does this return a static value or is it equivalent to creating a computed property
Watchers
If we watch a function that returns an array, does the watcher trigger whenever the array is modified? I'm asking because right now, passing a getter to the watcher will trigger only if the value or reference is modified, which requires the dev to serialize the information to properly trigger one watcher when one of the multiple properties we want to watch changes.
// taken from https://github.com/vuejs/rfcs/pull/22#issuecomment-481084795
watch(
() => [currentArenaRef.value, currentUserRef.value],
([currentArena, currentUser], [prevArena, prevUser]) => {
findMatches(currentArena,currentUser ) ...
}
})
can we also do
const currentInfo = observe({
currentArenaRef,
currentUserRef
})
watch(
currentInfo,
// what happens if the user does
// () => currentInfo
// do we warn them?
// ...
})
const currentArenaAndUser = value([
currentArenaRef,
currentUserRef
])
watch(
currentArenaAndUser,
// can we do this?
})
Watcher Callback Timing
The sync
option triggers the watcher as soon as the value being watched is modified, doesn't it? I think an extra sentence like Doing count.value++
will immediately trigger the watcher
I think it's worth adding that lazy
and deep
are the same as current API (maybe with a link to docs). What are onTrack
and onTrigger
?
Adoption Strategy > Options deprecated by this RFC
What is value.raw
? is it value
Drawbacks
Another drawback is having pretty much everything inside of one function, leaving to the user how to organize all the logic, which could indeed bring spaghetti code like mentioned in (vuejs/rfcs#22 (comment)). Previously, having methods
, data
, computed
etc, constrained developers to organize their code
from core.
Kinda agree on the drawback @posva mentioned. Thought the setup
would be intended to go along the object properties and not entirely replacing them.
from core.
Thinking about vue router navigation guards. Since properties returned in setup
are not accessible through this
, we will need to provide a function-based api as well for in-component guards like onBeforeRouteEnter
, onBeforeRouteUpdate
and onBeforeRouteLeave
but at the same time, it's impossible from the router to pick up onBeforeRouteEnter
function as they have to be invoked before the component is even instantiated.
Is there a way we make the returned value in setup
available in the component so other libraries can still interact with component properties through custom functions
from core.
I am also worried that having everything defined in setup
might very well lead to spaghetti code for developers who are unfamiliar with any best practices for using this new API.
With regard to being able to mutate unwrapped values in template, as shown:
const MyComponent = {
setup() {
return {
count: value(0)
}
},
template: `<button @click="count++">{{ count }}</button>`
}
Would that also apply when passing the unwrapped value through a scoped slot? e.g.
const MyComponent = {
setup() {
return {
count: value(0)
}
},
template: `<div><slot v-bind:count="count"></slot></div>`
}
const OtherComponent = {
template: `<my-component><button v-slot="props" @click="props.count++">{{ props.count }}</button></my-component>`
}
from core.
What also worries me that this will most likely break plugins like Vuelidate, that rely on component options and this
context. I imagine it could be rewritten to become a composition function
const validatedState = vuelidate(state, validationSchema)
I like the idea personally, but I fear this will make the library harder to use, especially since currently it also supports validating computed properties or Vuex getters.
Also, how would we use Vuex with that API? Say using mapState
inside setup
to get a variable?
const [users, posts] = mapGetters(['users', 'posts'])
from core.
@shentao Vue.observable – do we want to use the name observable? As it was already pointed out somewhere else, it might be misleading as it is in 2.6 and might not be a good idea to keep it.
Yes, this should be one of the unresolved questions. I was thinking between state
and value.raw
.
- Technically
state
doesn't really exclude primitive values, so the distinction betweenstate
andvalue
isn't super clear just by looking at the name. value.raw()
makes it more obvious that you are creating a non-wrapped reactive object, but looks... uglier?
@shentao What happens to this? Will it be available inside setup?
this
will still be there and will be available in setup
.
@nekosaur Would that also apply when passing the unwrapped value through a scoped slot?
No, slot props are always immutable.
@posva I was wondering if it's possible to mangle those in production with some option
This would require template references to be mangled too - which can be very hard.
@posva Does (the props example) return a static value or is it equivalent to creating a computed property
It works just like returning an object from data()
.
If we watch a function that returns an array, does the watcher trigger whenever the array is modified? I'm asking because right now, passing a getter to the watcher will trigger only if the value or reference is modified, which requires the dev to serialize the information to properly trigger one watcher when one of the multiple properties we want to watch changes.
I don't understand the question. Watchers have always been working this way. If you want to trigger watcher with nested mutations you should be using deep: true
.
Spaghetti code
I tend to look at this problem this way: since function-based APIs leave more flexibility to the users, this can lead to both less organized code (by beginners) AND better organized code. However, the less organized code can be largely mitigated with proper documentation of best practices, while the better organized code can never be achieved with current options-based API.
With component options, your code only seem to be organized - in a complex component, logic related to a specific task is often split up between multiple options. Separation of options vs. setup()
is like sticking to the separation of HTML/CSS/JS vs. Single File Components. If you put all the logic of your app in a single SFC, that SFC is going to become a monster and become very hard to maintain - so we split the component into many smaller ones. Similarly, if you have a huge setup()
function, you split it into multiple functions. Function based API makes better organized code easily possible while with options you are stuck with... options (because splitting into mixins makes things worse).
from core.
With component options, your code only seem to be organized - in a complex component, logic related to a specific task is often split up between multiple options. Separation of options vs. setup() is like sticking to the separation of HTML/CSS/JS vs. Single File Components. If you put all the logic of your app in a single SFC, that SFC is going to become a monster and become very hard to maintain - so we split the component into many smaller ones. Similarly, if you have a huge setup() function, you split it into multiple functions. Function based API makes better organized code easily possible while with options you are stuck with... options (because splitting into mixins makes things worse).
This does work for me. Thank you!
Just one more question and I think I already know the answer, but just need to make sure – there are no hidden restrictions as to what a composition function is returning right? So it can return methods that can be later used inside the component.
I also imagine that possibly, a composition function could return a render function (a functional component) that could then be used inside the components render function? Or template?
from core.
- Compatibility build: supports both the new function-based APIs AND all the 2.x options.
Does that mean that it's possible to mix the two?
export default {
setup() {
return {
count: value(0)
}
},
computed: {
plusOne() {
return this.count + 1
}
}
}
Not that I think this is nice, but it would be convenient for migrating bigger components piece by piece from the object notation to the setup function.
from core.
I don't understand the question. Watchers have always been working this way. If you want to trigger watcher with nested mutations you should be using deep: true.
I'm realizing I have been teaching a way of doing this that is more complicated than it should because using an array also works 😅. It may trigger more times if a value returned in the array is changed multiple times before a flush and is set to the same value it had before being changed the first time
However, the less organized code can be largely mitigated with proper documentation of best practices
I think that practice has shown us that people don't read the documentation, no matter how good it is. Aren't we going to complexify things for people who are starting if they learn first the object based syntax (Vue 2) and then learn the advanced version which allows them to use the smaller version of Vue? It feels like we are making the learning curve steeper.
However, I do agree that having the functional approach is better in bigger projects. So I wonder if having some tool to hint how to convert (I doubt it is possible to fully automatize) plus a good documentation teaching how to move from the non function-based syntax to the function one is enough to keep Vue as approachable as it is today, which is, in my experience the most selling point of Vue
from core.
I think that practice has shown us that people don't read the documentation, no matter how good it is.
I don't think that's true at all. People probably don't read the API listing in full details, but they definitely read the guide when they first learn Vue. The point is - anyone learning the new API will have to learn it through something - be it docs or 3rd party content - and any 3rd party content will be some sort of spin-off of the docs. So as long as the initial v3 docs introduces it properly, most users will learn to use it properly.
from core.
@LinusBorg Does that mean that it's possible to mix the two?
Yes. There might be some edge cases (I haven't fully implemented 2.x compat support yet) but theoretically it should work.
from core.
const plusOne = computed(() => count.value + 1)
Could that also be const plusOne = computed(() => this.count + 1)
or is setup
more like beforeCreate
?
from core.
@KaelWD it's more like beforeCreate
. It will be called only after beforeCreate
and props resolution, but before all other 2.x options.
from core.
const writableComputed = computed(
// read
() => count.value + 1,
// write
val => {
count.value = val - 1
}
)
I like how it's much easier to switch between a read-only and writeable computed properties without having to rewrite between an object and functions.
from core.
Related Issues (20)
- 【BUG】watch() 传入reactive类型的响应式数据,vue3.4版本与3.3/3.2表现不一致 HOT 1
- watchEffect did not perform as expected. HOT 3
- vue with element-plus,el-select component,el-option tag must use <el-option></el-option>,can't use <el-option label="xxx" /> HOT 1
- Async webcomponent inside another async webcomponent gives error
- SSR and Client-Hydrated Vue Components do not fully hydrate to client-rendered DOM as stated in the documentation HOT 1
- TS2536 thrown when using a conditional type with `keyof` for a generic type parameter HOT 4
- 浏览器开启翻译成中文后,输入框v-model无法正常双向绑定。 HOT 1
- Input loses focus and triggers a change event bound to the parent component HOT 2
- Hard code for `runtimeModuleName`
- When accessing the hasOwnProperty method of the array, the key cannot be tracked HOT 5
- TypeError: parent is null : Unhandled error during execution of scheduler flush HOT 1
- functional components behave differently in ssr mode as well as in csr mode HOT 4
- `window.devtoolsFormatters` increase on update HOT 2
- v-bind (3.4) shorthand not working on `<Component />`
- Failed to parse `<script>'<script></script>'</script>` HOT 1
- Computed does not trigger after its effect is run and returns different value HOT 4
- Getting a Type Error with Ref on an instance of a Class with a private field 'value' HOT 3
- Vue 3 Script Setup syntax + style tag scoped = bug HOT 4
- watchEffect Bug HOT 1
- watchEffect BUG HOT 6
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from core.