https://stackblitz.com/edit/jfunky?file=index.html,jfunky.ts,entry.ts,README.md
async function getPageData () { | |
const users = await get('/users') | |
const user1 = await get('/users/1') | |
const usersWithParams = await get('/users?limit=10') | |
const user1WithParams = await get('/users/1?hello=world') | |
return { | |
users, | |
user1, | |
usersWithParams, |
import { reactive, effectScope, watchEffect } from 'vue' | |
function switchboard(value) { | |
let lookup = {} | |
let scope = effectScope() | |
let current | |
let get = () => current |
Great question I got on Twitter: what are the benefits of returning a function ref from a Vue 3 composable, instead of returning the ref object directly?
Here are two SFC playgrounds to outline the two alternatives:
- function ref
- [ref object](https://sfc.vuejs.org/#eNp9UsFugzAM/ZUol3ZSm9wrqLQPmCbtnEtLTQciTuQENgnx73OAIsamHpCw/d7Dz49evnqvuhbkSWahoMpHESC2/mywst5RFL0gKMUgSnJW7Bi6W43aAB9Qvl9rKOKCUXrdVjEww2DhMCQKNGABEzr/Rd+/GMz0tAN/nYsI1jeXCFwJkd2qLm2SGzkrGHnONHd5nOkFKw9y2u5oL17VwSFb65OCmQfByJMYO6nHhlJt5GeMPpy0DmWRDlIH5eiu+U1Ri7GyoCDY45XcVwBiYSMPKw3Nz
Double-check all the Vue best practices that are easiest to forget when you’re focused on design and business logic, but hardest to catch with automated tooling.
The answer to every question should be “yes”, and you should aim to spend 10-15 minutes on the entire list after you’ve already had a chance to read and understand the code.
- Is reactive state created & managed where it's needed, instead of further up the component or composable tree?
- Is prop drilling avoided, within reason?
- Is state derived with
computed
when possible, instead of synced?
- Is reactive state created & managed where it's needed, instead of further up the component tree?
- Is prop drilling avoided, within reason? Further reading
- Is state derived when possible, instead of synced? Further reading
- Are persistent, non-reactive pieces of state (e.g. instances of third party libraries, resize/intersection/mutation observer instances, etc.) stored with
useRef
?
- Have iterations been avoided or simplified as often as possible for performance and/or readability?
- Are
.reduce()
callbacks reasonably readable?
Vue 3 effect timing API | React equivalent |
---|---|
setup |
None |
onMounted |
useEffect with an empty dependency list |
onUpdated |
useEffect |
onUnmounted |
Callback returned from useEffect with an empty dependency list |
onBeforeMount |
None |
onBeforeUpdate |
Render function, sort of |
onBeforeUnmount |
None |
onErrorCaptured |
None |
Vue 3's fantastic reactivity system and component update cycle almost always protects you from triggering infinite render loops: never-ending reactive updates that cause your app to rapidly re-render until it crashes.
There are ways to make infinite render loops happen though! Let's study a few different pitfalls, so we can be prepared to debug our apps if we ever see one of the following errors:
Maximum recursive updates exceeded
too much recursion
I did a screencast series recently talking about my favorite Vue 3 feature: function refs. Check out the screencast series for more context-in this gist, I'm going to answer a fantastic question I got in one of the videos' comment section.
This question was about how to properly update arrays of elements that are captured by function refs. Let's check out the basic principle first, before we look at the details of the question.
<script setup>
import { ref, onBeforeUpdate } from 'vue'
_TL;DR: You can use arrow functions that return slot function calls to render scoped slots in the render function returned by setup
. [Here's an SFC playground](https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHNjcmlwdD5cbmltcG9ydCB7IHJlZiB9IGZyb20gJ3Z1ZSdcblxuY29uc3QgV2l0aFJlbmRlckZuID0ge1xuXHRzZXR1cCAocHJvcHMsIHsgc2xvdHMgfSkge1xuICAgIGNvbnN0IGNvdW50ID0gcmVmKDApXG4gICAgXG4gICAgcmV0dXJuICgpID0+IHNsb3RzLmRlZmF1bHQoe1xuICAgICAgY291bnQ6IGNvdW50LnZhbHVlLFxuICAgICAgb25DbGljazogKCkgPT4gY291bnQudmFsdWUrKyxcbiAgICB9KVxuICB9XG59XG5cbmV4cG9ydCBkZWZhdWx0IHtcbiAgY29tcG9uZW50czoge1xuICAgIFdpdGhSZW5kZXJGbixcbiAgfVxufVxuPC9zY3JpcHQ+XG5cbjx0ZW1wbGF0ZT5cbiAgPFdpdGhSZW5kZXJGbiB2LXNsb3Q9XCJ7IGNvdW50LCBvbkNsaWNrIH1cIj5cblx0ICA8ZGl2IHN0eWxlPVwiZGlzcGxheTogZmxleDsgZ2FwOiAxcmVtO1wiPlxuXHRcdCAgPHNwYW4+Y291bnQgaXM6IHt7IGNvdW50IH19PC9zcGFuPlxuICBcdCAgPGJ1dHRvbiA6b25DbGljaz1cIm9uQ2xpY2tcIj5cbiAgICBcdCAgQ291bnQgdXBcbiAgICBcdDwvYnV0dG9uPlxuXHQgIDwvZGl2PlxuICA8L1dpdGhSZW5kZXJGbj5cbjwvdGVtcGxhdGU+Iiwi