Skip to content

Commit c9fa37c

Browse files
committed
docs: updates
1 parent 278916b commit c9fa37c

File tree

10 files changed

+114
-69
lines changed

10 files changed

+114
-69
lines changed

.vscode/settings.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"cSpell.words": [
3+
"vuefire",
4+
"Vuex",
5+
"Vuexfire"
6+
]
7+
}

docs/.vitepress/config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ function sidebarCookbook(): SidebarGroup {
200200
text: 'Cookbook',
201201
items: [
202202
{
203-
text: 'Cookbook',
203+
text: 'Cookbook Index',
204204
link: '/cookbook/',
205205
},
206206
{

docs/cookbook/index.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
# What is the Cookbook
22

3-
Here are some recipes that you may find useful. If you find a usecase that is missing, you can check if it isn't yet on [the roadmap](https://github.com/vuejs/vuefire/issues/145) or open an issue. You could also directly open a Pull request, but I recommend you to open an issue first so you don't end up wasting your time writing something that won't get published.
3+
Here are some recipes that you may find useful. If you find a use-case that is missing, you can check if it isn't yet on [the roadmap](https://github.com/vuejs/vuefire/issues/145) or open an issue. You could also directly open a Pull request, but I recommend you to open an issue first so you don't end up wasting your time writing something that won't get published.
44

55
## Recipes
66

7-
- [Prototyping](./prototyping.md)
8-
- [Using Firebase Database and Firestore together](./rtdb-and-firestore.md)
7+
- [Migration from VueFire 2](./migration-v2-v3.md)
98
- [Vuex](./vuex.md)
9+
- [Binding to existing refs](./subscriptions-external.md)
1010

1111
## Useful links
1212

docs/cookbook/subscriptions-external.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ When passing a target ref, the composable will not create a new `ref()` for you,
1111

1212
## Pinia
1313

14-
If you are using [Pinia](https://pinia.vuejs.org), you can directly use the `useCollection` function within [setup stores](https://pinia.vuejs.org/cookbook/composables.html#setup-stores):
14+
If you are using [Pinia](https://pinia.vuejs.org), you can directly use the `useCollection()` function within [setup stores](https://pinia.vuejs.org/cookbook/composables.html#setup-stores):
1515

1616
```ts
1717
import { defineStore } from 'pinia'

docs/guide/auth.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,17 @@ app.use(VueFire, {
1818
})
1919
```
2020

21-
This will automatically create and inject the [Auth module](https://firebase.google.com/docs/auth/web/start#add-initialize-sdk) from Firebase so you can access it from within any component with the `useFirebaseAuth()` composable.
21+
This will automatically initialize and inject the [Auth module](https://firebase.google.com/docs/auth/web/start#add-initialize-sdk) as well as the other features described in this page.
22+
23+
## Auth instance
24+
25+
You can access the current Auth instance in any component with the `useFirebaseAuth()` composable:
26+
27+
```vue
28+
<script setup>
29+
const auth = useFirebaseAuth()
30+
</script>
31+
```
2232

2333
## Current User
2434

docs/guide/global-options.md

+12-9
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,35 @@ globalFirestoreOptions.converter = ...
1818

1919
</FirebaseExample>
2020

21-
Changing these options will affect all calls to `useDocument()`, `useDatabaseObject()`, ... in your application **and the Options API usage** as well (`$firestoreBind()`, `$rtdbBind()`).
21+
Changing these options will affect **all calls** to `useDocument()`, `useDatabaseObject()`, ... in your application **as well as Options API calls** (`$firestoreBind()`, `$rtdbBind()`).
2222

23-
In both scenarios, **you need to make sure the returned objects contain their original `id`** so other VueFire functionalities can work correctly. The easies way to do this is by reusing the default `serialize`/`converter`:
23+
## Custom `serialize`/`converter`
24+
25+
When adapting `serialize`/`converter` or using `.withConverter()`, **you need to make sure the returned objects contain their original `id`** so other VueFire functionalities can work correctly. The easies way to do this is by reusing the default `serialize`/`converter`:
2426

2527
<FirebaseExample>
2628

2729
```ts
28-
import { globalDatabaseOptions } from 'vuefire'
30+
import { databaseDefaultSerializer } from 'vuefire'
2931

30-
const defaultSerialize = globalDatabaseOptions.serialize
3132
globalDatabaseOptions.serialize = (snapshot) => {
32-
const data = defaultSerialize(snapshot)
33+
const data = databaseDefaultSerializer(snapshot)
3334
// add anything custom to the returned object
3435
data.metadata = snapshot.metadata
3536
return data
3637
}
3738
```
3839

3940
```ts
40-
import { globalFirestoreOptions } from 'vuefire'
41+
import { firestoreDefaultConverter } from 'vuefire'
4142

42-
const defaultConverter = globalFirestoreOptions.converter
4343
globalFirestoreOptions.converter = {
44-
toFirestore: defaultConverter.toFirestore,
44+
// the default converter just returns the data: (data) => data
45+
toFirestore: firestoreDefaultConverter.toFirestore,
4546
fromFirestore: (snapshot, options) => {
46-
const data = defaultConverter.fromFirestore(snapshot, options)
47+
const data = firestoreDefaultConverter.fromFirestore(snapshot, options)
48+
// if the document doesn't exist, return null
49+
if (!data) return null
4750
// add anything custom to the returned object
4851
data.metadata = snapshot.metadata
4952
return data

docs/guide/options-api-realtime-data.md

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Options API Realtime Data
22

33
::: tip
4-
This pages assumes you have read the [Realtime Data](./realtime-data.md) page and will only cover the syntax differences between the Options API and the Composition API.
4+
This pages assumes you have read the [Realtime Data](./realtime-data.md) page and only covers the syntax differences between the Options API and the Composition API.
55
:::
66

77
There are two ways of using Realtime Data with VueFire:
@@ -28,18 +28,18 @@ app.use(VueFire, {
2828
})
2929
```
3030

31-
You can pass global options to the modules but note **these options are limited to the Options API usage**.They do not affect composition API calls such as `useDocument()` and `useDatabaseObject()`. [Check the global options](./global-options.md) to see how you can override those.
31+
You can pass global options to the modules but note **these options only affect the Options API usage**. They do not affect composition API calls such as `useDocument()` and `useDatabaseObject()`. [Check the global options](./global-options.md) to see how you can override those.
3232

3333
```ts
3434
app.use(VueFire, {
3535
modules: [
3636
VueFireFirestoreOptionsAPI({
37-
// same behavior as vuefire v2
37+
// this would be the same behavior as VueFire v2
3838
reset: true,
3939
wait: false,
4040
}),
4141
VueFireDatabaseOptionsAPI({
42-
// same behavior as vuefire v2
42+
// this would be the same behavior as VueFire v2
4343
reset: true,
4444
wait: false,
4545
}),
@@ -49,7 +49,7 @@ app.use(VueFire, {
4949

5050
## Declarative binding
5151

52-
Any Database Reference provided in a `firebase`/`firestore` option will be bound at creation (after Vue's `beforeMount` hook) to the specified key on the component. In the following example we bind a Collection of Documents to our `documents` property. The key provided in the `firebase`/`firestore` option (`documents`) must be initialized in the `data` of the component:
52+
Any Database Reference provided in a `firebase`/`firestore` option will be bound at creation (after Vue's `beforeMount` hook) to the specified key on the component. In the following example we bind a _collection_ of Documents to our `documents` property. The key provided in the `firebase`/`firestore` option (`documents`) must be initialized in the `data` of the component:
5353

5454
<FirebaseExample>
5555

@@ -60,6 +60,7 @@ import { ref as dbRef } from 'firebase/database'
6060
export default {
6161
data() {
6262
return {
63+
// must be an empty array to be bound as a list
6364
documents: [],
6465
}
6566
},
@@ -90,7 +91,7 @@ export default {
9091
</FirebaseExample>
9192

9293
::: warning
93-
You must declare properties with their initial values in `data`. **For the RTDB, using an _Array_ as the initial value will bind the Reference as an array, otherwise it is bound as an object**. For Firestore, collections and queries are bound as arrays while documents are bound as objects.
94+
You must declare properties with their initial values in `data`. **For Firebase Database, using an _Array_ as the initial value will bind the Reference as an array, otherwise it will be bound as an object**. For Firestore, collections and queries are bound as arrays while documents are bound as objects.
9495
:::
9596

9697
## Programmatic binding
@@ -189,7 +190,7 @@ this.$firestoreBind('documents', query(documents, where('creator', '==', this.id
189190
190191
## Unbinding / Unsubscribing to changes
191192
192-
While VueFire will automatically unbind any reference bound in a component whenever needed, you may still want to do it on your own to stop displaying updates on a document or collection or because the user logged out and they do not have read-access to a resource anymore.
193+
While VueFire will automatically unbind any reference bound in a component whenever needed, you may still want to do it on your own to stop displaying updates on a document or collection or because the user logged out and they do not have the permissions anymore.
193194
194195
<FirebaseExample>
195196
@@ -227,7 +228,7 @@ this.$databaseUnbind('user', () => ({ name: 'unregistered' }))
227228
// this.user === { name: 'unregistered' }
228229

229230
// for references bound as arrays, they are reset to an empty array by default instead of `null`
230-
this.$databaseUnbind('documents')
231+
this.$databaseUnbind('documents', true)
231232
// this.documents === []
232233
```
233234

docs/guide/realtime-data.md

+41-18
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,43 @@ const someTodo = useDocument(doc(collection(db, 'todos'), 'someId'))
5252

5353
</FirebaseExample>
5454

55-
These composables all return a Vue `Ref` of the data. Note **this is a readonly data**, you shouldn't mutate it directly, [use the Firebase SDK](./writing-data.md) instead. It will be automatically updated when the data changes anywhere.
55+
These composables all a Vue `Ref` containing the data. Note **this is a readonly data**, you shouldn't mutate it directly, you should instead [use the Firebase SDK](./writing-data.md). VueFire will automatically keep the data in sync with the database.
5656

57-
Sometimes, you need to change the document you are observing, let's say you have a list of contacts and that you display one based on the URL, you handle this by passing a reactive variable of the data source to the `useDocument()`, `useDatabaseObject()`, etc composables:
57+
Sometimes, you need to start observing a different document or collection, let's say you have a _collection_ of contacts and that you display a specific contact based on the URL, e.g. displaying the contact with an id equal to `24` on `/contacts/24`, you can achieve this this by passing a _reactive variable of the data source_ to the `useDocument()`, `useDatabaseObject()`, etc composables:
58+
59+
<FirebaseExample>
60+
61+
```ts
62+
const route = useRoute()
63+
// since route is reactive, `contactSource` will be reactive too
64+
const contactSource = computed(
65+
() => dbRef(db, 'contacts/' + route.params.id)
66+
)
67+
// contact will always be in sync with the data source
68+
const contact = useDatabaseObject(contactSource)
69+
```
5870

5971
```ts
6072
const route = useRoute()
73+
// since route is reactive, `contactSource` will be reactive too
6174
const contactSource = computed(
6275
() => doc(collection(db, 'contacts'), route.params.id)
6376
)
64-
// contact will always be
77+
// contact will always be in sync with the data source
6578
const contact = useDocument(contactSource)
6679
```
6780

68-
This way, if the route changes, the document will be updated to the new one, automatically unsubscribing from the previous one and subscribing to the new one.
81+
</FirebaseExample>
82+
83+
This way, when the route changes, the document will be updated to the new one, automatically unsubscribing from the previous one and subscribing to the new one.
6984

7085
::: tip
71-
If you can't use a `computed()`, use `shallowRef()`s instead of `ref()`s to store the data sources. This is because `shallowRef()` doesn't try to recursively observe the object it's given, which in the case of a Firebase data source, would be wasteful.
86+
If you can't use a `computed()`, use `shallowRef()`s instead of `ref()`s to store the data sources. This is because `shallowRef()` doesn't try to recursively observe the object it's given, which in the case of a Firebase data source, would be worse in terms of performance.
7287
:::
7388

7489
### Subscription state
7590

76-
All of the composables not only return a `Ref`, they can also be destructured to access other useful data like _is the initial load still pending?_ or _did the subscription fail?_. You only need to destructure the returned value from the composables:
91+
All of the composables can also be destructured to access other useful data like _is the initial load still pending?_ or _did the subscription fail?_. You only need to destructure the returned value from the composables:
7792

7893
```ts
7994
// instead of writing
@@ -91,19 +106,19 @@ const {
91106
} = useDocument(contactSource)
92107
```
93108

94-
Notice how we rename `data` to whatever makes more sense for the context. It's important to note
109+
Notice how we rename `data` to whatever makes more sense for the context.
95110

96111
::: warning
97-
All of the properties that can be defined on the Ref are defined as [non-enumerable properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) which means they won't be copied over when using the spread operator e.g. `const { data, ...rest } = useDocument(contactSource)`. This is to ensure they are completely ignored in other places like devtools.
112+
All of the properties that can be defined on the `Ref` are defined as [non-enumerable properties](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) which means they won't be copied over when using the spread operator e.g. `const { data, ...rest } = useDocument(contactSource)`. This is to ensure they are completely ignored and do not cerate problems in other places like devtools.
98113
:::
99114

100-
## VueFire extras
115+
## VueFire additions
101116

102117
VueFire adds a few properties to the data snapshot to make it easier to work with.
103118

104119
### Document's `id`
105120

106-
Each document/object, has an `id` property that is the key of the document/object. This is useful when you want to display the id of the document/object in your template. It's set as a non enumerable property so it won't be copied over when using the spread operator.
121+
Each document/object, has an convenient `id` property that is the id/key of the document/object. It's set as a non enumerable property so it won't be copied over when using the spread operator.
107122

108123
<FirebaseExample>
109124

@@ -129,6 +144,8 @@ doc(collection(db, 'users'), 'jORwjIykFn1NmkdzTkhU').id // 'jORwjIykFn1NmkdzTkhU
129144

130145
</FirebaseExample>
131146

147+
This behavior can be customized through the [`serialize`/`converter` option](./global-options.md#custom-serializeconverter). Note that in both cases, **you must keep the `id` property for VueFire to correctly work**.
148+
132149
### GeoPoints (Firestore only)
133150

134151
In Firestore you can store [GeoPoints](https://firebase.google.com/docs/reference/js/firestore_.geopoint). They are retrieved as-is by VueFire, meaning that you can directly use methods like `isEqual` and access its properties `latitude` and `longitude`.
@@ -152,7 +169,7 @@ await addDoc(collection(db, 'cities'), {
152169
// somewhere else...
153170
// we consider `cities` to be the result af `useCollection(collection(db, 'cities'))`
154171
// we retrieve Paris that was just added
155-
const paris = cities.value[cities.value.length - 1]
172+
const paris = cities.value.at(-1)
156173
paris.location.latitude // 48.8588377
157174
paris.location.longitude // 2.2770206
158175
```
@@ -182,7 +199,7 @@ await addDoc(collection(db, 'events'), {
182199
// somewhere else...
183200
// we consider `events` to be the result af `useCollection(collection(db, 'events'))`
184201
// we retrieve the event we just added
185-
const prise = events.value[events.value.length - 1]
202+
const prise = events.value.at(-1)
186203
prise.date.seconds // -5694969600
187204
prise.date.nanoseconds // 0
188205
prise.toDate() // Tue Jul 14 1789
@@ -277,7 +294,7 @@ const numberList = useDatabaseList(numbersRef)
277294

278295
## TypeScript
279296

280-
Usually, the different composables accept a generic to enforce the type of the documents:
297+
To enforce a type, you only need to pass a generic type when using the different composables functions:
281298

282299
<FirebaseExample>
283300

@@ -288,12 +305,14 @@ const settings = useDatabaseObject<Settings>(dbRef(db, 'settings/someId'))
288305

289306
```ts
290307
const contacts = useCollection<Contact>(collection(db, 'contacts'))
291-
const settings = useDocument<Settings>(doc(collection(db, 'settings'), 'someId'))
308+
const settings = useDocument<Settings>(
309+
doc(collection(db, 'settings'), 'someId')
310+
)
292311
```
293312

294313
</FirebaseExample>
295314

296-
Note this is only a type annotation, it does not perform any runtime validation.
315+
Note this is only a type annotation, it does not perform any runtime validation. If you want a runtime validation, you can use the `withConverter()` method as shown below.
297316

298317
### Firestore `.withConverter()`
299318

@@ -304,6 +323,8 @@ The recommended Firebase approach is to use the `withConverter()` for Firestore:
304323
:::
305324

306325
```ts
326+
import { firestoreDefaultConverter } from 'vuefire'
327+
307328
interface TodoI {
308329
text: string
309330
finished: boolean
@@ -312,11 +333,13 @@ interface TodoI {
312333
const todoList = useDocument(
313334
doc(db, 'todos').withConverter<TodoI>({
314335
fromFirestore: (snapshot) => {
315-
const data = snapshot.data()
336+
const data = firestoreDefaultConverter.fromFirestore(snapshot)
316337
// usually you can do data validation here
317-
return { text: data.text, finished: data.finished }
338+
if (!data || !isValidTodoItem(data)) return null
339+
340+
return data
318341
},
319-
toFirestore: (todo) => todo,
342+
toFirestore: firestoreDefaultConverter.toFirestore,
320343
})
321344
)
322345
```

docs/guide/ssr.md

+22-22
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,27 @@ Web Security is a broad topic that we cannot cover here. We recommend you to rea
9898
- [State Serialization in vite-ssg](https://github.com/antfu/vite-ssg#state-serialization)
9999
- [SSR Best practices for Vue.js](https://vuejs.org/guide/best-practices/security.html#server-side-rendering-ssr)
100100

101+
## Manual SSR keys
102+
103+
VueFire automatically infers an SSR key based on the path of the document or collection whenever possible. This means there are some scenarios where **you have to provide a manual `ssrKey`**:
104+
105+
- When using Firestore Queries
106+
- When binding the same document multiple times
107+
108+
In these scenarios, provide the `ssrKey` as a second argument to `useDocument()`, `useCollection()`, etc:
109+
110+
<FirebaseExample>
111+
112+
```ts
113+
useDatabaseList(queryRef, { ssrKey: 'my-quiz' })
114+
```
115+
116+
```ts
117+
useCollection(queryRef, { ssrKey: 'my-quiz' })
118+
```
119+
120+
</FirebaseExample>
121+
101122
## Usage outside of components
102123

103124
If you are using VueFire composables outside of components, e.g. using `useDocument()` within a [Pinia](https://pinia.vuejs.org) store, you need to manually wait for the data to be loaded on the server as VueFire cannot call `onServerPrefetch()` for you and you will have to manually call it yourself. VueFire exposes a function to retrieve all pending promises created by the different composables (`useDocument()`, `useDatabaseObject()`, etc). You will need to use it inside of **any component that uses the data**:
@@ -115,7 +136,7 @@ onServerPrefetch(() => usePendingPromises())
115136
</script>
116137
```
117138

118-
While the recommended approach is to use `onServerPrefetch()`, aother possibility is to [use `<Suspense>`](https://vuejs.org/guide/built-ins/suspense.html#suspense) to be able to use `await` within `setup()`:
139+
While the recommended approach is to use `onServerPrefetch()`, another possibility is to [use `<Suspense>`](https://vuejs.org/guide/built-ins/suspense.html#suspense) to be able to use `await` within `setup()`:
119140

120141
```vue
121142
<script setup>
@@ -165,24 +186,3 @@ const { data: users } = useUserList()
165186
```
166187
167188
-->
168-
169-
## Manual SSR keys
170-
171-
VueFire automatically infers an SSR key based on the path of the document or collection whenever possible. This means there are some scenarios where **you have to provide a manual `ssrKey`**:
172-
173-
- When using Firestore Queries
174-
- When binding the same document multiple times
175-
176-
In these scenarios, provide the `ssrKey` as a second argument to `useDocument()`, `useCollection()`, etc:
177-
178-
<FirebaseExample>
179-
180-
```ts
181-
useDatabaseList(queryRef, { ssrKey: 'my-quiz' })
182-
```
183-
184-
```ts
185-
useCollection(queryRef, { ssrKey: 'my-quiz' })
186-
```
187-
188-
</FirebaseExample>

0 commit comments

Comments
 (0)