Pular para o conteúdo principal

DataClientPlugin

Plugin do Vue que cria o store e o Controller e os disponibiliza para todos os componentes do app. Instale-o uma única vez, antes de app.mount(); os composables só funcionam em componentes de um app no qual ele esteja instalado.

main.ts
import { createApp } from 'vue';
import { DataClientPlugin } from '@data-client/vue';
import App from './App.vue';

const app = createApp(App);
app.use(DataClientPlugin);
app.mount('#app');

Os Managers iniciam quando o plugin é instalado e param quando o app é desmontado.

Opções​

app.use(DataClientPlugin, options);
interface ProvideOptions {
managers?: Manager[];
initialState?: State<unknown>;
Controller?: new (props: { gcPolicy: GCInterface }) => Controller;
gcPolicy?: GCInterface;
}

managers?: Manager[]​

Lista de Managers a usar. Este é o principal ponto de extensibilidade do store.

O padrão é getDefaultManagers(), que também pode ser usado para estender os padrões.

main.ts
import { createApp } from 'vue';
import { DataClientPlugin, getDefaultManagers } from '@data-client/vue';
import App from './App.vue';
import MyManager from './MyManager';

const app = createApp(App);
app.use(DataClientPlugin, {
managers: [...getDefaultManagers(), new MyManager()],
});

Padrão em produção:

[new NetworkManager(), new SubscriptionManager(PollingSubscription)];

Padrão em desenvolvimento:

[
new DevToolsManager(),
new NetworkManager(),
new SubscriptionManager(PollingSubscription),
];

initialState?: State<unknown>​

Em vez de começar com um cache vazio, você pode fornecer seu próprio estado inicial. Isso pode ser útil para testes ou para reidratar o estado do cache ao usar renderização no servidor. mockInitialState() cria um a partir de fixtures.

main.ts
app.use(DataClientPlugin, { initialState: window.__INITIAL_STATE__ });
export interface State<T> {
readonly entities: {
readonly [entityKey: string]: { readonly [pk: string]: T } | undefined;
};
readonly endpoints: {
readonly [key: string]: unknown | PK[] | PK | undefined;
};
readonly indexes: NormalizedIndex;
readonly meta: {
readonly [key: string]: {
readonly date: number;
readonly fetchedAt: number;
readonly expiresAt: number;
readonly prevExpiresAt?: number;
readonly error?: ErrorTypes;
readonly invalidated?: boolean;
readonly errorPolicy?: 'hard' | 'soft' | undefined;
};
};
readonly entitiesMeta: {
readonly [entityKey: string]: {
readonly [pk: string]: {
readonly date: number;
readonly expiresAt: number;
readonly fetchedAt: number;
};
};
};
readonly optimistic: (SetResponseAction | OptimisticAction)[];
readonly lastReset: number;
}

Controller?: classe Controller​

Isso permite estender o Controller para fornecer funcionalidades adicionais. Pode ser útil se você tiver ações adicionais que deseja despachar para Managers personalizados.

main.ts
import { createApp } from 'vue';
import { Controller, DataClientPlugin } from '@data-client/vue';
import App from './App.vue';

export class MyController extends Controller {
doSomething = () => {
console.log('hi');
};
}

const app = createApp(App);
app.use(DataClientPlugin, { Controller: MyController });

useController() e $dataClient passam então a retornar uma instância de MyController, mas continuam tipados como Controller. Faça um cast para acessar os membros adicionados:

import { useController } from '@data-client/vue';
import type { MyController } from './main';

const ctrl = useController() as MyController;
ctrl.doSomething();

gcPolicy?: GCInterface​

Remove dados do store quando nenhum componente os usa e eles ficaram desatualizados. O padrão é new GCPolicy(); passe uma instância para mudar a frequência da varredura ou por quanto tempo os dados não utilizados são mantidos.

main.ts
import { createApp } from 'vue';
import { DataClientPlugin, GCPolicy } from '@data-client/vue';
import App from './App.vue';

const app = createApp(App);
app.use(DataClientPlugin, {
// sweep every 10 minutes
gcPolicy: new GCPolicy({ intervalMS: 60 * 1000 * 10 }),
});
GCPolicy options
new GCPolicy({
// how often to sweep (default 5 minutes)
intervalMS: 60 * 1000 * 5,
// how many stale lifetimes before data is removed (default 2)
expiryMultiplier: 2,
// or choose when unused data is removed (replaces expiryMultiplier)
// here: one minute after it goes stale
expiresAt: ({ expiresAt }) => expiresAt + 60 * 1000,
});

$dataClient​

O plugin também adiciona o Controller como a propriedade global $dataClient, de modo que templates e componentes da Options API (como this.$dataClient) possam usá-lo sem useController(). Ele é tipado como Controller sem nenhuma configuração extra.

DeleteTodo.vue
<script setup lang="ts">
import { TodoResource } from '@/resources/Todo';

defineProps<{ id: number }>();
</script>

<template>
<button @click="$dataClient.fetch(TodoResource.delete, { id })">
Delete
</button>
</template>

Usando composables​

Composables como useSuspense() precisam ser executados durante o setup de um componente, para que o Vue saiba qual store de qual app usar. Aguardá-los exige <script setup>: em um async setup() escrito à mão, composables chamados depois do primeiro await perdem a instância do componente e lançam um erro.

TodoDetail.vue
<script setup lang="ts">
import { useSuspense } from '@data-client/vue';
import { TodoResource } from '@/resources/Todo';
import { UserResource } from '@/resources/User';

const todo = await useSuspense(TodoResource.get, { id: 1 });
// still works after the await
const user = await useSuspense(UserResource.get, {
id: todo.value.userId,
});
</script>

Componentes que usam await devem ser renderizados dentro de um boundary <Suspense>.