It lets us extract logic easily an not have to worry about the value of this
in our code.
It also works better with TypeScript because the value of this
no longer has to be typed.
In this article, we’ll look at how to create Vue 3 apps with the Composition API.
customRef
We can use the customRef
function to create a custom ref.
For instance, we can write:
<template>
<div>
<input v-model="text" />
{{ text }}
</div>
</template>
<script>
import { customRef } from "vue";
const useDebouncedRef = (value, delay = 200) => {
let timeout;
return customRef((track, trigger) => {
return {
get() {
track();
return value;
},
set(newValue) {
clearTimeout(timeout);
timeout = setTimeout(() => {
value = newValue;
trigger();
}, delay);
},
};
});
};
export default {
name: "App",
setup() {
return {
text: useDebouncedRef("hello world"),
};
},
};
</script>
We create the useDebounceRef
function that returns a custom ref created bu the customRef
function.
It takes a callback with the track
function to track the value.
And the setter calls trigger
to trigger state updates.
We ser the value
in the setter to newValue
so that the items are updated.
markRaw
We can call the markRaw
function to mark an object so that it’ll never be converted to a proxy.
For instance, we can write:
<template>
<div></div>
</template>
<script>
import { isReactive, markRaw, reactive } from "vue";
export default {
name: "App",
setup() {
const foo = markRaw({});
console.log(isReactive(reactive(foo)));
return { foo };
},
};
</script>
The console log logs false
since we called markRaw
to disable reactivity on the object we passed into it.
But if we mark one object as raw and we reference that object in another object, then they’ll be considered not to be equal even though they have the same reference:
<template>
<div></div>
</template>
<script>
import { isReactive, markRaw, reactive } from "vue";
export default {
name: "App",
setup() {
const foo = markRaw({
nested: {},
});
const bar = reactive({
nested: foo.nested,
});
console.log(foo.nested === bar.nested);
return { foo };
},
};
</script>
We set bar.nested
to foo.nested
, but the console log is false
.
shallowReactive
We can create a shallow reactive object with the shallowReactive
function.
The reactive property is limited to the top-level properties of the object.
For instance, we can write:
<template>
<div>
<button @click="increment">increment</button>
{{ state.foo }}
{{ state.nested.bar }}
</div>
</template>
<script>
import { isReactive, shallowReactive } from "vue";
export default {
name: "App",
setup() {
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
});
const increment = () => {
state.foo++;
state.nested.bar++;
};
console.log(isReactive(state.nested.bar));
return { state, increment };
},
};
</script>
In the increment
function, we increment both state.foo
and state.nested.bar
and they’ll both update in the template.
But when we log state.nested.bar
with isReactive
, we get false
logged.
Since it’s not reactive, it won’t trigger watchers to run:
<template>
<div>
<button @click="increment">increment</button>
{{ state.foo }}
{{ state.nested.bar }}
</div>
</template>
<script>
import { isReactive, shallowReactive, watch } from "vue";
export default {
name: "App",
setup() {
const state = shallowReactive({
foo: 1,
nested: {
bar: 2,
},
});
const increment = () => {
state.foo++;
state.nested.bar++;
};
watch(state.nested.bar, (bar) => {
console.log(bar);
});
return { state, increment };
},
};
</script>
Then watch
callback in the 2nd argument won’t run when state.nested.bar
is updated.
Conclusion
We can use various functions provided Vue 3’s composition API to create various kinds of reactive and non-reactive properties.