Comments (15)
Hey Evan!
I like this. I don't have any real opinion on the details like created()
vs. data()
vs. state()
, but the general syntax/workflow seems great.
I would like to see the risk of overwhelming users with too many new things, or rather - too many choices with 3.0 - being adressed, though.
I really think there will be FUD over object vs. class syntax already. We will of course adress that by recommending for people to stick to what they have similar to how React doesn't recommend converting all class components to functional components + hooks.
But just like in the React community, despite that recommendation, some level of FUD will exist.
Introducing this totally new way of doing things in 3.0 will probably have the same effect:
- "When I migrate to Vue 3.0, should I migrate my mixins to hooks or not? are mixins dead? They seem inferior, so why stick with them?"
- some random Blog post "7 reasons to switch to Vue hooks NOW" (it's always 7 reasons, btw - why?)
etc, pp.
We can only do so much to contain that, and I would hate for people getting the impression that 3.0 does an Angular move by changing "everything" (however wrong that impression may be).
Just as an idea worth discussing - Would we maybe consider introducting this as explicitly experimental, or publish it in 3.1, after 3.0 has stabilized after release?
from core.
Another important aspect is the ability to compose multiple "logic modules" together by passing state between them (while not polluting the namespace of the host component):
export default {
props: ['id'],
created() {
const position = useMousePosition()
const orientation = useDeviceOrientation()
const matrix = useMatrix(x, y, orientation)
return {
matrix
}
}
}
So instead of multiple mixins all competing on the same this
namespace for state persistence (and risk clashing with one another), we can just pass refs around via function arguments.
from core.
I think another aspect, not necessarily related to mixins, is that you don't spread parts of a behaviour over different parts of your component, i.e. data&created&watch&destroyed. You can define the whole lifecycle of a piece of behaviour in one place.
from core.
Another thought crossing my mind: Since this is a completely new API, shouldn't we be able to port it to v2, or at least 2.x-next
(which will be using the new Porxy-based Reactivity system?
Would make it possible to use new packages that expose hooks in in 2.0 apps that are not ready tpo be migrated.
And yes, I'm aware that this kind of contradicts my previous point about releasing hooks later :-P
from core.
@LinusBorg I totally agree. This is in fact my biggest concern as well.
But I think that really depends on how we present it. Here this internal proposal is presenting it directly as a hooks equivalent because that's what it is aiming to be - however, we don't really have to introduce it to the users that way.
Notice that the reactivity APIs (value
, computed
and watch
) are not really bound to hooks - they can be used anywhere and are useful in their own right, exposing more capabilities of Vue's reactivity system. The standalone watch
function can be a direct replacement of the current watch
option and this.$watch
method (and it is tree-shakable!).
If we use data()
instead of created()
, then all we are adding is the ability to bind refs in the returned object, which also makes sense.
The only part that is really hooks-like is the useXXX
APIs - this is in fact similar to 2.x's vm.$on('hook:mounted')
. So we actually already have the equivalent (but less ergonomic) capabilities in 2.x today:
function useSomeLogic(vm) {
const count = Vue.observable({ value: 0 })
vm.$watch(() => count.value, value => {
// ...
}, { immediate: true })
vm.$on('hook:mounted', () => {
// ...
})
return count
}
export default {
template: `<div>{{ count.value }}</div>`,
data() {
return {
count: useSomeLogic(this)
}
}
}
This proposal isn't really introducing anything drastically different, but rather polishing some existing APIs to make them work better when used to encapsulate and reuse logic.
from core.
Some thoughts:
- What about computed setters?
- Why not
onMounted
instead ofuseMounted
? I find it more natural to write. Maybe havingonXXX
for side-effects only "'hooks" is more Vue-like, anduseXXX
could be for "hooks" that returns a reference? - I'm not so sure about having
watch
being immediate by default, it clashes with the existingwatch
API we use everywhere in Vue 2.x, adding to the FUD. I get it's useful though, so maybe we could expose another hook likerunWatch
,doWatch
orwatchImmediate
? - What if we want to watch a ref returned in
created
, or use it in a computed property? Since those are defined beforecreated
is called, won't there be issues? What about having a new lifecycle hook, likebind
for example that gets called afterdata
andcomputed
but before watchers are setup? How about "hooks" that rely on computed properties that in turn could rely on references?
from core.
What about computed setters?
Would that work?
function computedWithSetter(getter, setter) {
const computedRef = computed(getter)
return Object.defineProperty(res, 'value',{
get() { return computedRef.value }
set: setter,
})
}
created() {
const computedWSetterRef = computedWithSetter(() => someRef.value, () => /*do whatever*/ )
return {
computedWSetterRef
}
}
<input v-model="computedWSetterRef.value">
I'm not so sure about having watch being immediate by default,
I think it's fine. There's a lot of use cases where I feel watchers would be more useful and code shorter if they were immediate by default.
What if we want to watch a ref returned in created, or use it in a computed property? Since those are defined before created is called, won't there be issues?
Tricky ... :/ Tried to simulate it in 2.0 and failed to make it work.
I like adding it to created
as I think it fits the mental model - we create a component, then add additional behaviours. but that indeed makes it hard / impossible to reference them in computed props or watchers afaict.
beforeCreate
doesn't seem to fit as well, as now we can't access any data, $store and whatnot?
from core.
@LinusBorg if a ref is returned there's no need to use .value
in the template so it could just be v-model="computedWSetterRef"
What if we want to watch a ref returned in created, or use it in a computed property? Since those are defined before created is called, won't there be issues?
Yeah I think in that case a new hook that is called after props initialization but before computed initialization would be useful. As for watchers - I'm thinking of removing watch
option and this.$watch
altogether because they are non-treeshakable.
from core.
I'm thinking of removing watch option and this.$watch altogether because they are non-treeshakable.
Considering our promise of keeping the Vue 3 API largely compatible to 2.0, I don't think we should/can remove APIs for the sole reason of shaving a few bytes of the final bundle in a few use cases.
from core.
@LinusBorg it is trivial to support in the migration build, and in most cases can be automatically converted. The only reason this.$watch
is exposed on this
is so that the watcher can be stopped when the component is destroyed. A generic watch
API can and should be usable in any context. However if we introduce that we end up with 3 different ways of watching things:
this.$watch
- the
watch
option - the globally imported
watch
method.
Where (3) provides a superset of (1) and (2). In this case, (1) and (2) just seem pointless and redundant.
from core.
is it possible to hit a middleground with a compat plugin that adds those features (watch for example) with warnings an cli hints to do the automatic conversion?
from core.
Unlike React Hooks, created is called only once, so these calls are not subject to call order and can be conditional.
Good point. Another positive effect of this, combined with the reactivity system v. React's immutable state, it that we don't have to worry about closures breaking stuff if the wrong references are included in it, or dependency tracking in React's useEffect
.
We should really stress this point as from what I read about problems with hooks, people really fall a lot for these kind of unwritten / hidden rules - which is why React also did an eslint plugin to keep people from shooting themselves in the foot.
We don't really need that, do we?
OTOH, The point about referencing hook refs from within computed etc. might be our version of such footguns.
from core.
Yes, this proposal should work more along the lines of typical JavaScript intuitions than React hooks.
Re refs: once a ref is exposed to the instance they just become normal properties.
For the sake of discussion, let's call it "Vue blocks" (to differentiate from React hooks and lifecycle hooks)... we extract and encapsulate logic inside a block, and then expose it to the instance. Let's call it "the block side" and "the instance side":
import { value } from 'vue'
funciton useCustomBlock() {
// the block side
return value(0)
}
export default {
created() {
return {
// expose a ref to the instance (becomes a normal root-level property)
count: useCustomBlock()
}
},
computed: {
countPlusOne() {
// the instance side
// no need to use .value
return this.count + 1
}
}
}
You will only create and use refs on the blocks side, because there is not a persistent this
context to reference values that will change over time. But once refs are bound to an instance, there's no longer the need for them to be refs, so they can just work like normal properties.
from core.
@yyx990803 I might have some ideas for how we could build on top of current mixins instead and avoid adding much new API or concepts, but first I want to make sure I'm understanding the full advantages this syntax provides. The ones I've seen are:
- We're explicit about what's exposed to the component rather than forcing users to jump through hoops to make some properties private.
- Components explicitly define what's added to the instance from a mixin, rather than this information being invisible.
- It's possible to mix in behavior after the component is created, e.g. using data from props like in the
useFriendStatus
example.
Is that everything, or are there other advantages/goals?
from core.
This has been split into two separate RFCs: #24 & #25
from core.
Related Issues (20)
- SFC playground crashed after renaming a file HOT 1
- On hot reload: "VueCompilerError: Element is missing end tag." HOT 1
- `toRaw(attrs)` is not a plain object anymore in ^3.4.22 HOT 8
- 中文文档官网搜索不好用 HOT 2
- Putting a ref inside a ref should unwrap it, but its possible to trick the build system so it still thinks its a ref. HOT 1
- Activated lifecycle hook not triggered in async context HOT 5
- 组件的字面量prop的监听被意外触发 HOT 3
- Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. HOT 5
- A type for a vnode as the first argument of h() is missing
- Unhandled error during execution of scheduler flush. This is likely a Vue internals bug. HOT 3
- [HMR] Something went wrong during Vue component hot-reload. Full reload required. HOT 8
- A render error when using the <transition> and setting mode='out-in/in-out'.(The same problem exists at vue2) HOT 1
- Problems with using Transition and KeepAlive at the same time
- Failed to execute 'observe' on 'ResizeObserver' HOT 1
- 读取某一篇md格式的文档报错
- Use ref<T> with recursive type will cause ts(2589):Type instantiation is excessively deep and possibly infinite. HOT 1
- defineOptions type regression in v3.5.0-alpha.1 HOT 1
- props typings regression in v3.5.0-alpha.1 HOT 1
- inline `defineComponent` props types are missing
- after upgrade to 3.4.26, got error TS2589: Type instantiation is excessively deep and possibly infinite HOT 4
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.