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.
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.
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.
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.
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.
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 }),
});
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.
<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.
<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>.