126 Commits

Author SHA1 Message Date
Guillaume Chau fb027e6adf v4.2.2
Publish Nightlies / build (push) Has been cancelled
E2E composable / Build and test (push) Has been cancelled
E2E options/components / Build and test (push) Has been cancelled
E2E composable + SSR / Build and test (push) Has been cancelled
Main continuous tests / Build and test (push) Has been cancelled
2025-03-11 14:31:15 +01:00
Guillaume Chau 6bfd0ec337 ci: add name to pr title job 2025-03-11 14:27:02 +01:00
Guillaume Chau d99865fd22 ci: rename job 2025-03-11 14:25:29 +01:00
Guillaume Chau df96345bf9 ci: fix cypress 2025-03-11 14:23:08 +01:00
Guillaume Chau 35fb59c57c ci: update upload artifact 2025-03-08 21:22:59 +01:00
Guillaume Chau 99ca23b11c test(lint): switch to eslint 9 and antfu config 2025-03-08 21:12:47 +01:00
Guillaume Chau f2578cbbe5 ci: update gh actions to use corepack 2025-03-07 16:10:44 +01:00
Jeroen de Jong 22f6797c34 fix: useMutations onError Event hook gets triggered too early (#1585)
similar to #1558, except for onError
2025-03-07 16:08:04 +01:00
Guillaume Chau d42188786c chore: update deps + jest to vitest 2025-03-07 16:06:16 +01:00
Mark Florian 7a48882637 docs: fix $skipAll mention (#1573)
The "Special options" documentation mentions `$skip` as a special
option, but that isn't correct. It should be `$skipAll`.
2025-01-09 20:10:52 +01:00
ChatonDeAru (Romain) 365c95467a fix: augment vue rather than @vue/runtime-core (#1576)
This PR removes augmentations of `@vue/runtime-core` in favour of only augmenting `vue` (the new recommendation), which should fix issues when other packages are only augmenting vue (see nuxt/nuxt#28542).
2025-01-09 20:10:35 +01:00
Kristaps Fabians Geikins b57f2befe3 fix: memory leak in SSR caused by global tracking (#1582) 2025-01-09 20:10:11 +01:00
Guillaume Chau 8a5f424e2e v4.2.1 2024-08-23 11:42:25 +02:00
Nick Messing d329d4da69 fix: improved pinia support (#1571) 2024-08-23 11:32:59 +02:00
Guillaume Chau 7f3cf7d1d1 docs: use nightly.akryum.dev 2024-08-22 18:34:57 +02:00
Guillaume Chau 732e66e3e9 chore: specify pnpm version in package.json 2024-08-22 15:33:30 +02:00
Guillaume Chau ff836ea8b5 docs: readme smaller logo 2024-08-19 21:01:31 +02:00
Guillaume Chau 9622392013 docs: update broken circleci badge 2024-08-19 21:00:34 +02:00
Guillaume Chau 28f4265e39 v4.2.0 2024-08-19 20:57:19 +02:00
Guillaume Chau 51e09e7b71 docs: note about continuous releases 2024-08-19 20:50:52 +02:00
Guillaume Chau 319f6eca2d ci: nightly releases 2024-08-19 20:40:40 +02:00
Guillaume Chau d141ed6718 fix: resolveClient throwing too soon, fix #1557 (#1570) 2024-08-19 20:29:21 +02:00
Guillaume Chau c8e5106870 chore: switch some tests to script setup 2024-08-19 20:06:04 +02:00
Guillaume Chau 4679a04279 fix: reuse previous result, fix #1483 (#1569) 2024-08-19 20:04:58 +02:00
Matt Garrett fceb40cd2c fix: (@vue/apollo-option) memory leak in wrapped ssrRender (#1553) 2024-08-14 16:39:44 +02:00
mobsean f0ab70883e fix: useMutations onDone Event hook gets triggered too early (#1559) 2024-08-14 16:14:03 +02:00
Leonardo Santos d1098bee4f feat: add updateQuery to useQuery (#1552) 2024-08-14 16:12:58 +02:00
Alex Liu f2360701ba docs: add github link to documentation (#1549) 2024-08-14 16:05:46 +02:00
Guillaume Chau 68347a6812 v4.1.0 2024-08-14 16:05:27 +02:00
Guillaume Chau fe6684029d ci: update versions 2024-08-14 15:54:38 +02:00
Guillaume Chau 827ea6e7ae chore: updqte pnpm to v9 2024-08-14 15:54:25 +02:00
Nick Messing 29d722c5b4 docs(useQuery): document refetch with new variables (#1564) 2024-08-14 15:45:34 +02:00
Nick Messing 6fa46ab122 fix: change teardown to use onScopeDispose (#1545) 2024-03-29 10:03:06 +01:00
Nick Messing dbd1c8d236 test: fix subscription test (#1547) 2024-03-29 09:57:15 +01:00
Guillaume Chau 0f5ae610c5 test: useSubscription 2024-03-19 16:59:06 +01:00
Guillaume Chau 89a0240f0b v4.0.2 2024-03-08 11:56:15 +01:00
Guillaume Chau 08f0fcdeab fix: use shallowRef on result & error 2024-03-08 11:52:05 +01:00
Guillaume Chau fda4c9fb4a docs: remove mentions of fetchResults, fix #1060 2024-01-24 14:34:50 +01:00
Guillaume Chau 3de258d754 v4.0.1 2024-01-24 10:38:25 +01:00
Guillaume Chau 500d6e498a test: build test app in test command 2024-01-24 10:33:51 +01:00
Guillaume Chau 48d0ac2c32 test: split into outsideComponent.cy.ts 2024-01-24 10:24:58 +01:00
Arun Sathiya 4eb80fb9b2 ci: Use GITHUB_OUTPUT envvar instead of set-output command (#1530) 2024-01-24 10:19:43 +01:00
Guillaume Chau 330564c7f4 fix(useLazyQuery): load() on server, fix #1495 2024-01-24 10:12:02 +01:00
Dawid Kopys 460a0dbd11 fix: use hasInjectionContext in useApolloClient before calling inject (#1529)
Co-authored-by: Dawid Kopys <dawid.kopys@creativestyle.pl>
2024-01-24 09:41:26 +01:00
Guillaume Chau c795f25708 v4.0.0 2024-01-15 11:31:26 +01:00
Guillaume Chau 9be63fa8e8 chore: update sheep 2024-01-15 11:28:11 +01:00
Guillaume Chau 062f72a9b7 test: fragment 2024-01-15 10:55:52 +01:00
Guillaume Chau f8568e851e fix: prefetch type 2024-01-15 10:36:46 +01:00
Guillaume Chau ddf9aa038a fix(ApolloMutation): return result in mutate 2024-01-15 10:36:02 +01:00
Guillaume Chau b17817e9c9 fix(options): use exponential backoff on subscribe error retry 2024-01-15 10:26:13 +01:00
Dawid Kopys 72d4df5cce fix: import serializeJs using default import instead of a namespace import (#1485)
Co-authored-by: Dawid Kopys <dawid.kopys@creativestyle.pl>
2024-01-15 10:09:42 +01:00
Yury Savin 2746efa632 feat(useLazyQuery): add interface for lazy query return (#1523)
Co-authored-by: Guillaume Chau <guillaume.b.chau@gmail.com>
2024-01-15 10:04:12 +01:00
Guillaume Chau fb66dcebbb docs: update vitepress + enable detailed search by default 2024-01-15 09:57:04 +01:00
Guillaume Chau ca3f2f4f84 ci: update node and pnpm 2024-01-15 09:54:08 +01:00
Guillaume Chau 659ba90699 fix: improve esm support, fix #1524 2024-01-15 09:48:39 +01:00
Guillaume Chau dd01cee2e1 v4.0.0-beta.12 2023-11-29 15:02:26 +01:00
Guillaume Chau 0be5d9b444 feat: new context params in event hook handlers
Allow easy access to the client, especially useful with useSubscription().
2023-11-29 14:58:09 +01:00
Hassan c9b648d4bd docs: import createApolloProvider is missing (#1515) 2023-11-29 14:38:51 +01:00
mekraldi 9d8cc79ae9 docs: Added missing createClient import in example when creating Graph… (#1513) 2023-11-29 14:38:37 +01:00
mekraldi d953688119 docs: missing curly brace (#1512) 2023-11-29 14:38:19 +01:00
Guillaume Chau 76f19f6e0c fix: use shallowRef for apollo query 2023-11-29 14:37:33 +01:00
Vitaliy 21fca98911 chore: moved resolutions to the root of the workspace (#1508) 2023-09-14 01:02:55 +02:00
Guillaume Chau d1f732cf79 v4.0.0-beta.11 2023-09-13 11:59:04 +02:00
Guillaume Chau 23c0c98231 fix: remove console.log, console log remained in code #1507 2023-09-13 11:56:38 +02:00
Guillaume Chau f545763fb9 docs: update vitepress + fix components API menu 2023-09-13 11:04:12 +02:00
Guillaume Chau 68addf80a0 docs: Update README.md logo 2023-09-12 17:31:00 +02:00
Guillaume Chau 10d8de36c6 v4.0.0-beta.10 2023-09-12 16:59:52 +02:00
Guillaume Chau 995131d48b chore: seq test 2023-09-12 16:58:05 +02:00
Guillaume Chau 96cc4fe78a feat(useLazyQuery): load returns Promise, fix #1486 2023-09-12 16:52:13 +02:00
Guillaume Chau daffd75db2 chore: upgrade vitepress to 1.0 RC 2023-09-12 16:31:06 +02:00
Guillaume Chau f5e371f5e6 feat: support effect scope outside of component, fix #1505 2023-09-12 15:31:46 +02:00
Guillaume Chau 574bd8f2b1 test: ssr 2023-09-12 11:34:12 +02:00
Guillaume Chau 1ac1372dff test(lint): fix 2023-09-12 10:13:49 +02:00
Viktor 7ed4884c76 chore: package test-e2e-composable-vue3, update deps, migrate to vite (#1488)
Co-authored-by: Guillaume Chau <guillaume.b.chau@gmail.com>
2023-09-12 10:04:56 +02:00
Vitaliy b0844aca91 types: Extended "enabled" option type (#1492) 2023-09-12 09:36:16 +02:00
forgottencsc ca0bd28f4b fix: apollo components should have emits (#1504) 2023-09-12 09:29:09 +02:00
Guillaume Chau 6f2cf309b9 v4.0.0-beta.9 2023-08-29 00:18:30 +02:00
Guillaume Chau f47759e5d9 chore: update deps 2023-08-29 00:06:42 +02:00
Guillaume Chau 1adf135a09 fix: don't call debounced restart too much 2023-08-29 00:04:40 +02:00
Guillaume Chau 500cc49e11 chore: update throttle-debounce 2023-08-28 23:46:27 +02:00
Guillaume Chau dfeec67aad v4.0.0-beta.8 2023-07-04 15:07:40 +02:00
Guillaume Chau 28f3520040 feat(useQuery): nullable query (auto disable) 2023-07-04 15:05:24 +02:00
Guillaume Chau 6a94797dfc v4.0.0-beta.7 2023-06-13 16:09:03 +02:00
Guillaume Chau 87188c49a1 fix(ssr): hydration mismatch with keepPreviousResult 2023-06-13 16:07:07 +02:00
Guillaume Chau a9c60128f6 v4.0.0-beta.6 2023-06-12 16:31:56 +02:00
Stefan Schneider d8f93bf28b docs: provideApolloClient (#1442) 2023-06-12 16:14:30 +02:00
Guillaume Chau c8814394d1 test: fix 2023-06-12 16:09:19 +02:00
Guillaume Chau 68217df8e7 revert: graphql version override 2023-06-12 15:59:17 +02:00
Guillaume Chau 97c14020a0 test: migrate server to typescript 2023-06-12 15:47:34 +02:00
Guillaume Chau f1ebe703d5 test: test-server package 2023-06-12 15:11:42 +02:00
Guillaume Chau 722fa0ff89 test: update pnpm version 2023-06-12 13:05:20 +02:00
Guillaume Chau e794c1edde feat: keepPreviousResult 2023-06-12 12:26:48 +02:00
Guillaume Chau 13bfbbea98 test: update server 2023-06-12 11:55:02 +02:00
Guillaume Chau 2077502adf chore: update lockfile version 2023-06-08 16:36:50 +02:00
Guillaume Chau bfca616ad3 chore: typo in test component file 2023-06-08 16:03:02 +02:00
Guillaume Chau 4dcfa20401 chore: update graphql to 16 in repo 2023-06-07 17:43:18 +02:00
Guillaume Chau 71ece764ed v4.0.0-beta.5 2023-05-16 16:28:19 +02:00
Guillaume Chau cf7917e0b8 chore: update sheep/release-tag 2023-05-16 16:23:28 +02:00
Guillaume Chau 5967e160be feat: allow global tracking outside of components 2023-05-16 16:05:24 +02:00
Guillaume Chau d5f6275261 fix(ssr): handle result/error set before serverPrefetch call, fix #1429
Original PR #1435 by @fabis94
2023-05-16 15:40:00 +02:00
Guillaume Chau 337c02a90d fix(ts): allow null on userLazyQuery load fn, fix #1386 2023-05-16 15:27:34 +02:00
Gibran Amparan 4dc555f9a3 docs: Subscriptions configuration docs updated to describe graphql-ws configuration. (#1449) 2023-05-16 15:09:00 +02:00
Guillaume Chau 2de65e4822 fix: initialize currentDocument early, fix #1325 2023-05-16 15:05:39 +02:00
Alessia Bellisario b2f773c6f7 feat(ts): update types to account for changes in TypeScript 4.8 (#1454)
Co-authored-by: Guillaume Chau <guillaume.b.chau@gmail.com>
2023-05-16 14:56:09 +02:00
Guillaume Chau 2f4c6c358d fix: ssr export paths, fix #1469 2023-05-16 13:14:01 +02:00
Dominik Klein aad2ae03ea fix: avoid multiple on error calls without usage of errorPolicy 'all' (#1461) 2023-05-16 13:13:27 +02:00
Changwan Jun aaa040c827 fix: @vue/apollo-composable ESM settings, fix #1462 (#1463) 2023-05-16 13:08:37 +02:00
Guillaume Chau 63067a2ea9 fix: events not registered in case of immediate trigger, fix #1154 2023-05-16 13:01:02 +02:00
Guillaume Chau d1d8426758 fix: don't call variables if query is disabled + fix enabling race conditions, fix #1243, fix #1422 2023-05-16 12:37:38 +02:00
Guillaume Chau db7d79c506 test: enabled 2023-05-16 12:16:40 +02:00
Guillaume Chau 53554b8444 test: demo useLazyQuery with immediate load 2023-05-16 12:05:39 +02:00
Guillaume Chau dcb1768f7d feat: useLazyQuery load returns boolean to make is easier to refetch 2023-05-16 11:41:24 +02:00
Guillaume Chau 32c95de64b test: config cypress downloads 2023-05-16 10:56:17 +02:00
Guillaume Chau e3dcdf5dbc ci fix missing working-directory 2023-05-16 10:43:28 +02:00
Guillaume Chau bc3d80ca8a ci: enable on v4 branch 2023-05-16 10:40:41 +02:00
Guillaume Chau 25c31d22c3 ci: switch to github actions 2023-05-16 10:38:08 +02:00
Guillaume Chau 81ea32c248 chore: update lockfile to v6.0 2023-05-16 10:23:53 +02:00
Guillaume Chau fdfe756b5d v4.0.0-beta.4 2023-02-22 10:49:10 +01:00
Guillaume Chau 2623b32d6c feat: improve ESM support 2023-02-22 10:46:47 +01:00
Guillaume Chau 3caf750881 v4.0.0-beta.3 2023-02-21 14:31:19 +01:00
Guillaume Chau c454937b8a chore: update deps and fix tests 2023-02-21 14:27:09 +01:00
Guillaume Chau 18fe206761 fix(ssr): error not bubbling up 2023-02-21 14:16:11 +01:00
Guillaume Chau 5aa44c2e30 v4.0.0-beta.2 2023-02-03 14:29:42 +01:00
Guillaume Chau 1e24d2110c fix: ignore next result only if not loading 2023-02-03 14:24:24 +01:00
Csizmadia Szabolcs 8dfe93b826 fix(@vue/apollo-option): ssr cleanup function fails to run (#1424) (#1425)
Co-authored-by: Szabolcs Csizmadia <Szabolcs.Csizmadia-ext@querplex.biz>
2023-02-03 14:24:00 +01:00
Guillaume Chau 9302d4d4a5 fix: hydration error, revert #1388, fix #1432 2023-02-03 14:10:58 +01:00
262 changed files with 22061 additions and 12963 deletions
-92
View File
@@ -1,92 +0,0 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
- image: cypress/base:14
environment:
## this enables colors in the output
TERM: xterm
working_directory: ~/repo
steps:
- checkout
- run: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
- run: |
echo 'export NVM_DIR=$HOME/.nvm' >> $BASH_ENV
echo 'source $NVM_DIR/nvm.sh' >> $BASH_ENV
- run: |
nvm install 16
nvm use 16
nvm alias default 16
- run: npm install -g pnpm
# Download and cache dependencies
- restore_cache:
keys:
- v11-dependencies-{{ checksum "pnpm-lock.yaml" }}
# fallback to using the latest cache if no exact match is found
- v11-dependencies-
- run: pnpm install --frozen-lockfile
- save_cache:
paths:
- node_modules
- packages/docs/node_modules
- packages/test-e2e/node_modules
- packages/test-e2e-composition/node_modules
- packages/test-e2e-composable-vue3/node_modules
- packages/test-ssr/node_modules
- packages/test-ssr-composition/node_modules
- packages/vue-apollo-components/node_modules
- packages/vue-apollo-composable/node_modules
- packages/vue-apollo-option/node_modules
- packages/vue-apollo-ssr/node_modules
- packages/vue-apollo-util/node_modules
- ~/.cache
- ~/.pnpm-store
key: v11-dependencies-{{ checksum "pnpm-lock.yaml" }}
# run tests!
- run: pnpm run lint
- run: pnpm run build
- run: cd packages/vue-apollo-option && pnpm run test:types
- run: cd packages/vue-apollo-option && pnpm run test:unit
- run: cd packages/vue-apollo-composable && pnpm run test:types
- run: cd packages/test-e2e && pnpm run test:e2e
# - run: cd packages/test-e2e-composition && pnpm run test:e2e
- run: cd packages/test-e2e-composable-vue3 && pnpm run test:e2e
# - run: cd packages/test-ssr && pnpm run test:e2e
# - run: cd packages/test-ssr-composition && pnpm run test:e2e
- store_artifacts:
path: packages/test-e2e/tests/e2e/videos
- store_artifacts:
path: packages/test-e2e/tests/e2e/screenshots
- store_artifacts:
path: packages/test-e2e-composition/tests/e2e/videos
- store_artifacts:
path: packages/test-e2e-composition/tests/e2e/screenshots
- store_artifacts:
path: packages/test-e2e-composable-vue3/tests/e2e/videos
- store_artifacts:
path: packages/test-e2e-composable-vue3/tests/e2e/screenshots
- store_artifacts:
path: packages/test-ssr/tests/e2e/videos
- store_artifacts:
path: packages/test-ssr/tests/e2e/screenshots
- store_artifacts:
path: packages/test-ssr-composition/tests/e2e/videos
- store_artifacts:
path: packages/test-ssr-composition/tests/e2e/screenshots
+29
View File
@@ -0,0 +1,29 @@
name: Publish Nightlies
on:
pull_request:
push:
branches:
- '**'
tags:
- '!**'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Set alternate npm integrity keys
run: |
echo COREPACK_INTEGRITY_KEYS="$(curl https://registry.npmjs.org/-/npm/v1/keys | jq -c '{npm: .keys}')" >> $GITHUB_ENV
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 23
cache: pnpm
- run: pnpm install
- name: Build
run: pnpm build
- run: pnpx pkg-pr-new publish './packages/*'
+3 -2
View File
@@ -1,4 +1,4 @@
name: "Check PR title"
name: Check PR title
on:
pull_request_target:
@@ -8,8 +8,9 @@ on:
- synchronize
jobs:
main:
check-pr-title:
runs-on: ubuntu-latest
name: Check PR title
steps:
# Please look up the latest version from
# https://github.com/amannn/action-semantic-pull-request/releases
+3 -4
View File
@@ -3,7 +3,7 @@ name: Create release
on:
push:
tags:
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
jobs:
build:
@@ -13,13 +13,12 @@ jobs:
- name: Checkout code
uses: actions/checkout@master
with:
fetch-depth: 0
fetch-depth: 0 # Fetch all tags
- name: Create Release for Tag
id: release_tag
uses: Akryum/release-tag@conventional
uses: Akryum/release-tag@v4.0.7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
preset: angular
+67
View File
@@ -0,0 +1,67 @@
name: E2E composable
on:
push:
branches:
- main
- v4
- feat/*
- fix/*
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
name: Build and test
env:
dir: ./packages/test-e2e-composable-vue3
steps:
- name: Set alternate npm integrity keys
run: |
echo COREPACK_INTEGRITY_KEYS="$(curl https://registry.npmjs.org/-/npm/v1/keys | jq -c '{npm: .keys}')" >> $GITHUB_ENV
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 23
cache: pnpm
- uses: actions/cache@v4
with:
path: ~/.cache/Cypress
key: cypress-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm install
- name: Build
run: pnpm run build
- name: Build app
working-directory: ${{env.dir}}
run: pnpm run build
- name: E2E tests
working-directory: ${{env.dir}}
run: pnpm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: ${{env.dir}}/tests/e2e/screenshots
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-videos
path: ${{env.dir}}/tests/e2e/videos
+63
View File
@@ -0,0 +1,63 @@
name: E2E options/components
on:
push:
branches:
- main
- v4
- feat/*
- fix/*
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
name: Build and test
env:
dir: ./packages/test-e2e
steps:
- name: Set alternate npm integrity keys
run: |
echo COREPACK_INTEGRITY_KEYS="$(curl https://registry.npmjs.org/-/npm/v1/keys | jq -c '{npm: .keys}')" >> $GITHUB_ENV
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 23
cache: pnpm
- uses: actions/cache@v4
with:
path: ~/.cache/Cypress
key: cypress-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm install
- name: Build
run: pnpm run build
- name: E2E tests
working-directory: ${{env.dir}}
run: pnpm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: ${{env.dir}}/tests/e2e/screenshots
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-videos
path: ${{env.dir}}/tests/e2e/videos
+63
View File
@@ -0,0 +1,63 @@
name: E2E composable + SSR
on:
push:
branches:
- main
- v4
- feat/*
- fix/*
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
name: Build and test
env:
dir: ./packages/test-e2e-ssr
steps:
- name: Set alternate npm integrity keys
run: |
echo COREPACK_INTEGRITY_KEYS="$(curl https://registry.npmjs.org/-/npm/v1/keys | jq -c '{npm: .keys}')" >> $GITHUB_ENV
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 23
cache: pnpm
- uses: actions/cache@v4
with:
path: ~/.cache/Cypress
key: cypress-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }}
- run: pnpm install
- name: Build
run: pnpm run build
- name: E2E tests
working-directory: ${{env.dir}}
run: pnpm run test:e2e
- uses: actions/upload-artifact@v4
if: failure()
with:
name: cypress-screenshots
path: ${{env.dir}}/tests/e2e/screenshots
- uses: actions/upload-artifact@v4
if: always()
with:
name: cypress-videos
path: ${{env.dir}}/tests/e2e/videos
+44
View File
@@ -0,0 +1,44 @@
name: Main continuous tests
on:
push:
branches:
- main
- v4
- feat/*
- fix/*
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
name: Build and test
steps:
- name: Set alternate npm integrity keys
run: |
echo COREPACK_INTEGRITY_KEYS="$(curl https://registry.npmjs.org/-/npm/v1/keys | jq -c '{npm: .keys}')" >> $GITHUB_ENV
- uses: actions/checkout@v4
- run: corepack enable
- uses: actions/setup-node@v4
with:
node-version: 23
cache: pnpm
- run: pnpm install
- name: Lint
run: pnpm run lint
- name: Build
run: pnpm run build
- name: Types
run: pnpm run test:types
- name: Unit tests
run: pnpm run test:unit
+3
View File
@@ -1,2 +1,5 @@
.idea
node_modules/
dist/
cache/
.eslintcache
+426
View File
@@ -1,3 +1,429 @@
# Changelog
## v4.2.2
[compare changes](https://github.com/vuejs/apollo/compare/v4.2.1...v4.2.2)
### 🩹 Fixes
- Memory leak in SSR caused by global tracking ([#1582](https://github.com/vuejs/apollo/pull/1582))
- Augment `vue` rather than `@vue/runtime-core` ([#1576](https://github.com/vuejs/apollo/pull/1576))
- UseMutations onError Event hook gets triggered too early ([#1585](https://github.com/vuejs/apollo/pull/1585))
### 📖 Documentation
- Fix $skipAll mention ([#1573](https://github.com/vuejs/apollo/pull/1573))
### 🏡 Chore
- Update deps + jest to vitest ([d421887](https://github.com/vuejs/apollo/commit/d421887))
### ✅ Tests
- **lint:** Switch to eslint 9 and antfu config ([99ca23b](https://github.com/vuejs/apollo/commit/99ca23b))
### 🤖 CI
- Update gh actions to use corepack ([f2578cb](https://github.com/vuejs/apollo/commit/f2578cb))
- Update upload artifact ([35fb59c](https://github.com/vuejs/apollo/commit/35fb59c))
- Fix cypress ([df96345](https://github.com/vuejs/apollo/commit/df96345))
- Rename job ([d99865f](https://github.com/vuejs/apollo/commit/d99865f))
- Add name to pr title job ([6bfd0ec](https://github.com/vuejs/apollo/commit/6bfd0ec))
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Jeroen De Jong ([@thumbnail](http://github.com/thumbnail))
- Mark Florian <markrian@gmail.com>
- ChatonDeAru (Romain) ([@chatondearu](http://github.com/chatondearu))
- Kristaps Fabians Geikins <fabis94@live.com>
## v4.2.1
### 🩹 Fixes
- Improved pinia support (#1571)
### 📖 Documentation
- Update broken circleci badge (9622392)
- Readme smaller logo (ff836ea)
- Use nightly.akryum.dev (7f3cf7d)
### 🏡 Chore
- Specify pnpm version in package.json (732e66e)
### ❤️ Contributors
- Nick Messing ([@nickmessing](http://github.com/nickmessing))
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
## v4.2.0
### 🚀 Enhancements
- Add updateQuery to useQuery (#1552)
### 🩹 Fixes
- UseMutations onDone Event hook gets triggered too early (#1559)
- (@vue/apollo-option) memory leak in wrapped ssrRender (#1553)
- Reuse previous result, fix #1483 (#1569, #1483)
- ResolveClient throwing too soon, fix #1557 (#1570, #1557)
### 📖 Documentation
- Add github link to documentation (#1549)
- Note about continuous releases (51e09e7)
### 🏡 Chore
- Switch some tests to script setup (c8e5106)
### 🤖 CI
- Nightly releases (319f6ec)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Matt Garrett <mattga@gmail.com>
- Mobsean ([@mobsean](http://github.com/mobsean))
- Leonardo Santos ([@syllomex](http://github.com/syllomex))
- Alex Liu ([@Mini-ghost](http://github.com/Mini-ghost))
## v4.1.0
### 🩹 Fixes
- Change teardown to use onScopeDispose (#1545)
### 📖 Documentation
- **useQuery:** Document refetch with new variables (#1564)
### 🏡 Chore
- Updqte pnpm to v9 (827ea6e)
### ✅ Tests
- UseSubscription (0f5ae61)
- Fix subscription test (#1547)
### 🤖 CI
- Update versions (fe66840)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Nick Messing ([@nickmessing](http://github.com/nickmessing))
## v4.0.2
### 🩹 Fixes
- Use shallowRef on result & error (08f0fcd)
### 📖 Documentation
- Remove mentions of fetchResults, fix #1060 (#1060)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
## v4.0.1
### 🩹 Fixes
- Use hasInjectionContext in useApolloClient before calling inject (#1529)
- **useLazyQuery:** Load() on server, fix #1495 (#1495)
### ✅ Tests
- Split into outsideComponent.cy.ts (48d0ac2)
- Build test app in test command (500d6e4)
### 🤖 CI
- Use GITHUB_OUTPUT envvar instead of set-output command (#1530)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Arun Sathiya <arun@arun.blog>
- Dawid Kopys <dewke17@gmail.com>
## v4.0.0
### 🚀 Enhancements
- **useLazyQuery:** Add interface for lazy query return (#1523)
### 🩹 Fixes
- Improve esm support, fix #1524 (#1524)
- Import serializeJs using default import instead of a namespace import (#1485)
- **options:** Use exponential backoff on subscribe error retry (b17817e)
- **ApolloMutation:** Return result in `mutate` (ddf9aa0)
- Prefetch type (f8568e8)
### 📖 Documentation
- Update vitepress + enable detailed search by default (fb66dce)
### 🏡 Chore
- Update sheep (9be63fa)
### ✅ Tests
- Fragment (062f72a)
### 🤖 CI
- Update node and pnpm (ca3f2f4)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Dawid Kopys <dewke17@gmail.com>
- Yury Savin <yury@savin.dev>
## v4.0.0-beta.12
### 🚀 Enhancements
- New context params in event hook handlers (0be5d9b)
### 🩹 Fixes
- Use shallowRef for apollo query (76f19f6)
### 📖 Documentation
- Missing curly brace (#1512)
- Added missing createClient import in example when creating Graph… (#1513)
- Import createApolloProvider is missing (#1515)
### 🏡 Chore
- Moved resolutions to the root of the workspace (#1508)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Hassan <hassanfayyaz19@gmail.com>
- Mekraldi
- Vitaliy
## v4.0.0-beta.11
### 🩹 Fixes
- Remove console.log, console log remained in code #1507 (#1507)
### 📖 Documentation
- Update README.md logo (68addf8)
- Update vitepress + fix components API menu (f545763)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
## v4.0.0-beta.10
### 🚀 Enhancements
- Support effect scope outside of component, fix #1505 (#1505)
- **useLazyQuery:** Load returns Promise, fix #1486 (#1486)
### 🩹 Fixes
- Apollo components should have emits (#1504)
### 🌊 Types
- Extended "enabled" option type (#1492)
### 🏡 Chore
- Package test-e2e-composable-vue3, update deps, migrate to vite (#1488)
- Upgrade vitepress to 1.0 RC (daffd75)
- Seq test (995131d)
### ✅ Tests
- **lint:** Fix (1ac1372)
- Ssr (574bd8f)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Viktor ([@websitevirtuoso](http://github.com/websitevirtuoso))
- Vitaliy
- Forgottencsc <forgottencosecant@outlook.com>
## v4.0.0-beta.9
### 🩹 Fixes
- Don't call debounced restart too much (1adf135)
### 🏡 Chore
- Update throttle-debounce (500cc49)
- Update deps (f47759e)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
## v4.0.0-beta.8
### 🚀 Enhancements
- **useQuery:** Nullable query (auto disable) (28f3520)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
## v4.0.0-beta.7
### 🩹 Fixes
- **ssr:** Hydration mismatch with keepPreviousResult (87188c4)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
## v4.0.0-beta.6
### 🚀 Enhancements
- KeepPreviousResult (e794c1e)
### 📖 Documentation
- ProvideApolloClient (#1442)
### 🏡 Chore
- Update graphql to 16 in repo (4dcfa20)
- Typo in test component file (bfca616)
- Update lockfile version (2077502)
### ✅ Tests
- Update server (13bfbbe)
- Update pnpm version (722fa0f)
- Test-server package (f1ebe70)
- Migrate server to typescript (97c1402)
- Fix (c881439)
### ❤️ Contributors
- Stefan Schneider <stefan.schneider@gmx.net>
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
## v4.0.0-beta.5
### 🚀 Enhancements
- UseLazyQuery load returns boolean to make is easier to refetch (dcb1768)
- **ts:** Update types to account for changes in TypeScript 4.8 (#1454)
- Allow global tracking outside of components (5967e16)
### 🩹 Fixes
- Don't call variables if query is disabled + fix enabling race conditions, fix #1243, fix #1422 (#1243, #1422)
- Events not registered in case of immediate trigger, fix #1154 (#1154)
- @vue/apollo-composable ESM settings, fix #1462 (#1463, #1462)
- Avoid multiple on error calls without usage of errorPolicy 'all' (#1461)
- Ssr export paths, fix #1469 (#1469)
- Initialize currentDocument early, fix #1325 (#1325)
- **ts:** Allow null on `userLazyQuery` `load` fn, fix #1386 (#1386)
- **ssr:** Handle result/error set before serverPrefetch call, fix #1429 (#1429)
### 📖 Documentation
- Subscriptions configuration docs updated to describe graphql-ws configuration. (#1449)
### 🏡 Chore
- Update lockfile to v6.0 (81ea32c)
- Update sheep/release-tag (cf7917e)
### ✅ Tests
- Config cypress downloads (32c95de)
- Demo useLazyQuery with immediate load (53554b8)
- Enabled (db7d79c)
### 🤖 CI
- Switch to github actions (25c31d2)
- Enable on v4 branch (bc3d80c)
### ❤️ Contributors
- Guillaume Chau ([@Akryum](http://github.com/Akryum))
- Gibran Amparan ([@gibranamparan](http://github.com/gibranamparan))
- Alessia Bellisario <alessia@apollographql.com>
- Dominik Klein <dk@zammad.com>
- Changwan Jun ([@wan2land](http://github.com/wan2land))
# [4.0.0-beta.4](https://github.com/vuejs/vue-apollo/compare/v4.0.0-beta.3...v4.0.0-beta.4) (2023-02-22)
### Features
* improve ESM support ([2623b32](https://github.com/vuejs/vue-apollo/commit/2623b32d6c999cfa677b3b36969bd6b5b782d387))
# [4.0.0-beta.3](https://github.com/vuejs/vue-apollo/compare/v4.0.0-beta.2...v4.0.0-beta.3) (2023-02-21)
### Bug Fixes
* **ssr:** error not bubbling up ([18fe206](https://github.com/vuejs/vue-apollo/commit/18fe206761eba0af05971dff34113d5396e6e6bf))
# [4.0.0-beta.2](https://github.com/vuejs/vue-apollo/compare/v4.0.0-beta.1...v4.0.0-beta.2) (2023-02-03)
### Bug Fixes
* **@vue/apollo-option:** ssr cleanup function fails to run ([#1424](https://github.com/vuejs/vue-apollo/issues/1424)) ([#1425](https://github.com/vuejs/vue-apollo/issues/1425)) ([8dfe93b](https://github.com/vuejs/vue-apollo/commit/8dfe93b82679fac42b8d1509febc97e7faeed1e0))
* hydration error, revert [#1388](https://github.com/vuejs/vue-apollo/issues/1388), fix [#1432](https://github.com/vuejs/vue-apollo/issues/1432) ([9302d4d](https://github.com/vuejs/vue-apollo/commit/9302d4d4a55541bb49292463d8176d0527c06ce9))
* ignore next result only if not loading ([1e24d21](https://github.com/vuejs/vue-apollo/commit/1e24d2110c3ea6ee80590c2b6578fef45a2e448e))
* typo in useResult deprecation message ([#1414](https://github.com/vuejs/vue-apollo/issues/1414)) ([3728928](https://github.com/vuejs/vue-apollo/commit/372892855d76622128ac560e8fadc689c50675bc))
# [4.0.0-beta.1](https://github.com/vuejs/vue-apollo/compare/v4.0.0-alpha.20...v4.0.0-beta.1) (2022-10-05)
+2 -2
View File
@@ -10,10 +10,10 @@ pnpm install
Go to a package in `packages`.
Build the library with watching:
Build the library:
```
pnpm run dev
pnpm run build
```
Run tests:
+11 -6
View File
@@ -1,14 +1,13 @@
<p align="center">
<img src="./packages/docs/src/public/hero.svg" width="256">
</p>
# Apollo and GraphQL for Vue.js
[![npm](https://img.shields.io/npm/v/@vue/apollo-composable.svg) ![npm](https://img.shields.io/npm/dm/@vue/apollo-composable.svg)](https://www.npmjs.com/package/@vue/apollo-composable)
[![apollo3](https://img.shields.io/badge/apollo-3.x-blue.svg)](https://www.apollographql.com/)
[![vue3](https://img.shields.io/badge/vue-3-brightgreen.svg)](https://vuejs.org/)
[![CircleCI branch](https://img.shields.io/circleci/build/github/vuejs/vue-apollo/v4.svg)](https://circleci.com/gh/vuejs/vue-apollo/tree/v4)
<p align="center">
<img src="https://cdn-images-1.medium.com/max/400/1*H9AANoofLqjS10Xd5TwRYw.png">
</p>
![GitHub branch check runs](https://img.shields.io/github/check-runs/vuejs/apollo/v4)
:book: Documentation [**for Vue 3**](http://v4.apollo.vuejs.org) | [for Vue 2](https://apollo.vuejs.org/)
@@ -16,6 +15,12 @@
[:heart: Sponsor me!](https://github.com/sponsors/Akryum)
## Continuous Releases
You can install builds from any commit on the main branch from [here](https://nightly.akryum.dev/vuejs/vue-apollo) or from any Pull Request.
## Monorepo
In this monorepository:
| Package | Description |
+58
View File
@@ -0,0 +1,58 @@
// eslint.config.mjs
import antfu from '@antfu/eslint-config'
export default antfu({
ignores: [
'node_modules/',
'dist/',
'generated/',
'!.*',
'schema.graphql',
'.test-todo/',
'**/types/test/',
],
rules: {
'ts/no-use-before-define': 'warn',
'unused-imports/no-unused-vars': 'warn',
'accessor-pairs': 'off',
},
}, {
files: [
'packages/docs/**',
],
rules: {
'no-dupe-keys': 'off',
'no-new': 'off',
'no-console': 'off',
},
}, {
files: [
'packages/test-*/**',
'**/*.test.*',
],
rules: {
'antfu/no-top-level-await': 'off',
'no-console': 'off',
'unused-imports/no-unused-vars': 'off',
'node/prefer-global/process': 'off',
'import/no-mutable-exports': 'off',
},
languageOptions: {
globals: {
cy: false,
expect: false,
describe: false,
it: false,
before: false,
},
},
}, {
files: [
'**/tests/types/**',
],
rules: {
'ts/no-unused-expressions': 'off',
},
})
+3 -3
View File
@@ -37,9 +37,9 @@ module.exports = {
'@typescript-eslint/no-use-before-define': 'off',
'comma-dangle': ['error', 'always-multiline'],
'vue/no-multiple-template-root': 'off',
indent: 'off',
'indent': 'off',
'@typescript-eslint/indent': ['error', 2],
quotes: ['error', 'single', { allowTemplateLiterals: true }],
'quotes': ['error', 'single', { allowTemplateLiterals: true }],
'no-use-before-define': 'warn',
'accessor-pairs': 'off',
'no-async-promise-executor': 'off',
@@ -73,7 +73,7 @@ module.exports = {
'packages/*/types/test/**/*.ts',
],
rules: {
camelcase: 'off',
'camelcase': 'off',
'no-unused-expressions': 'off',
'array-callback-return': 'warn',
},
+27 -16
View File
@@ -1,35 +1,34 @@
{
"name": "vue-apollo-monorepo",
"version": "4.0.0-beta.1",
"version": "4.2.2",
"private": true,
"packageManager": "pnpm@10.6.2+sha512.47870716bea1572b53df34ad8647b42962bc790ce2bf4562ba0f643237d7302a3d6a8ecef9e4bdfc01d23af1969aa90485d4cebb0b9638fa5ef1daef656f6c1b",
"scripts": {
"build": "pnpm run -r --filter \"vue-apollo*\" --filter \"@vue/apollo*\" build",
"test": "pnpm run -r test",
"lint": "eslint . --ext js,vue,ts",
"release": "pnpm run build && pnpm run test && sheep release -b v4"
"test": "pnpm run -r --sequential test",
"test:unit": "pnpm run -r test:unit",
"test:types": "pnpm run -r test:types",
"lint": "eslint . --cache",
"release": "pnpm run build && pnpm run test && sheep release -b v4",
"docs:dev": "pnpm run -r --filter \"private-vue-apollo-docs\" dev"
},
"devDependencies": {
"@akryum/sheep": "^0.3.3",
"@akryum/sheep": "^0.5.1",
"@antfu/eslint-config": "^4.7.0",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"@vue/eslint-config-standard": "^6.1.0",
"@vue/eslint-config-typescript": "^7.0.0",
"conventional-changelog-cli": "^2.2.2",
"core-js": "^3.23.2",
"esbuild": "^0.8.57",
"esbuild-node-externals": "^1.4.1",
"eslint": "^7.32.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^4.3.1",
"eslint-plugin-standard": "^5.0.0",
"eslint-plugin-vue": "^7.20.0",
"typescript": "^4.7.4"
"esbuild": "^0.25.0",
"esbuild-node-externals": "^1.18.0",
"eslint": "^9.21.0",
"typescript": "^5.8.2"
},
"pnpm": {
"overrides": {
"eslint-scope": "^5",
"graphql": "^15",
"serialize-javascript": "^6.0.0",
"subscriptions-transport-ws": "^0.9"
},
@@ -51,6 +50,18 @@
"esbuild": "*",
"vue": "*"
}
}
},
"onlyBuiltDependencies": [
"@apollo/protobufjs",
"core-js",
"core-js-pure",
"cypress",
"esbuild",
"nodemon",
"vue-demi"
]
},
"resolutions": {
"js-yaml": "^3.13.1"
}
}
+4 -3
View File
@@ -1,15 +1,16 @@
{
"name": "private-vue-apollo-docs",
"type": "module",
"version": "4.0.0-alpha.16",
"private": true,
"scripts": {
"dev": "vitepress dev src",
"build": "vitepress build src"
},
"devDependencies": {
"vitepress": "^0.22.4"
},
"dependencies": {
"vue-github-button": "^3.0.3"
},
"devDependencies": {
"vitepress": "^1.0.0-rc.36"
}
}
+341 -340
View File
@@ -7,334 +7,341 @@ export default defineConfig({
head: [
['link', { rel: 'icon', href: '/favicon.png' }],
],
themeConfig: {
lastUpdated: true,
socialLinks: [
{ icon: 'github', link: 'https://github.com/vuejs/apollo' },
],
footer: {
message: `Released under the MIT License.`,
copyright: `Copyright © 2015-present Guillaume Chau`,
},
editLink: {
pattern: 'https://github.com/vuejs/apollo/edit/v4/packages/docs/src/:path',
},
nav: [
{
text: 'Guide',
items: [
{
text: 'Getting started',
link: '/guide/',
},
{
text: 'Option API',
link: '/guide-option/',
},
{
text: 'Composition API',
link: '/guide-composable/',
},
{
text: 'Component API',
link: '/guide-components/',
},
{
text: 'Advanced topics',
link: '/guide-advanced/',
},
],
},
{
text: 'API Reference',
link: '/api/',
},
{
text: 'Migration',
link: '/migration/',
},
{
text: 'CLI plugin',
link: 'https://github.com/Akryum/vue-cli-plugin-apollo',
},
{
text: 'Sponsor',
link: 'https://github.com/sponsors/Akryum',
},
],
sidebar: {
'/guide/': [
{
text: 'Introduction',
link: '/guide/',
},
{
text: 'Installation',
link: '/guide/installation',
},
],
'/guide-option/': [
{
text: 'Option API Guide',
collapsable: false,
items: [
{
text: 'Introduction',
link: '/guide-option/',
},
{
text: 'Setup',
link: '/guide-option/setup',
},
],
},
{
text: 'Basics',
collapsable: false,
items: [
{
text: 'Usage in Vue components',
link: '/guide-option/usage',
},
{
text: 'Queries',
link: '/guide-option/queries',
},
{
text: 'Mutations',
link: '/guide-option/mutations',
},
{
text: 'Subscriptions',
link: '/guide-option/subscriptions',
},
],
},
{
text: 'Advanced',
collapsable: false,
items: [
{
text: 'Special options',
link: '/guide-option/special-options',
},
{
text: 'Pagination',
link: '/guide-option/pagination',
},
{
text: 'Multiple clients',
link: '/guide-option/multiple-clients',
},
],
},
],
'/guide-composable/': [
{
text: 'Composition API Guide',
collapsable: false,
items: [
{
text: 'Introduction',
link: '/guide-composable/',
},
{
text: 'Setup',
link: '/guide-composable/setup',
},
],
},
{
text: 'Fetching data',
collapsable: false,
items: [
{
text: 'Queries',
link: '/guide-composable/query',
},
{
text: 'Mutations',
link: '/guide-composable/mutation',
},
{
text: 'Subscriptions',
link: '/guide-composable/subscription',
},
{
text: 'Pagination',
link: '/guide-composable/pagination',
},
{
text: 'Fragments',
link: '/guide-composable/fragments',
},
{
text: 'Error handling',
link: '/guide-composable/error-handling',
},
],
},
],
'/guide-components/': [
{
text: 'Components Guide',
collapsable: false,
items: [
{
text: 'Introduction',
link: '/guide-components/',
},
{
text: 'Setup',
link: '/guide-components/setup',
},
],
},
{
text: 'Usage',
collapsable: false,
items: [
{
text: 'Queries',
link: '/guide-components/query',
},
{
text: 'Mutations',
link: '/guide-components/mutation',
},
{
text: 'Subscribe to a Query',
link: '/guide-components/subscribe-to-more',
},
],
},
],
'/guide-advanced/': [
{
text: 'Advanced topics',
collapsable: false,
items: [
{
text: 'Local state',
link: '/guide-advanced/local-state',
},
{
text: 'Server-Side Rendering',
link: '/guide-advanced/ssr',
},
{
text: 'Testing',
link: '/guide-advanced/testing',
},
],
},
],
'/api/': [
{
text: 'Option API',
collapsable: false,
items: [
{
text: 'createApolloProvider',
link: '/api/apollo-provider',
},
{
text: '$apollo',
link: '/api/dollar-apollo',
},
{
text: 'Reactive queries',
link: '/api/smart-query',
},
{
text: 'Reactive subscriptions',
link: '/api/smart-subscription',
},
],
},
{
text: 'Composition API',
collapsable: false,
items: [
{
text: 'useQuery',
link: '/api/use-query',
},
{
text: 'useLazyQuery',
link: '/api/use-lazy-query',
},
{
text: 'useMutation',
link: '/api/use-mutation',
},
{
text: 'useSubscription',
link: '/api/use-subscription',
},
{
text: 'useApolloClient',
link: '/api/use-apollo-client',
},
{
text: 'Loading utilities',
link: '/api/use-loading',
},
],
},
{
text: 'Components',
collapsable: false,
items: [
{
text: 'ApolloQuery',
link: '/api/apollo-query',
},
{
text: 'ApolloMutation',
link: '/api/apollo-mutation',
},
{
text: 'ApolloSubscribeToMore',
link: '/api/apollo-subscribe-to-more',
},
],
},
{
text: 'Advanced',
collapsable: false,
items: [
{
text: 'ApolloSSR',
link: '/api/ssr',
},
],
},
],
'/migration/': [
{
text: 'Migration guide',
link: '/migration/',
},
],
},
search: {
provider: 'local',
options: {
detailedView: true,
},
},
},
locales: {
'/': {
'root': {
label: 'English',
lang: 'en-US',
title: 'Vue Apollo',
description: '🚀 Integrate GraphQL in your Vue.js apps!',
},
'/zh-cn/': {
'zh-cn': {
label: '简体中文',
lang: 'zh-CN',
title: 'Vue Apollo',
description: '🚀 在你的 Vue.js 应用中集成 GraphQL',
},
},
themeConfig: {
repo: 'Akryum/vue-apollo',
docsBranch: 'v4',
docsDir: 'packages/docs/src',
editLinks: true,
editLinkText: 'Suggest changes to this page',
locales: {
'/': {
selectText: 'Languages',
label: 'English',
lastUpdated: 'Last Updated',
nav: [
{
text: 'Guide',
items: [
{
text: 'Getting started',
link: '/guide/',
},
{
text: 'Option API',
link: '/guide-option/',
},
{
text: 'Composition API',
link: '/guide-composable/',
},
{
text: 'Component API',
link: '/guide-components/',
},
{
text: 'Advanced topics',
link: '/guide-advanced/',
},
],
},
{
text: 'API Reference',
link: '/api/',
},
{
text: 'Migration',
link: '/migration/',
},
{
text: 'CLI plugin',
link: 'https://github.com/Akryum/vue-cli-plugin-apollo',
},
{
text: 'Sponsor',
link: 'https://github.com/sponsors/Akryum',
},
],
sidebarDepth: 2,
sidebar: {
'/guide/': [
{
text: 'Introduction',
link: '/guide/',
},
{
text: 'Installation',
link: '/guide/installation',
},
],
'/guide-option/': [
{
text: 'Option API Guide',
collapsable: false,
children: [
{
text: 'Introduction',
link: '/guide-option/',
},
{
text: 'Setup',
link: '/guide-option/setup',
},
],
},
{
text: 'Basics',
collapsable: false,
children: [
{
text: 'Usage in Vue components',
link: '/guide-option/usage',
},
{
text: 'Queries',
link: '/guide-option/queries',
},
{
text: 'Mutations',
link: '/guide-option/mutations',
},
{
text: 'Subscriptions',
link: '/guide-option/subscriptions',
},
],
},
{
text: 'Advanced',
collapsable: false,
children: [
{
text: 'Special options',
link: '/guide-option/special-options',
},
{
text: 'Pagination',
link: '/guide-option/pagination',
},
{
text: 'Multiple clients',
link: '/guide-option/multiple-clients',
},
],
},
],
'/guide-composable/': [
{
text: 'Composition API Guide',
collapsable: false,
children: [
{
text: 'Introduction',
link: '/guide-composable/',
},
{
text: 'Setup',
link: '/guide-composable/setup',
},
],
},
{
text: 'Fetching data',
collapsable: false,
children: [
{
text: 'Queries',
link: '/guide-composable/query',
},
{
text: 'Mutations',
link: '/guide-composable/mutation',
},
{
text: 'Subscriptions',
link: '/guide-composable/subscription',
},
{
text: 'Pagination',
link: '/guide-composable/pagination',
},
{
text: 'Fragments',
link: '/guide-composable/fragments',
},
{
text: 'Error handling',
link: '/guide-composable/error-handling',
},
],
},
],
'/guide-components/': [
{
text: 'Components Guide',
collapsable: false,
children: [
{
text: 'Introduction',
link: '/guide-components/',
},
{
text: 'Setup',
link: '/guide-components/setup',
},
],
},
{
text: 'Usage',
collapsable: false,
children: [
{
text: 'Queries',
link: '/guide-components/query',
},
{
text: 'Mutations',
link: '/guide-components/mutation',
},
{
text: 'Subscribe to a Query',
link: '/guide-components/subscribe-to-more',
},
],
},
],
'/guide-advanced/': [
{
text: 'Advanced topics',
collapsable: false,
children: [
{
text: 'Local state',
link: '/guide-advanced/local-state',
},
{
text: 'Server-Side Rendering',
link: '/guide-advanced/ssr',
},
{
text: 'Testing',
link: '/guide-advanced/testing',
},
],
},
],
'/api/': [
{
text: 'Option API',
collapsable: false,
children: [
{
text: 'createApolloProvider',
link: '/api/apollo-provider',
},
{
text: '$apollo',
link: '/api/dollar-apollo',
},
{
text: 'Reactive queries',
link: '/api/smart-query',
},
{
text: 'Reactive subscriptions',
link: '/api/smart-subscription',
},
],
},
{
text: 'Composition API',
collapsable: false,
children: [
{
text: 'useQuery',
link: '/api/use-query',
},
{
text: 'useLazyQuery',
link: '/api/use-lazy-query',
},
{
text: 'useMutation',
link: '/api/use-mutation',
},
{
text: 'useSubscription',
link: '/api/use-subscription',
},
{
text: 'useApolloClient',
link: '/api/use-apollo-client',
},
{
text: 'Loading utilities',
link: '/api/use-loading',
},
],
},
{
text: 'Components',
collapsable: false,
children: [
{
text: '<ApolloQuery>',
link: '/api/apollo-query',
},
{
text: '<ApolloMutation>',
link: '/api/apollo-mutation',
},
{
text: '<ApolloSubscribeToMore>',
link: '/api/apollo-subscribe-to-more',
},
],
},
{
text: 'Advanced',
collapsable: false,
children: [
{
text: 'ApolloSSR',
link: '/api/ssr',
},
],
},
],
'/migration/': [
{
text: 'Migration guide',
link: '/migration/',
},
],
themeConfig: {
lastUpdated: {
message: '上次更新时间',
},
},
'/zh-cn/': {
selectText: '选择语言',
label: '简体中文',
lastUpdated: '上次更新时间',
nav: [
{
text: '指南',
@@ -378,7 +385,6 @@ export default defineConfig({
link: 'https://github.com/sponsors/Akryum',
},
],
sidebarDepth: 3,
sidebar: {
'/zh-cn/guide/': [
{
@@ -394,7 +400,7 @@ export default defineConfig({
{
text: '选项 API 指南',
collapsable: false,
children: [
items: [
{
text: 'Introduction',
link: '/zh-cn/guide-option/',
@@ -408,7 +414,7 @@ export default defineConfig({
{
text: '基础',
collapsable: false,
children: [
items: [
{
text: 'Usage in Vue components',
link: '/zh-cn/guide-option/usage',
@@ -430,7 +436,7 @@ export default defineConfig({
{
text: '进阶',
collapsable: false,
children: [
items: [
{
text: 'Special options',
link: '/zh-cn/guide-option/special-options',
@@ -450,7 +456,7 @@ export default defineConfig({
{
text: '组合 API 指南',
collapsable: false,
children: [
items: [
{
text: 'Introduction',
link: '/zh-cn/guide-composable/',
@@ -464,7 +470,7 @@ export default defineConfig({
{
text: '获取数据',
collapsable: false,
children: [
items: [
{
text: 'Queries',
link: '/zh-cn/guide-composable/query',
@@ -496,7 +502,7 @@ export default defineConfig({
{
text: '组件指南',
collapsable: false,
children: [
items: [
{
text: 'Introduction',
link: '/zh-cn/guide-components/',
@@ -510,7 +516,7 @@ export default defineConfig({
{
text: '用法',
collapsable: false,
children: [
items: [
{
text: 'Queries',
link: '/zh-cn/guide-components/query',
@@ -530,7 +536,7 @@ export default defineConfig({
{
text: '进阶主题',
collapsable: false,
children: [
items: [
{
text: 'Local state',
link: '/zh-cn/guide-advanced/local-state',
@@ -550,7 +556,7 @@ export default defineConfig({
{
text: '选项 API',
collapsable: false,
children: [
items: [
{
text: 'createApolloProvider',
link: '/zh-cn/api/apollo-provider',
@@ -572,7 +578,7 @@ export default defineConfig({
{
text: '组合 API',
collapsable: false,
children: [
items: [
{
text: 'useQuery',
link: '/zh-cn/api/use-query',
@@ -602,17 +608,17 @@ export default defineConfig({
{
text: '组件',
collapsable: false,
children: [
items: [
{
text: '<ApolloQuery>',
text: 'ApolloQuery',
link: '/zh-cn/api/apollo-query',
},
{
text: '<ApolloMutation>',
text: 'ApolloMutation',
link: '/zh-cn/api/apollo-mutation',
},
{
text: '<ApolloSubscribeToMore>',
text: 'ApolloSubscribeToMore',
link: '/zh-cn/api/apollo-subscribe-to-more',
},
],
@@ -620,7 +626,7 @@ export default defineConfig({
{
text: '进阶',
collapsable: false,
children: [
items: [
{
text: 'ApolloSSR',
link: '/zh-cn/api/ssr',
@@ -637,11 +643,6 @@ export default defineConfig({
},
},
},
algolia: {
appId: 'X6FFODVB9N',
apiKey: 'cc89b1eff7e2fc6e6c0bbf8b066ab488',
indexName: 'apollo-vuejs',
},
},
vite: {
+4 -4
View File
@@ -1,11 +1,11 @@
import './styles/index.pcss'
import DefaultTheme from 'vitepress/dist/client/theme-default'
import DefaultTheme from 'vitepress/theme'
import SponsorButton from './components/SponsorButton.vue'
import './styles/index.pcss'
export default {
...DefaultTheme,
enhanceApp ({ app }) {
enhanceApp({ app }) {
app.component('SponsorButton', SponsorButton)
},
}
@@ -1,3 +1,6 @@
.home .hero img {
max-width: 80vw;
:root {
--vp-c-brand-1: #5591d8;
--vp-c-brand-2: #336cb0;
--vp-c-brand-3: #1f4c80;
--vp-c-brand-soft: rgba(42, 95, 156, 0.14);
}
+1 -1
View File
@@ -209,7 +209,7 @@ See [apollo context](https://www.apollographql.com/docs/react/api/link/apollo-li
Signature:
```ts
mutate(options = null): Promise<FetchResult>
function mutate(options = null): Promise<FetchResult>
```
- `options`: [mutation options](https://www.apollographql.com/docs/react/api/core/ApolloClient/#ApolloClient.mutate).
+2 -2
View File
@@ -26,12 +26,12 @@ const apolloProvider = createApolloProvider({
},
// Watch loading state for all queries
// See 'Smart Query > options > watchLoading' for detail
watchLoading (isLoading, countModifier) {
watchLoading(isLoading, countModifier) {
loading += countModifier
console.log('Global loading', loading, countModifier)
},
// Global error handler for all smart queries and subscriptions
errorHandler (error) {
errorHandler(error) {
console.log('Global error handler')
console.error(error)
},
+58 -52
View File
@@ -24,61 +24,63 @@ Each query declared in the `apollo` definition (that is, which doesn't start wit
Example:
```js
export default {
// Apollo-specific options
apollo: {
apollo: {
// Advanced query with parameters
// The 'variables' method is watched by vue
pingMessage: {
query: gql`query PingMessage($message: String!) {
pingMessage: {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// Reactive parameters
variables () {
// Reactive parameters
variables() {
// Use vue reactive properties here
return {
message: this.pingInput,
}
},
// Polling interval in milliseconds
pollInterval: 10000,
// Or, set polling interval as a vue reactive property
pollInterval() {
return this.pollInterval;
},
// Variables: deep object watch
deep: false,
// We use a custom update callback because
// the field names don't match
// By default, the 'pingMessage' attribute
// would be used on the 'data' result object
// Here we know the result is in the 'ping' attribute
// considering the way the apollo server works
update (data) {
console.log(data)
// The returned value will update
// the vue property 'pingMessage'
return data.ping
},
// Optional result hook
result ({ data, loading, networkStatus }) {
console.log('We got some result!')
},
// Error handling
error (error) {
console.error('We\'ve got an error!', error)
},
// Loading state
// loadingKey is the name of the data property
// that will be incremented when the query is loading
// and decremented when it no longer is.
loadingKey: 'loadingQueriesCount',
// watchLoading will be called whenever the loading state changes
watchLoading (isLoading, countModifier) {
return {
message: this.pingInput,
}
},
// Polling interval in milliseconds
pollInterval: 10000,
// Or, set polling interval as a vue reactive property
pollInterval() {
return this.pollInterval
},
// Variables: deep object watch
deep: false,
// We use a custom update callback because
// the field names don't match
// By default, the 'pingMessage' attribute
// would be used on the 'data' result object
// Here we know the result is in the 'ping' attribute
// considering the way the apollo server works
update(data) {
console.log(data)
// The returned value will update
// the vue property 'pingMessage'
return data.ping
},
// Optional result hook
result({ data, loading, networkStatus }) {
console.log('We got some result!')
},
// Error handling
error(error) {
console.error('We\'ve got an error!', error)
},
// Loading state
// loadingKey is the name of the data property
// that will be incremented when the query is loading
// and decremented when it no longer is.
loadingKey: 'loadingQueriesCount',
// watchLoading will be called whenever the loading state changes
watchLoading(isLoading, countModifier) {
// isLoading is a boolean
// countModifier is either 1 or -1
},
},
},
},
}
```
If you use `ES2015`, you can also write the `update` like this:
@@ -90,14 +92,18 @@ update: data => data.ping
Manual mode example:
```js
{
query: gql`...`,
manual: true,
result ({ data, loading }) {
if (!loading) {
this.items = data.items
export default {
apollo: {
myQuery: {
query: gql`...`,
manual: true,
result({ data, loading }) {
if (!loading) {
this.items = data.items
}
},
}
},
}
}
```
+3 -3
View File
@@ -17,7 +17,7 @@ const states = ApolloSSR.getStates(clientsObject, options)
`options` defaults to:
```js
{
const defaultOptions = {
// Prefix for the keys of each apollo client state
exportNamespace: '',
}
@@ -34,14 +34,14 @@ const js = ApolloSSR.exportStates(clientsObject, options)
`options` defaults to:
```js
{
const defaultOptions = {
// Global variable name
globalName: '__APOLLO_STATE__',
// Global object on which the variable is set
attachTo: 'window',
// Prefix for the keys of each apollo client state
exportNamespace: '',
// By default we use sanitize js library to prevent XSS
// By default we use sanitize js library to prevent XSS
// pass true here will perform a standard JSON.stringify on the states
useUnsafeSerializer: false,
}
+21 -1
View File
@@ -4,4 +4,24 @@ Extends [useQuery](./use-query.md)
## Additional Return
- `load(document?, variables?, options?)`: function to start querying.
- `load(document?, variables?, options?)`: function to start querying. Returns `Promise<Result>` if it is the first time the query is called, `false` otherwise.
Example:
```js
const { load, refetch } = useLazyQuery(query, variables, options)
function fetchOrRefetch() {
load() || refetch()
}
async function waitForLoad() {
try {
const result = await load()
// do something with result
}
catch (error) {
// handle error
}
}
```
+2 -2
View File
@@ -12,8 +12,8 @@ import { useQuery, useQueryLoading } from '@vue/apollo-composable'
export default {
setup () {
const { result: one } = useQuery(...)
const { result: two } = useQuery(...)
const { result: one } = useQuery(/* ... */)
const { result: two } = useQuery(/* ... */)
const loading = useQueryLoading()
return {
+2 -2
View File
@@ -47,6 +47,6 @@
- `called`: boolean `Ref` holding `true` if the mutation was already called.
- `onDone`: Event hook called when the mutation successfully completes.
- `onDone(handler)`: Event hook called when the mutation successfully completes. Handler is called with: `result` (mutation result) and `context` which is an object with `client` (ApolloClient instance).
- `onError`: Event hook called when an error occurs.
- `onError(handler)`: Event hook called when an error occurs. Handler is called with: `error` and `context` which is an object with `client` (ApolloClient instance).
+4 -5
View File
@@ -28,8 +28,6 @@
- `network-only`: return result from network, fail if network call doesn't succeed, save to cache.
- `no-cache`: return result from network, fail if network call doesn't succeed, don't save to cache.
- `fetchResults`: Whether or not to fetch results.
- `metadata`: Arbitrary metadata stored in the store with this query. Designed for debugging, developer tools, etc.
- `notifyOnNetworkStatusChange`: Whether or not updates to the network status should trigger next on the observer of this query.
@@ -42,6 +40,8 @@
- `throttle`: Throttle interval in ms.
- `keepPreviousResult`: (default: `false`) Whether or not to keep previous result when the query is fetch again (for example when a variable changes). This can be useful to prevent a flash of empty content.
## Return
- `result`: result data object.
@@ -58,7 +58,6 @@
- `subscribeToMore(options)`: Add a subscription to the query, useful to add new data received from the server in real-time. See [Subscription](../guide-composable/subscription#subscribetomore).
- `onResult(handler)`: Event hook called when a new result is available.
- `onError(handler)`: Event hook called when an error occurs.
- `onResult(handler)`: Event hook called when a new result is available. Handler is called with: `result` (query result) and `context` which is an object with `client` (ApolloClient instance).
- `onError(handler)`: Event hook called when an error occurs. Handler is called with: `error` and `context` which is an object with `client` (ApolloClient instance).
+2 -3
View File
@@ -33,7 +33,6 @@
- `variables`: Ref holding the variables object.
- `onResult(handler)`: Event hook called when a new result is available.
- `onError(handler)`: Event hook called when an error occurs.
- `onResult(handler)`: Event hook called when a new result is available. Handler is called with: `result` (new result) and `context` which is an object with `client` (ApolloClient instance).
- `onError(handler)`: Event hook called when an error occurs. Handler is called with: `error` and `context` which is an object with `client` (ApolloClient instance).
+36 -34
View File
@@ -17,7 +17,7 @@ Just how creating a GraphQL schema is the first step toward defining our data mo
Let's create a local schema to describe an item that will serve as a single element of todo-items list. This item should have some text, some property to define if it's already done or not and also an ID to distinguish one todo-item from another. So, it should be an object with three properties:
```js
{
const obj = {
id: 'uniqueId',
text: 'some text',
done: false
@@ -27,9 +27,9 @@ Let's create a local schema to describe an item that will serve as a single elem
Now we're ready to add an `Item` type to our local GraphQL schema.
```js
//main.js
// main.js
import gql from 'graphql-tag';
import gql from 'graphql-tag'
export const typeDefs = gql`
type Item {
@@ -37,7 +37,7 @@ export const typeDefs = gql`
text: String!
done: Boolean!
}
`;
`
```
`gql` here stands for the JavaScript template literal tag that parses GraphQL query strings.
@@ -63,7 +63,7 @@ You can not only create a local schema from scratch but also add a local **virtu
Imagine we have a type `User` in our remote schema:
```js
```gql
type User {
name: String!
age: Int!
@@ -77,7 +77,7 @@ export const schema = gql`
extend type User {
twitter: String
}
`;
`
```
Now, when querying a user, we will need to specify `twitter` field is local:
@@ -89,7 +89,7 @@ const userQuery = gql`
age
twitter @client
}
`;
`
```
## Initializing an Apollo cache
@@ -151,7 +151,7 @@ Querying local cache is very similar to [sending GraphQL queries to remote serve
```js
// App.vue
import gql from 'graphql-tag';
import gql from 'graphql-tag'
const todoItemsQuery = gql`
{
@@ -161,7 +161,7 @@ const todoItemsQuery = gql`
done
}
}
`;
`
```
The main difference with queries to remote API is `@client` directive. This directive specifies that this query should not be executed against remote GraqhQL API. Instead, Apollo client should fetch results from the local cache.
@@ -169,13 +169,13 @@ The main difference with queries to remote API is `@client` directive. This dire
Now, we can use this query in our Vue component as a usual Apollo query:
```js
// App.vue
apollo: {
todoItems: {
query: todoItemsQuery
}
},
export default {
apollo: {
todoItems: {
query: todoItemsQuery
}
},
}
```
## Change local data with mutations
@@ -213,7 +213,7 @@ const checkItemMutation = gql`
mutation($id: ID!) {
checkItem(id: $id) @client
}
`;
`
```
We defined a _local_ mutation (because we have a `@client` directive here) that will accept a unique identifier as a parameter. Now, we need a _resolver_: a function that resolves a value for a type or field in a schema.
@@ -228,13 +228,14 @@ Let's add a resolver to our main file:
const resolvers = {
Mutation: {
checkItem: (_, { id }, { cache }) => {
const data = cache.readQuery({ query: todoItemsQuery });
const currentItem = data.todoItems.find(item => item.id === id);
currentItem.done = !currentItem.done;
cache.writeQuery({ query: todoItemsQuery, data });
return currentItem.done;
const data = cache.readQuery({ query: todoItemsQuery })
const currentItem = data.todoItems.find(item => item.id === id)
currentItem.done = !currentItem.done
cache.writeQuery({ query: todoItemsQuery, data })
return currentItem.done
},
};
}
}
```
What are we doing here?
@@ -259,26 +260,27 @@ const resolvers = {
cache.writeQuery({ query: todoItemsQuery, data });
return currentItem.done;
},
};
},
}
const apolloClient = new ApolloClient({
cache,
typeDefs,
resolvers,
});
})
```
After this, we can use the mutation in our Vue component like normal [mutations](../guide-option/mutations.md):
```js
// App.vue
methods: {
checkItem(id) {
this.$apollo.mutate({
mutation: checkItemMutation,
variables: { id }
});
},
export default {
methods: {
checkItem(id) {
this.$apollo.mutate({
mutation: checkItemMutation,
variables: { id }
})
},
}
}
```
+1 -1
View File
@@ -75,7 +75,7 @@ export default {
description
}
}`,
variables () {
variables() {
return {
id: this.id,
}
+5 -6
View File
@@ -68,7 +68,6 @@ const sourceSchema = `
twitter: String
}
type Query {
allHeroes: [VueHero]
}
@@ -76,14 +75,14 @@ const sourceSchema = `
type Mutation {
addHero(hero: HeroInput!): VueHero!
deleteHero(name: String!): Boolean
}
}
`
```
Next step is to create an executable schema with `graphql-tools` method:
```js
import { makeExecutableSchema } from 'graphql-tools'
...
// ...
const schema = makeExecutableSchema({
typeDefs: sourceSchema,
})
@@ -92,7 +91,7 @@ After this you need to add mock functions to schema:
```js
import { addMockFunctionsToSchema } from 'graphql-tools'
...
// ...
addMockFunctionsToSchema({
schema,
})
@@ -115,7 +114,7 @@ const query = `
Call GraphQL query in the test case, save response to component data and then check if rendered component matches a snapshot:
```js
graphql(schema, query).then(result => {
graphql(schema, query).then((result) => {
wrapper.setData(result.data)
expect(wrapper.element).toMatchSnapshot()
})
@@ -153,4 +152,4 @@ addMockFunctionsToSchema({
```
You can test mutations in the same way.
---
---
+11 -11
View File
@@ -1,6 +1,6 @@
# ApolloQuery
You can use the `ApolloQuery` (or `apollo-query`) component to have watched Apollo queries directly in your template.
You can use the `ApolloQuery` (or `apollo-query`) component to have watched Apollo queries directly in your template.
After reading this page, see the [API Reference](../api/apollo-query.md) for all the possible options.
## Query gql tag
@@ -95,19 +95,19 @@ If you are not using [vue-cli-plugin-apollo](https://github.com/Akryum/vue-cli-p
// vue.config.js
module.exports = {
chainWebpack: config => {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
options.transpileOptions = {
transforms: {
dangerousTaggedTemplateString: true,
},
}
return options
})
.loader('vue-loader')
.tap((options) => {
options.transpileOptions = {
transforms: {
dangerousTaggedTemplateString: true,
},
}
return options
})
}
}
```
+2 -1
View File
@@ -25,7 +25,6 @@ const apolloClient = new ApolloClient({
cache,
uri: 'http://localhost:4042/graphql',
})
```
::: warning
@@ -37,6 +36,8 @@ Use the `@apollo/client/core` import path otherwise you will also import React.
The provider holds the Apollo client instances that can then be used by all the child components.
```js
import { createApolloProvider } from '@vue/apollo-option'
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
})
@@ -66,18 +66,20 @@ See [API Reference](../api/apollo-subscribe-to-more.md) for all the available op
Add a new item to the cache:
```js
methods: {
onMessageAdded (previousResult, { subscriptionData }) {
// The previous result is immutable
const newResult = {
...previousResult,
messages: [
...previousResult.messages,
// Add the question to the list
subscriptionData.data.messageAdded,
],
export default {
methods: {
onMessageAdded(previousResult, { subscriptionData }) {
// The previous result is immutable
const newResult = {
...previousResult,
messages: [
...previousResult.messages,
// Add the question to the list
subscriptionData.data.messageAdded,
],
}
return newResult
}
return newResult
}
}
```
@@ -85,23 +87,26 @@ methods: {
Remove an item from the cache:
```js
methods: {
onMessageAdded (previousResult, { subscriptionData }) {
const removedMessage = subscriptionData.data.messageRemoved
const index = previousResult.messages.findIndex(
m => m.id === removedMessage.id
)
export default {
methods: {
onMessageAdded(previousResult, { subscriptionData }) {
const removedMessage = subscriptionData.data.messageRemoved
const index = previousResult.messages.findIndex(
m => m.id === removedMessage.id
)
if (index === -1) return previousResult
if (index === -1)
return previousResult
// The previous result is immutable
const newResult = {
...previousResult,
messages: [...previousResult.messages],
// The previous result is immutable
const newResult = {
...previousResult,
messages: [...previousResult.messages],
}
// Remove the question from the list
newResult.messages.splice(index, 1)
return newResult
}
// Remove the question from the list
newResult.messages.splice(index, 1)
return newResult
}
}
```
@@ -61,14 +61,16 @@ When using Apollo Link, the ability to handle network errors is way more powerfu
import { onError } from '@apollo/client/link/error'
const link = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
)
}
if (networkError) console.log(`[Network error]: ${networkError}`)
if (networkError)
console.log(`[Network error]: ${networkError}`)
})
```
@@ -78,7 +80,7 @@ You can also use the `logErrorMessages` function from the `@vue/apollo-util` pac
import { onError } from '@apollo/client/link/error'
import { logErrorMessages } from '@vue/apollo-util'
const link = onError(error => {
const link = onError((error) => {
logErrorMessages(error)
})
```
@@ -93,7 +95,7 @@ If you are using Webpack or Vue CLI, it's a good idea to only use it in developm
import { onError } from '@apollo/client/link/error'
import { logErrorMessages } from '@vue/apollo-util'
const link = onError(error => {
const link = onError((error) => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error)
}
@@ -105,19 +107,19 @@ That way it will be dropped when compiling the project for production.
Full example:
```js
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import { onError } from '@apollo/client/link/error'
import { logErrorMessages } from '@vue/apollo-util'
const cache = new InMemoryCache()
// HTTP connection to the API
let httpLink = createHttpLink({
const httpLink = createHttpLink({
uri: 'http://localhost:4242/graphql',
})
// Handle errors
const errorLink = onError(error => {
const errorLink = onError((error) => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error)
}
@@ -120,8 +120,8 @@ export const entryFragment = gql`
If our fragments include sub-fragments then we can pass them into the `gql` helper:
```js
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'
import { entryFragment as RepoInfoEntryFragment } from './RepoInfo.vue'
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'
export const entryFragment = gql`
fragment FeedEntry on Entry {
@@ -216,11 +216,11 @@ Read the documentation about how to [extract possibleTypes automatically](https:
2. Use `possibleTypes.json` to configure your cache during construction. Then, you pass your newly configured cache to `ApolloClient` to complete the process.
```js
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import possibleTypes from './possibleTypes.json'
const cache = new InMemoryCache({ possibleTypes })
const httpLink = createHttpLink({ uri });
const httpLink = createHttpLink({ uri })
const client = new ApolloClient({
cache,
@@ -487,9 +487,9 @@ const { mutate: sendMessage, error: sendMessageError } = useMutation(gql`
This is called when the mutation successfully completes.
```js
const { onDone } = useMutation(...)
const { onDone } = useMutation(/* ... */)
onDone(result => {
onDone((result) => {
console.log(result.data)
})
```
@@ -575,9 +575,9 @@ This is triggered when an error occurs during the mutation.
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useMutation(...)
const { onError } = useMutation(/* ... */)
onError(error => {
onError((error) => {
logErrorMessages(error)
})
```
@@ -11,7 +11,7 @@ In this article, we'll cover the technical details of using Apollo to implement
In Apollo, the easiest way to do pagination is with a function called `fetchMore`, which is returned by the `useQuery` composition function. This basically allows you to do a new GraphQL query and merge the result into the original result.
```js
const { fetchMore } = useQuery(...)
const { fetchMore } = useQuery(/* ... */)
```
You can specify what query and variables to use for the new query, and how to merge the new query result with the existing data on the client. How exactly you do that will determine what kind of pagination you are implementing.
+107 -11
View File
@@ -461,7 +461,7 @@ This also means you can pass `props` from `setup` directly, since `props` is alr
export default {
props: ['id'],
setup (props) {
setup(props) {
const { result } = useQuery(gql`
query getUserById ($id: ID!) {
user (id: $id) {
@@ -525,7 +525,7 @@ const { result } = useQuery(gql`
id: id.value
}))
function selectUser (id) {
function selectUser(id) {
id.value = id
}
```
@@ -602,7 +602,7 @@ const { result } = useQuery(gql`
enabled: enabled.value,
}))
function enableQuery () {
function enableQuery() {
enabled.value = true
}
```
@@ -700,6 +700,63 @@ export default {
</template>
```
### Providing new variables to `refetch`
You call `refetch` with a new set of variables like so:
```vue{41}
<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'
export default {
setup () {
const { result, loading, error, refetch } = useQuery(gql`
query getUsers($search: String) {
users(search: $search) {
id
firstname
lastname
email
}
}
`)
const users = computed(() => result.value?.users)
return {
users,
loading,
error,
refetch,
}
},
}
</script>
<template>
<div v-if="loading">Loading...</div>
<div v-else-if="error">Error: {{ error.message }}</div>
<ul v-else-if="users">
<li v-for="user of users" :key="user.id">
{{ user.firstname }} {{ user.lastname }}
</li>
<button
@click="refetch({ search: 'some search input' })"
>
Search "some search input"
</button>
</ul>
</template>
```
::: warning
If you provide new values for **some** of your original query's variables but not **all** of them, `refetch` uses each omitted variable's original value.
:::
## Event hooks
`useQuery` returns event hooks allowing you to execute code when a specific event occurs.
@@ -709,9 +766,9 @@ export default {
This is called whenever a new result is available.
```js
const { onResult } = useQuery(...)
const { onResult } = useQuery(/* ... */)
onResult(queryResult => {
onResult((queryResult) => {
console.log(queryResult.data)
console.log(queryResult.loading)
console.log(queryResult.networkStatus)
@@ -734,9 +791,9 @@ useQuery(gql`
It is triggered when an error occurs:
```js
const { onError } = useQuery(...)
const { onError } = useQuery(/* ... */)
onError(error => {
onError((error) => {
console.log(error.graphQLErrors)
console.log(error.networkError)
})
@@ -747,9 +804,9 @@ You can use the `logErrorMessages` function from the `@vue/apollo-util` package
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useQuery(...)
const { onError } = useQuery(/* ... */)
onError(error => {
onError((error) => {
logErrorMessages(error)
})
```
@@ -763,9 +820,9 @@ If you are using Webpack or Vue CLI, it's a good idea to only use it in developm
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useQuery(...)
const { onError } = useQuery(/* ... */)
onError(error => {
onError((error) => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error)
}
@@ -824,3 +881,42 @@ export default {
</div>
</template>
```
### Getting the result
`load()` returns a Promise to the result of the first request if it's the first time the query is activated.
```js
const { result, load, refetch } = useLazyQuery(gql`
query list {
list
}
`)
// ...
async function myLoad() {
try {
const result = await load()
}
catch (e) {
// Handle error
}
}
```
### Refetch lazy query
`load()` returns `false` if it is not the first time the query is activated. You can use this to refetch the query with `refetch()` in case the user clicks on the button again, meaning `load()` returns `false`.
```js
const { result, load, refetch } = useLazyQuery(gql`
query list {
list
}
`)
// ...
function loadOrRefetch() {
load() || refetch()
}
```
+14 -9
View File
@@ -21,11 +21,11 @@ yarn add @vue/apollo-composable
In your root instance, you need to provide a default Apollo Client instance:
```js
import { provide } from '@vue/composition-api'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { provide } from '@vue/composition-api'
const app = new Vue({
setup () {
setup() {
provide(DefaultApolloClient, apolloClient)
},
@@ -40,11 +40,11 @@ In the rest of the guide, we will show code examples with Vue 3. If you need Vue
### Vue 3
```js
import { createApp, provide, h } from 'vue'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { createApp, h, provide } from 'vue'
const app = createApp({
setup () {
setup() {
provide(DefaultApolloClient, apolloClient)
},
@@ -57,11 +57,11 @@ const app = createApp({
You can also provide multiple Apollo Client instances to be used in your application. In this case, it's recommended to provide a `default` one:
```js
import { provide } from 'vue'
import { ApolloClients } from '@vue/apollo-composable'
import { provide } from 'vue'
const app = new Vue({
setup () {
setup() {
provide(ApolloClients, {
default: apolloClient,
})
@@ -90,15 +90,20 @@ When using e.g. `useQuery` outside of vue contexts, the clients cannot be inject
Use `provideApolloClient` for a single default client:
```js
import { provideApolloClient } from "@vue/apollo-composable";
import { provideApolloClient } from '@vue/apollo-composable'
provideApolloClient(apolloClient)
const query = provideApolloClient(apolloClient)(() => useQuery(gql`
query hello {
hello
}
`))
const hello = computed(() => query.result.value?.hello ?? '')
```
Use `provideApolloClients` for multiple clients:
```js
import { provideApolloClients } from "@vue/apollo-composable";
import { provideApolloClients } from '@vue/apollo-composable'
provideApolloClients({
default: apolloClient,
@@ -55,40 +55,47 @@ A future version of Apollo or GraphQL might include support for live queries, wh
In this article, we'll explain how to set it up on the client, but you'll also need a server implementation. You can [read about how to use subscriptions with a JavaScript server](https://www.apollographql.com/docs/graphql-subscriptions/setup), or enjoy subscriptions set up out of the box if you are using a GraphQL backend as a service like [Graphcool](https://www.graph.cool/docs/tutorials/worldchat-subscriptions-example-ui0eizishe/).
Let's look at how to add support for this transport to Apollo Client.
The GraphQL spec does not define a specific protocol for sending subscription requests. The first popular JavaScript library to implement subscriptions over WebSocket is called *subscriptions-transport-ws*. This library is no longer actively maintained. Its successor is a library called *graphql-ws*. The two libraries do not use the same WebSocket subprotocol, so you need to make sure that your server and clients all use the same library.
First, initialize a GraphQL web socket link:
Apollo Client supports both *graphql-ws* and *subscriptions-transport-ws*. Apollo [documentation](https://www.apollographql.com/docs/react/data/subscriptions/#choosing-a-subscription-library) suggest to use the newer library *graphql-ws*, but in case you need it, here its explained how to do it with both.
### The new library: **graphql-ws**
Let's look at how to add support for this transport to Apollo Client using a link set up for newest library [graphql-ws](https://github.com/enisdenjo/graphql-ws). First, install:
```bash
npm install graphql-ws
```
Then initialize a GraphQL web socket link:
```js
import { WebSocketLink } from "@apollo/client/link/ws"
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { createClient } from 'graphql-ws'
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
options: {
reconnect: true
}
})
const wsLink = new GraphQLWsLink(
createClient({
url: 'ws://localhost:4000/graphql',
})
)
```
We need to either use the `WebSocketLink` or the `HttpLink` depending on the operation type:
We need to either use the `GraphQLWsLink` or the `HttpLink` depending on the operation type:
```js
import { HttpLink, split } from "@apollo/client/core"
import { WebSocketLink } from "@apollo/client/link/ws"
import { getMainDefinition } from "@apollo/client/utilities"
import { HttpLink, split } from '@apollo/client/core'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions' // <-- This one uses graphql-ws
import { getMainDefinition } from '@apollo/client/utilities'
import { createClient } from 'graphql-ws'
// Create an http link:
const httpLink = new HttpLink({
uri: "http://localhost:3000/graphql"
uri: 'http://localhost:3000/graphql'
})
// Create a WebSocket link:
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
options: {
reconnect: true
}
})
// Create a GraphQLWsLink link:
const wsLink = new GraphQLWsLink(
createClient({
url: 'ws://localhost:5000/',
})
)
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
@@ -97,16 +104,40 @@ const link = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
// Create the apollo client with cache implementation.
const apolloClient = new ApolloClient({
link,
cache: new InMemoryCache(),
})
```
The apollo client is the one that will be provided to the vue app, see the [setup section](https://v4.apollo.vuejs.org/guide-composable/setup.html) for more details.
Now, queries and mutations will go over HTTP as normal, but subscriptions will be done over the websocket transport.
### The old library: **subscriptions-transport-ws**
If you need to use [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws) because your server still uses that protocol, instead of installing graphql-ws, install:
```bash
npm install subscriptions-transport-ws
```
And then initialize a GraphQL web socket link:
```js
import { WebSocketLink } from '@apollo/client/link/ws' // <-- This one uses subscriptions-transport-ws
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
options: {
reconnect: true
}
})
```
The rest of the configuration (creating a httpLink and link) is the same as described above for graphql-ws.
## useSubscription
@@ -235,7 +266,7 @@ With a ref:
```js
const variables = ref({
channelId: "abc"
channelId: 'abc'
})
const { result } = useSubscription(
@@ -255,7 +286,7 @@ With a reactive object:
```js
const variables = reactive({
channelId: "abc"
channelId: 'abc'
})
const { result } = useSubscription(
@@ -274,7 +305,7 @@ const { result } = useSubscription(
With a function (which will automatically be made reactive):
```js
const channelId = ref("abc")
const channelId = ref('abc')
const { result } = useSubscription(
gql`
@@ -307,7 +338,7 @@ const { result } = useSubscription(
`,
null,
{
fetchPolicy: "no-cache"
fetchPolicy: 'no-cache'
}
)
```
@@ -326,7 +357,7 @@ const { result } = useSubscription(
`,
null,
() => ({
fetchPolicy: "no-cache"
fetchPolicy: 'no-cache'
})
)
```
@@ -360,7 +391,7 @@ function enableSub() {
You can retrieve the loading and error stats from `useSubscription`:
```js
const { loading, error } = useSubscription(...)
const { loading, error } = useSubscription(/* ... */)
```
### Event hooks
@@ -370,9 +401,9 @@ const { loading, error } = useSubscription(...)
This is called when a new result is received from the server:
```js
const { onResult } = useSubscription(...)
const { onResult } = useSubscription(/* ... */)
onResult(result => {
onResult((result, context) => {
console.log(result.data)
})
```
@@ -384,13 +415,50 @@ This is triggered when an error occurs:
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useSubscription(...)
const { onError } = useSubscription(/* ... */)
onError(error => {
onError((error, context) => {
logErrorMessages(error)
})
```
### Update the cache
Using `onResult`, you can update the Apollo cache with the new data:
```js
const { onResult } = useSubscription(/* ... */)
onResult((result, { client }) => {
const query = {
query: gql`query getMessages ($channelId: ID!) {
messages(channelId: $channelId) {
id
text
}
}`,
variables: {
channelId: '123',
},
}
// Read the query
let data = client.readQuery(query)
// Update cached data
data = {
...data,
messages: [...data.messages, result.data.messageAdded],
}
// Write back the new result for the query
client.writeQuery({
...query,
data,
})
})
```
## subscribeToMore
With GraphQL subscriptions your client will be alerted on push from the server and you should choose the pattern that fits your application the most:
@@ -545,7 +613,7 @@ subscribeToMore(() => ({
channelId: props.channelId
},
updateQuery: (previousResult, { subscriptionData }) => {
const tmp = [...previousResult]
const tmp = [...previousResult]
tmp.messages.push(subscriptionData.data.messageAdded)
return tmp
}
@@ -557,15 +625,16 @@ subscribeToMore(() => ({
In many cases it is necessary to authenticate clients before allowing them to receive subscription results. To do this, the `SubscriptionClient` constructor accepts a `connectionParams` field, which passes a custom object that the server can use to validate the connection before setting up any subscriptions.
```js
import { WebSocketLink } from "@apollo/client/link/ws"
import { WebSocketLink } from '@apollo/client/link/ws'
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
options: {
reconnect: true,
connectionParams: {
authToken: user.authToken,
authToken: user.authToken,
},
}
})
```
@@ -19,8 +19,8 @@ const defaultOptions = {
}
const clientAOptions = {
// You can use `https` for secure connection (recommended in production)
httpEndpoint: 'http://localhost:4000/graphql',
// You can use `https` for secure connection (recommended in production)
httpEndpoint: 'http://localhost:4000/graphql',
}
const clientBOptions = {
@@ -28,28 +28,29 @@ const clientBOptions = {
}
// Call this in the Vue app file
export function createProvider (options = {}) {
const createA= createApolloClient({
export function createProvider(options = {}) {
const createA = createApolloClient({
...defaultOptions,
...clientAOptions,
});
})
const createB = createApolloClient({
...defaultOptions,
...clientBOptions,
});
})
const a = createA.apolloClient;
const b = createB.apolloClient;
const a = createA.apolloClient
const b = createB.apolloClient
// Create vue apollo provider
const apolloProvider = createApolloProvider({
clients: {
a,
b
}
},
defaultClient: a,
})
})
}
```
In the component `apollo` option, you can define the client for all the queries, subscriptions and mutations with `$client` (only for this component):
@@ -65,8 +66,12 @@ export default {
You can also specify the client in individual queries, subscriptions and mutations with the `client` property in the options:
```js
tags: {
query: gql`...`,
client: 'b',
export default {
apollo: {
tags: {
query: gql`...`,
client: 'b',
}
}
}
```
+57 -55
View File
@@ -11,69 +11,74 @@ You shouldn't send the `__typename` fields in the variables, so it is not recomm
:::
```js
methods: {
addTag() {
export default {
methods: {
addTag() {
// We save the user input in case of an error
const newTag = this.newTag
// We clear it early to give the UI a snappy feel
this.newTag = ''
// Call to the graphql mutation
this.$apollo.mutate({
const newTag = this.newTag
// We clear it early to give the UI a snappy feel
this.newTag = ''
// Call to the graphql mutation
this.$apollo.mutate({
// Query
mutation: gql`mutation ($label: String!) {
mutation: gql`mutation ($label: String!) {
addTag(label: $label) {
id
label
}
}`,
// Parameters
variables: {
label: newTag,
},
// Update the cache with the result
// The query will be updated with the optimistic response
// and then with the real result of the mutation
update: (store, { data: { addTag } }) => {
// Read the data from our cache for this query.
let data = store.readQuery({ query: TAGS_QUERY })
// Add our tag from the mutation to the end
data = {
...data,
tags: [
...data.tags,
addTag,
],
}
// Write our data back to the cache.
store.writeQuery({ query: TAGS_QUERY, data })
},
// Optimistic UI
// Will be treated as a 'fake' result as soon as the request is made
// so that the UI can react quickly and the user be happy
optimisticResponse: {
__typename: 'Mutation',
addTag: {
__typename: 'Tag',
id: -1,
// Parameters
variables: {
label: newTag,
},
},
}).then((data) => {
// Update the cache with the result
// The query will be updated with the optimistic response
// and then with the real result of the mutation
update: (store, { data: { addTag } }) => {
// Read the data from our cache for this query.
let data = store.readQuery({ query: TAGS_QUERY })
// Add our tag from the mutation to the end
data = {
...data,
tags: [
...data.tags,
addTag,
],
}
// Write our data back to the cache.
store.writeQuery({ query: TAGS_QUERY, data })
},
// Optimistic UI
// Will be treated as a 'fake' result as soon as the request is made
// so that the UI can react quickly and the user be happy
optimisticResponse: {
__typename: 'Mutation',
addTag: {
__typename: 'Tag',
id: -1,
label: newTag,
},
},
}).then((data) => {
// Result
console.log(data)
}).catch((error) => {
console.log(data)
}).catch((error) => {
// Error
console.error(error)
// We restore the initial user input
this.newTag = newTag
})
console.error(error)
// We restore the initial user input
this.newTag = newTag
})
},
},
},
}
```
## Server-side example
```js
// Fake word generator
import faker from 'faker'
export const schema = `
type Tag {
id: Int
@@ -94,18 +99,15 @@ schema {
}
`
// Fake word generator
import faker from 'faker'
// Let's generate some tags
var id = 0
var tags = []
let id = 0
const tags = []
for (let i = 0; i < 42; i++) {
addTag(faker.random.word())
}
function addTag (label) {
let t = {
function addTag(label) {
const t = {
id: id++,
label,
}
@@ -115,12 +117,12 @@ function addTag (label) {
export const resolvers = {
Query: {
tags (root, args, context) {
tags(root, args, context) {
return tags
},
},
Mutation: {
addTag (root, { label }, context) {
addTag(root, { label }, context) {
console.log(`adding tag '${label}'`)
return addTag(label)
},
+127 -96
View File
@@ -23,12 +23,14 @@ import gql from 'graphql-tag'
Put the [gql](https://github.com/apollographql/graphql-tag) query directly as the value:
```js
apollo: {
export default {
apollo: {
// Simple query that will update the 'hello' vue property
hello: gql`query {
hello: gql`query {
hello
}`,
},
},
}
```
You can then access the smart query with `this.$apollo.queries.<name>`.
@@ -36,12 +38,14 @@ You can then access the smart query with `this.$apollo.queries.<name>`.
You can initialize the property in your vue component's `data` hook:
```js
data () {
return {
export default {
data() {
return {
// Initialize your apollo data
hello: '',
hello: '',
}
},
},
}
```
Server-side, add the corresponding schema and resolver:
@@ -59,7 +63,7 @@ schema {
export const resolvers = {
Query: {
hello (root, args, context) {
hello(root, args, context) {
return 'Hello world!'
},
},
@@ -86,22 +90,26 @@ You can then use your property as usual in your vue component:
Please note that a common beginner's mistake is to use a data name different from the field name in the query, e.g.:
```js
apollo: {
world: gql`query {
export default {
apollo: {
world: gql`query {
hello
}`
}
}
```
Notice how `world` is different from `hello`; `vue-apollo` won't guess which data you want to put in the component from the query result. By default, it will just try the name you are using for the data in the component (which is the key in the `apollo` object), in this case `world`. If the names don't match, you can use `update` option to tell `vue-apollo` what to use as data from the result:
```js
apollo: {
world: {
query: gql`query {
export default {
apollo: {
world: {
query: gql`query {
hello
}`,
update: data => data.hello
update: data => data.hello
}
}
}
```
@@ -109,10 +117,12 @@ apollo: {
You can also rename the field in the GraphQL document directly:
```js
apollo: {
world: gql`query {
export default {
apollo: {
world: gql`query {
world: hello
}`
}
}
```
@@ -123,20 +133,22 @@ In this example, we rename the `hello` field to `world` so that `vue-apollo` can
You can add variables (and other parameters) to your `gql` query by declaring `query` and `variables` in an object instead of just the GraphQL query:
```js
export default {
// Apollo-specific options
apollo: {
apollo: {
// Query with parameters
ping: {
ping: {
// gql query
query: gql`query PingMessage($message: String!) {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// Static parameters
variables: {
message: 'Meow',
// Static parameters
variables: {
message: 'Meow',
},
},
},
},
}
```
You can use the apollo `watchQuery` options in the object, like:
@@ -149,30 +161,34 @@ See the [apollo doc](https://www.apollographql.com/docs/react/api/core/apolloCli
For example, you could add the `fetchPolicy` apollo option like this:
```js
apollo: {
export default {
apollo: {
// Query with parameters
ping: {
query: gql`query PingMessage($message: String!) {
ping: {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
variables: {
message: 'Meow'
variables: {
message: 'Meow'
},
// Additional options here
fetchPolicy: 'cache-and-network',
},
// Additional options here
fetchPolicy: 'cache-and-network',
},
},
}
```
Again, you can initialize your property in your vue component:
```js
data () {
return {
export default {
data() {
return {
// Initialize your apollo data
ping: '',
}
},
ping: '',
}
},
}
```
Server-side, add the corresponding schema and resolver:
@@ -190,7 +206,7 @@ schema {
export const resolvers = {
Query: {
ping (root, { message }, context) {
ping(root, { message }, context) {
return `Answering ${message}`
},
},
@@ -215,22 +231,24 @@ And then use it in your vue component:
Use a function instead to make the parameters reactive with vue properties:
```js
export default {
// Apollo-specific options
apollo: {
apollo: {
// Query with parameters
ping: {
query: gql`query PingMessage($message: String!) {
ping: {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// Reactive parameters
variables () {
// Reactive parameters
variables() {
// Use vue reactive properties here
return {
return {
message: this.pingInput,
}
}
},
},
},
},
}
```
This will re-fetch the query each time a parameter changes, for example:
@@ -266,24 +284,26 @@ Or for this specific `ping` query:
You can use a function which will be called once when the component is created and it must return the option object:
```js
export default {
// Apollo-specific options
apollo: {
apollo: {
// Query with parameters
ping () {
ping() {
// This is called once when the component is created
// It must return the option object
return {
return {
// gql query
query: gql`query PingMessage($message: String!) {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// Static parameters
variables: {
message: 'Meow',
},
}
// Static parameters
variables: {
message: 'Meow',
},
}
},
},
},
}
```
::: tip
@@ -295,31 +315,36 @@ This also works for [subscriptions](./subscriptions.md).
You can use a function for the `query` option. This will update the graphql query definition automatically:
```js
// The featured tag can be either a random tag or the last added tag
featuredTag: {
query () {
// Here you can access the component instance with 'this'
if (this.showTag === 'random') {
return gql`{
export default {
apollo: {
// The featured tag can be either a random tag or the last added tag
featuredTag: {
query() {
// Here you can access the component instance with 'this'
if (this.showTag === 'random') {
return gql`{
randomTag {
id
label
type
}
}`
} else if (this.showTag === 'last') {
return gql`{
}
else if (this.showTag === 'last') {
return gql`{
lastTag {
id
label
type
}
}`
}
},
// We need this to assign the value of the 'featuredTag' component property
update: data => data.randomTag || data.lastTag,
},
}
},
// We need this to assign the value of the 'featuredTag' component property
update: data => data.randomTag || data.lastTag,
},
}
}
```
::: tip
@@ -331,28 +356,30 @@ This also works for [subscriptions](./subscriptions.md).
If the query is skipped, it will disable it and the result will not be updated anymore. You can use the `skip` option:
```js
export default {
// Apollo-specific options
apollo: {
tags: {
apollo: {
tags: {
// GraphQL Query
query: gql`query tagList ($type: String!) {
query: gql`query tagList ($type: String!) {
tags(type: $type) {
id
label
}
}`,
// Reactive variables
variables () {
return {
type: this.type,
}
},
// Disable the query
skip () {
return this.skipQuery
// Reactive variables
variables() {
return {
type: this.type,
}
},
// Disable the query
skip() {
return this.skipQuery
},
},
},
},
}
```
Here, `skip` will be called automatically when the `skipQuery` component property changes.
@@ -370,24 +397,29 @@ If the query `skip` becomes `false`, the query will automatically execute again.
Here is a reactive query example using polling:
```js
export default {
// Apollo-specific options
apollo: {
apollo: {
// 'tags' data property on vue instance
tags: {
query: gql`query tagList {
tags: {
query: gql`query tagList {
tags {
id,
label
}
}`,
pollInterval: 300, // ms
pollInterval: 300, // ms
},
},
},
}
```
Here is how the server-side looks like:
```js
// Fake word generator
import casual from 'casual'
export const schema = `
type Tag {
id: Int
@@ -403,18 +435,15 @@ schema {
}
`
// Fake word generator
import casual from 'casual'
// Let's generate some tags
var id = 0
var tags = []
let id = 0
const tags = []
for (let i = 0; i < 42; i++) {
addTag(casual.word)
}
function addTag (label) {
let t = {
function addTag(label) {
const t = {
id: id++,
label,
}
@@ -436,10 +465,12 @@ export const resolvers = {
You can manually add a smart query with the `$apollo.addSmartQuery(key, options)` method:
```js
created () {
this.$apollo.addSmartQuery('comments', {
export default {
created() {
this.$apollo.addSmartQuery('comments', {
// Same options like above
})
})
}
}
```
-1
View File
@@ -25,7 +25,6 @@ const apolloClient = new ApolloClient({
cache,
uri: 'http://localhost:4042/graphql',
})
```
::: warning
@@ -1,9 +1,8 @@
# Special options
The special options begin with `$` in the `apollo` object.
- `$skip` to disable all queries and subscriptions (see below)
- `$skipAll` to disable all queries and subscriptions (see below)
- `$skipAllQueries` to disable all queries (see below)
- `$skipAllSubscriptions` to disable all subscriptions (see below)
- `$deep` to watch with `deep: true` on the properties above when a function is provided
@@ -59,17 +58,21 @@ this.$apollo.skipAll = true
You can also declare these properties in the `apollo` option of the component. They can be booleans:
```js
apollo: {
$skipAll: true
export default {
apollo: {
$skipAll: true
}
}
```
Or reactive functions:
```js
apollo: {
$skipAll () {
return this.foo === 42
export default {
apollo: {
$skipAll() {
return this.foo === 42
}
}
}
```
+151 -97
View File
@@ -1,26 +1,47 @@
# Subscriptions
## Setup
*For the server implementation, you can take a look at [this simple example](https://github.com/Akryum/apollo-server-example).*
## Client setup
The GraphQL spec does not define a specific protocol for sending subscription requests. The first popular JavaScript library to implement subscriptions over WebSocket is called *subscriptions-transport-ws*. This library is no longer actively maintained. Its successor is a library called *graphql-ws*. The two libraries do not use the same WebSocket subprotocol, so you need to make sure that your server and clients all use the same library.
Apollo Client supports both *graphql-ws* and *subscriptions-transport-ws*. Apollo [documentation](https://www.apollographql.com/docs/react/data/subscriptions/#choosing-a-subscription-library) suggest to use the newer library *graphql-ws*, but in case you need it, here its explained how to do it with both.
### The new library: **graphql-ws**
Let's look at how to add support for this transport to Apollo Client using a link set up for newest library [graphql-ws](https://github.com/enisdenjo/graphql-ws). First, install:
```bash
npm install graphql-ws
```
Then initialize a GraphQL web socket link:
```js
import { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { createClient } from 'graphql-ws'
const wsLink = new GraphQLWsLink(
createClient({
url: 'ws://localhost:4000/graphql',
})
)
```
We need to either use the `GraphQLWsLink` or the `HttpLink` depending on the operation type:
```js
import { HttpLink, split } from '@apollo/client/core'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions' // <-- This one uses graphql-ws
import { getMainDefinition } from '@apollo/client/utilities'
// Create an http link:
const httpLink = new HttpLink({
// You should use an absolute URL here
uri: 'http://localhost:3020/graphql',
uri: 'http://localhost:3000/graphql'
})
// Create the subscription websocket link
const wsLink = new WebSocketLink({
uri: 'ws://localhost:3000/subscriptions',
options: {
reconnect: true,
},
})
// Create a GraphQLWsLink link:
const wsLink = new GraphQLWsLink(
createClient({
url: 'ws://localhost:5000/',
})
)
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
@@ -28,47 +49,70 @@ const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query)
return definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
return (
definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
// Create the apollo client
// Create the apollo client with cache implementation.
const apolloClient = new ApolloClient({
link,
cache: new InMemoryCache(),
connectToDevTools: true,
})
```
The apollo client is the one that will be provided to the vue app, see the [setup section](https://v4.apollo.vuejs.org/guide-composable/setup.html) for more details.
Now, queries and mutations will go over HTTP as normal, but subscriptions will be done over the websocket transport.
### The old library: **subscriptions-transport-ws**
If you need to use [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws) because your server still uses that protocol, instead of installing graphql-ws, install:
```bash
npm install subscriptions-transport-ws
```
And then initialize a GraphQL web socket link:
```js
import { WebSocketLink } from '@apollo/client/link/ws' // <-- This one uses subscriptions-transport-ws
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
options: {
reconnect: true
}
})
```
The rest of the configuration (creating a httpLink and link) is the same as described above for graphql-ws.
## Subscribe To More
If you need to update a smart query result from a subscription, the best way is using the `subscribeToMore` smart query method. It will create [Smart Subscriptions](../api/smart-subscription.md) that are linked to the smart query. Just add a `subscribeToMore` to your smart query:
```js
apollo: {
tags: {
query: TAGS_QUERY,
subscribeToMore: {
document: gql`subscription name($param: String!) {
export default {
apollo: {
tags: {
query: TAGS_QUERY,
subscribeToMore: {
document: gql`subscription name($param: String!) {
itemAdded(param: $param) {
id
label
}
}`,
// Variables passed to the subscription. Since we're using a function,
// they are reactive
variables () {
return {
param: this.param,
}
},
// Mutate the previous result
updateQuery: (previousResult, { subscriptionData }) => {
// Variables passed to the subscription. Since we're using a function,
// they are reactive
variables() {
return {
param: this.param,
}
},
// Mutate the previous result
updateQuery: (previousResult, { subscriptionData }) => {
// Here, return the new result from the previous with the new data
},
},
}
}
}
}
@@ -164,35 +208,37 @@ The methods below are suitable for a 'notify' use case.
You can declare [Smart Subscriptions](../api/smart-subscription.md) in the `apollo` option with the `$subscribe` keyword:
```js
apollo: {
export default {
apollo: {
// Subscriptions
$subscribe: {
$subscribe: {
// When a tag is added
tagAdded: {
query: gql`subscription tags($type: String!) {
tagAdded: {
query: gql`subscription tags($type: String!) {
tagAdded(type: $type) {
id
label
type
}
}`,
// Reactive variables
variables () {
// Reactive variables
variables() {
// This works just like regular queries
// and will re-subscribe with the right variables
// each time the values change
return {
type: this.type,
}
},
// Result hook
// Don't forget to destructure `data`
result ({ data }) {
console.log(data.tagAdded)
return {
type: this.type,
}
},
// Result hook
// Don't forget to destructure `data`
result({ data }) {
console.log(data.tagAdded)
},
},
},
},
},
}
```
You can then access the subscription with `this.$apollo.subscriptions.<name>`.
@@ -205,27 +251,29 @@ When server supports live queries and uses subscriptions to update them, like [H
use simple subscriptions for reactive queries:
```js
data () {
return {
tags: [],
};
},
apollo: {
$subscribe: {
tags: {
query: gql`subscription {
export default {
data() {
return {
tags: [],
}
},
apollo: {
$subscribe: {
tags: {
query: gql`subscription {
tags {
id
label
type
}
}`,
result ({ data }) {
this.tags = data.tags;
result({ data }) {
this.tags = data.tags
},
},
},
},
},
}
```
## Skipping the subscription
@@ -233,37 +281,39 @@ apollo: {
If the subscription is skipped, it will disable it and it will not be updated anymore. You can use the `skip` option:
```js
export default {
// Apollo-specific options
apollo: {
apollo: {
// Subscriptions
$subscribe: {
$subscribe: {
// When a tag is added
tags: {
query: gql`subscription tags($type: String!) {
tags: {
query: gql`subscription tags($type: String!) {
tagAdded(type: $type) {
id
label
type
}
}`,
// Reactive variables
variables () {
return {
type: this.type,
// Reactive variables
variables() {
return {
type: this.type,
}
},
// Result hook
result(data) {
// Let's update the local data
this.tags.push(data.tagAdded)
},
// Skip the subscription
skip() {
return this.skipSubscription
}
},
// Result hook
result (data) {
// Let's update the local data
this.tags.push(data.tagAdded)
},
// Skip the subscription
skip () {
return this.skipSubscription
}
},
},
},
}
```
Here, `skip` will be called automatically when the `skipSubscription` component property changes.
@@ -279,10 +329,12 @@ this.$apollo.subscriptions.tags.skip = true
You can manually add a smart subscription with the `$apollo.addSmartSubscription(key, options)` method:
```js
created () {
this.$apollo.addSmartSubscription('tagAdded', {
export default {
created() {
this.$apollo.addSmartSubscription('tagAdded', {
// Same options like '$subscribe' above
})
})
}
}
```
@@ -295,8 +347,9 @@ Internally, this method is called for each entry of the `$subscribe` object in t
Use the `$apollo.subscribe()` method to subscribe to a GraphQL subscription that will get killed automatically when the component is destroyed. It will **NOT** create a Smart Subscription.
```js
mounted () {
const subQuery = gql`subscription tags($type: String!) {
export default {
mounted() {
const subQuery = gql`subscription tags($type: String!) {
tagAdded(type: $type) {
id
label
@@ -304,20 +357,21 @@ mounted () {
}
}`
const observer = this.$apollo.subscribe({
query: subQuery,
variables: {
type: 'City',
},
})
const observer = this.$apollo.subscribe({
query: subQuery,
variables: {
type: 'City',
},
})
observer.subscribe({
next (data) {
console.log(data)
},
error (error) {
console.error(error)
},
})
},
observer.subscribe({
next(data) {
console.log(data)
},
error(error) {
console.error(error)
},
})
},
}
```
+9
View File
@@ -46,3 +46,12 @@ Is your company using `vue-apollo` or `vue-cli-plugin-apollo` to build awesome a
[<img src="https://conf.vuejs.org/img/logo-48.png" alt="icon" width="16" height="16"/> VueConf 2017 demo](https://github.com/Akryum/vueconf-2017-demo) &amp; [slides](http://slides.com/akryum/graphql#/)
[<img src="https://github.com/fluidicon.png" alt="icon" width="16" height="16"/> Devfest Summit Example](https://github.com/Akryum/devfest-nantes-2017) (with lots of features like SSR, OAuth, Realtime updates, Apollo Engine...)
<style scoped>
a > img {
max-width: none;
width: 16px;
height: 16px;
display: inline-block;
}
</style>
+1 -1
View File
@@ -94,7 +94,7 @@ If you are using Webstorm, it's recommended to install the [JS GraphQL extension
Then configure it by creating a `.graphqlconfig` file in the root folder of the Vue project:
```graphqlconfig
```json
{
"name": "Untitled GraphQL Schema",
"schemaPath": "./path/to/schema.graphql",
+16 -7
View File
@@ -1,22 +1,31 @@
---
home: true
sidebar: false
heroImage: /logo.png
actionText: Get Started →
actionLink: /guide/
layout: home
hero:
name: Vue Apollo
text: GraphQL
tagline: Effortless GraphQL in your Vue app!
image:
src: /hero.svg
alt: Vue Apollo
actions:
- theme: brand
text: Get Started
link: /guide/
features:
- title: Automatic updates
details: Don't think about updating the UI or refetching the queries!
icon: ✨
- title: Supports all Vue APIs
details: Option API, Composition API or Components
icon: 🧩
- title: SSR-ready
details: Run your queries on the server before rendering the page HTML
footer: LICENCE MIT - Created by Guillaume CHAU (@Akryum)
icon: 🌐
---
<SponsorButton/>
## Sponsors
<h2 style="text-align: center; font-size: 2rem;">Sponsors</h2>
<p align="center">
<a href="https://guillaume-chau.info/sponsors/" target="_blank">
+5 -5
View File
@@ -23,19 +23,19 @@ npm install --save @vue/apollo-option @apollo/client
Before:
```js
import Vue from 'vue'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import Vue from 'vue'
import VueApollo from 'vue-apollo'
```
After:
```js
import Vue from 'vue'
import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client/core'
import { createApolloProvider } from '@vue/apollo-option'
import Vue from 'vue'
```
### Apollo Setup
@@ -89,8 +89,8 @@ const link = split(
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' &&
operation === 'subscription'
return kind === 'OperationDefinition'
&& operation === 'subscription'
},
wsLink,
httpLink
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 31 KiB

+81
View File
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="2430"
viewBox="26.69 28.9 1173.9 1144.1"
width="2500"
version="1.1"
id="svg2"
sodipodi:docname="hero.svg"
xml:space="preserve"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs2"><linearGradient
id="linearGradient1"
inkscape:collect="always"><stop
style="stop-color:#1c4472;stop-opacity:1;"
offset="0"
id="stop1" /><stop
style="stop-color:#1c4472;stop-opacity:0.35686275;"
offset="0.62594885"
id="stop3" /><stop
style="stop-color:#1c4472;stop-opacity:0;"
offset="1"
id="stop2" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="radialGradient2"
cx="613.64"
cy="600.94999"
fx="613.64"
fy="600.94999"
r="588.52881"
gradientTransform="matrix(1,0,0,0.97199998,0,16.826614)"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
id="namedview2"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.33900387"
inkscape:cx="665.18415"
inkscape:cy="1623.8753"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="1920"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" /><rect
style="fill:url(#radialGradient2);stroke-width:13.183;stroke-linecap:round;paint-order:markers stroke fill;opacity:0.17374599"
id="rect1"
width="1177.0576"
height="1144.1"
x="25.111193"
y="28.9"
rx="11.299753"
ry="11.299753" /><path
d="m 593.93786,204.45219 c -1.52718,0.0694 -7.28877,0.55534 -12.84211,0.97184 -69.4168,5.13684 -139.80545,30.82107 -197.49081,72.05464 -19.08963,13.6057 -34.15307,26.37839 -50.95193,43.17725 -96.55877,96.6282 -136.3346,234.90648 -106.1383,369.228 12.3562,55.11694 38.73457,110.58096 73.72065,155.14656 54.90869,70.04155 130.15651,119.3969 214.56734,140.77727 34.36132,8.74653 62.89163,12.28679 98.91895,12.28679 35.5414,0 61.71154,-3.19319 95.58694,-11.45379 48.66118,-11.93968 91.42193,-31.58464 134.18268,-61.50329 40.9559,-28.73855 79.06572,-69.0003 106.41595,-112.38579 35.26373,-56.08879 55.32518,-118.00857 60.25378,-185.96764 1.0413,-14.29985 0.4165,-49.70243 -1.1107,-64.27995 -3.0543,-28.94681 -10.82899,-67.12606 -15.34108,-75.10898 -1.80484,-3.12376 -6.87227,-7.14994 -10.62078,-8.46885 -1.31891,-0.4165 -4.23441,-0.76359 -6.59459,-0.76359 -10.41252,0 -19.15903,8.6771 -19.15903,18.88137 0,2.42959 1.11066,8.2606 2.70724,14.57753 13.74453,53.45094 14.85521,112.17755 3.12376,165.76732 -14.99403,68.65323 -48.45293,129.18468 -98.91891,178.8177 -23.53231,23.1158 -46.2316,40.19233 -75.59491,56.9912 -68.51439,39.2205 -153.68881,54.07569 -233.58755,40.74766 C 494.39416,943.88 441.7068,920.55596 391.65729,881.61313 368.125,863.28709 339.45585,833.09079 321.68515,807.82307 c -30.68223,-43.59376 -49.9801,-89.54768 -59.97613,-142.9292 -5.06741,-27.21139 -6.80284,-63.30813 -4.44266,-92.67145 3.60967,-44.56558 14.78577,-85.938 33.73656,-125.15849 8.74653,-18.25662 14.16103,-27.76672 24.64296,-43.52434 21.38039,-32.34823 49.70245,-62.61396 81.00943,-86.63217 36.72148,-28.25264 80.31523,-49.70243 124.3949,-61.22563 23.80997,-6.2475 42.83018,-9.37126 66.98722,-11.10668 87.39576,-6.24752 172.36192,18.88137 241.57049,71.56872 5.13684,3.88735 10.55134,8.12178 12.00911,9.44069 l 2.63783,2.29076 -1.04125,2.91551 c -5.34509,14.99402 -1.38833,33.66715 9.85719,46.7175 5.76159,6.66402 16.17411,12.91152 24.99005,14.99403 6.03924,1.45775 16.52117,1.45775 22.62985,0 15.47995,-3.67909 29.91864,-16.9377 34.50015,-31.65407 5.13684,-16.45177 2.15192,-32.69531 -8.39942,-45.8845 -11.66204,-14.57753 -31.72348,-21.10271 -50.25776,-16.24353 l -4.02616,1.04125 -5.69218,-4.72035 c -23.11581,-19.36728 -51.09078,-37.27682 -80.5235,-51.50726 -47.75877,-23.1158 -95.72577,-35.54141 -150.35681,-38.87342 -10.48193,-0.62475 -35.26374,-0.76358 -41.99716,-0.20825 z"
id="path1"
style="fill:#1c426d;fill-opacity:1;stroke-width:0.694168" /><g
transform="matrix(3.0101768,0,0,-3.0101768,145.88562,1137.4985)"
id="g10"><g
transform="translate(178.06,235.01)"
id="g4"
style="fill:#173960;fill-opacity:1"><path
d="M 0,0 -22.669,-39.264 -45.338,0 h -75.491 L -22.669,-170.02 75.491,0 Z"
fill="#41b883"
id="path2"
style="fill:#1c4472;fill-opacity:1" /></g><g
transform="translate(178.06,235.01)"
id="g8"
style="fill:#2a5f9c;fill-opacity:1"><path
d="M 0,0 -22.669,-39.264 -45.338,0 H -81.565 L -22.669,-102.01 36.227,0 Z"
fill="#34495e"
id="path6"
style="fill:#2a5f9c;fill-opacity:1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

+51
View File
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="2430"
viewBox="26.69 28.9 1173.9 1144.1"
width="2500"
version="1.1"
id="svg2"
sodipodi:docname="vue-apollo.svg"
xml:space="preserve"
inkscape:version="1.3 (0e150ed6c4, 2023-07-21)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs2" /><sodipodi:namedview
id="namedview2"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="0.47942387"
inkscape:cx="870.83691"
inkscape:cy="1406.897"
inkscape:window-width="2560"
inkscape:window-height="1371"
inkscape:window-x="1920"
inkscape:window-y="32"
inkscape:window-maximized="1"
inkscape:current-layer="g10" /><path
d="m 585.25763,29.7 c -2.2,0.1 -10.5,0.8 -18.5,1.4 -100,7.4 -201.4,44.4 -284.5,103.8 -27.5,19.6 -49.2,38 -73.4,62.2 C 69.757632,336.3 12.457632,535.5 55.957632,729 c 17.8,79.4 55.799998,159.3 106.199998,223.5 79.1,100.9 187.5,172 309.1,202.8 49.5,12.6 90.6,17.7 142.5,17.7 51.2,0 88.9,-4.6 137.7,-16.5 70.1,-17.2 131.7,-45.5 193.3,-88.6 58.99997,-41.4 113.89997,-99.4 153.29997,-161.9 50.8,-80.8 79.7,-170 86.8,-267.9 1.5,-20.6 0.6,-71.6 -1.6,-92.6 -4.4,-41.7 -15.6,-96.7 -22.1,-108.2 -2.6,-4.5 -9.9,-10.3 -15.3,-12.2 -1.9,-0.6 -6.1,-1.1 -9.5,-1.1 -15,0 -27.6,12.5 -27.6,27.2 0,3.5 1.6,11.9 3.9,21 19.8,77 21.4,161.6 4.5,238.8 -21.6,98.9 -69.8,186.1 -142.49997,257.6 -33.9,33.3 -66.6,57.9 -108.9,82.1 -98.7,56.5 -221.4,77.9 -336.5,58.7 -87.4,-14.5 -163.3,-48.1 -235.4,-104.2 -33.9,-26.4 -75.2,-69.9 -100.8,-106.3 -44.2,-62.8 -72,-129 -86.4,-205.9 -7.299998,-39.2 -9.799998,-91.2 -6.4,-133.5 5.2,-64.2 21.3,-123.8 48.6,-180.3 12.6,-26.3 20.4,-40 35.5,-62.7 30.8,-46.6 71.6,-90.2 116.7,-124.8 52.9,-40.7 115.7,-71.6 179.2,-88.2 34.3,-9 61.7,-13.5 96.5,-16 125.9,-9 248.3,27.2 348,103.1 7.4,5.6 15.2,11.7 17.3,13.6 l 3.8,3.3 -1.5,4.2 c -7.7,21.6 -2,48.5 14.2,67.3 8.3,9.6 23.3,18.6 36,21.6 8.69997,2.1 23.79997,2.1 32.59997,0 22.3,-5.3 43.1,-24.4 49.7,-45.6 7.4,-23.7 3.1,-47.1 -12.1,-66.1 -16.8,-21 -45.7,-30.4 -72.39997,-23.4 l -5.8,1.5 -8.2,-6.8 c -33.3,-27.9 -73.6,-53.7 -116,-74.2 -68.8,-33.3 -137.9,-51.2 -216.6,-56 -15.1,-0.9 -50.8,-1.1 -60.5,-0.3 z"
id="path1"
style="fill:#1c426d;fill-opacity:1" /><g
transform="matrix(4.3363805,0,0,-4.3363805,-60.194502,1373.8216)"
id="g10"><g
transform="translate(178.06,235.01)"
id="g4"
style="fill:#173960;fill-opacity:1"><path
d="M 0,0 -22.669,-39.264 -45.338,0 h -75.491 L -22.669,-170.02 75.491,0 Z"
fill="#41b883"
id="path2"
style="fill:#1c4472;fill-opacity:1" /></g><g
transform="translate(178.06,235.01)"
id="g8"
style="fill:#2a5f9c;fill-opacity:1"><path
d="M 0,0 -22.669,-39.264 -45.338,0 H -81.565 L -22.669,-102.01 36.227,0 Z"
fill="#34495e"
id="path6"
style="fill:#2a5f9c;fill-opacity:1" /></g></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

@@ -209,7 +209,7 @@ export default {
签名:
```ts
mutate(options = null): Promise<FetchResult>
function mutate(options = null): Promise<FetchResult>
```
- `options`: [变更选项](https://www.apollographql.com/docs/react/api/core/ApolloClient/#ApolloClient.mutate).
@@ -24,12 +24,12 @@ const apolloProvider = createApolloProvider({
},
// 查看所有查询的加载状态
// 详见 '智能查询 > 选项 > watchLoading'
watchLoading (isLoading, countModifier) {
watchLoading(isLoading, countModifier) {
loading += countModifier
console.log('Global loading', loading, countModifier)
},
// 所有智能查询和订阅的全局错误处理函数
errorHandler (error) {
errorHandler(error) {
console.log('Global error handler')
console.error(error)
},
+53 -47
View File
@@ -24,56 +24,58 @@
示例:
```js
export default {
// Apollo 具体选项
apollo: {
apollo: {
// 带参数的高级查询
// vue 会侦听 'variables' 方法
pingMessage: {
query: gql`query PingMessage($message: String!) {
pingMessage: {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// 响应式参数
variables () {
// 响应式参数
variables() {
// 在这里使用 vue 的响应式属性
return {
message: this.pingInput,
}
},
// 轮询间隔,以毫秒为单位
pollInterval: 10000,
// 也可以通过 vue 响应式属性设置轮询间隔
pollInterval() {
return this.pollInterval;
},
// 变量:深度对象侦听
deep: false,
// 我们使用自定义的 update 回调函数,因为字段名称不匹配
// 默认情况下,将使用 'data' 结果对象上的 'pingMessage' 属性
// 考虑到 apollo 服务端的工作方式,我们知道结果是在 'ping' 属性中
update (data) {
console.log(data)
// 返回的值将更新 vue 属性 'pingMessage'
return data.ping
},
// 可选结果钩子
result ({ data, loading, networkStatus }) {
console.log('We got some result!')
},
// 错误处理
error (error) {
console.error('We\'ve got an error!', error)
},
// 加载状态
// loadingKey 是数据属性的名称
// 在查询正在加载时将递增,不再加载时递减
loadingKey: 'loadingQueriesCount',
// 当加载状态发生变化时会调用 watchLoading
watchLoading (isLoading, countModifier) {
return {
message: this.pingInput,
}
},
// 轮询间隔,以毫秒为单位
pollInterval: 10000,
// 也可以通过 vue 响应式属性设置轮询间隔
pollInterval() {
return this.pollInterval
},
// 变量:深度对象侦听
deep: false,
// 我们使用自定义的 update 回调函数,因为字段名称不匹配
// 默认情况下,将使用 'data' 结果对象上的 'pingMessage' 属性
// 考虑到 apollo 服务端的工作方式,我们知道结果是在 'ping' 属性中
update(data) {
console.log(data)
// 返回的值将更新 vue 属性 'pingMessage'
return data.ping
},
// 可选结果钩子
result({ data, loading, networkStatus }) {
console.log('We got some result!')
},
// 错误处理
error(error) {
console.error('We\'ve got an error!', error)
},
// 加载状态
// loadingKey 是数据属性的名称
// 在查询正在加载时将递增,不再加载时递减
loadingKey: 'loadingQueriesCount',
// 当加载状态发生变化时会调用 watchLoading
watchLoading(isLoading, countModifier) {
// isLoading 是一个布尔值
// countModifier 为 1 或 -1
},
},
},
},
}
```
如果你使用 `ES2015``update` 也可以这样写:
@@ -85,14 +87,18 @@ update: data => data.ping
手动模式示例:
```js
{
query: gql`...`,
manual: true,
result ({ data, loading }) {
if (!loading) {
this.items = data.items
export default {
apollo: {
myData: {
query: gql`...`,
manual: true,
result({ data, loading }) {
if (!loading) {
this.items = data.items
}
},
}
},
}
}
```
+2 -2
View File
@@ -17,7 +17,7 @@ const states = ApolloSSR.getStates(apolloProvider, options)
`options` 的默认值是:
```js
{
const defaultOptions = {
// 每个 apollo 客户端状态的 key 的前缀
exportNamespace: '',
}
@@ -34,7 +34,7 @@ const js = ApolloSSR.exportStates(apolloProvider, options)
`options` 的默认值是:
```js
{
const defaultOptions = {
// 全局变量名
globalName: '__APOLLO_STATE__',
// 变量设置到的全局对象
+2 -2
View File
@@ -12,8 +12,8 @@ import { useQuery, useQueryLoading } from '@vue/apollo-composable'
export default {
setup () {
const { result: one } = useQuery(...)
const { result: two } = useQuery(...)
const { result: one } = useQuery(/* ... */)
const { result: two } = useQuery(/* ... */)
const loading = useQueryLoading()
return {
-3
View File
@@ -28,8 +28,6 @@
- `network-only`:从网络返回结果并保存到缓存,如果网络调用未成功则失败。
- `no-cache`:从网络返回结果但不保存到缓存,如果网络调用未成功则失败。
- `fetchResults`:是否获取结果。
- `metadata`:当前查询在存储中的任意元数据。可用于调试、开发人员工具等场景。
- `notifyOnNetworkStatusChange`:网络状态更新时是否应在此查询的观察者上触发下一步。
@@ -61,4 +59,3 @@
- `onResult(handler)`:有新结果可用时调用的事件钩子。
- `onError(handler)`:发生错误时调用的事件钩子。
@@ -36,4 +36,3 @@
- `onResult(handler)`:有新结果可用时调用的事件钩子。
- `onError(handler)`:发生错误时调用的事件钩子。
@@ -13,7 +13,7 @@
让我们创建一个本地 schema 来描述将在 todo 列表中作为单个事项的元素。在这个事项中应该有一些文本、一些定义它是否已经完成的属性、以及一个 ID 来区分不同的待办事项。所以,它应该是一个具有三个属性的对象:
```js
{
const item = {
id: 'uniqueId',
text: 'some text',
done: false
@@ -23,9 +23,9 @@
现在我们可以将 `Item` 类型添加到本地 GraphQL schema 中。
```js
//main.js
// main.js
import gql from 'graphql-tag';
import gql from 'graphql-tag'
export const typeDefs = gql`
type Item {
@@ -33,7 +33,7 @@ export const typeDefs = gql`
text: String!
done: Boolean!
}
`;
`
```
这里的 `gql` 代表解析 GraphQL 查询字符串的 JavaScript 模板字符串标签。
@@ -59,7 +59,7 @@ const apolloClient = new ApolloClient({
想象我们的远程 schema 中有一个 `User` 类型:
```js
```gql
type User {
name: String!
age: Int!
@@ -73,7 +73,7 @@ export const schema = gql`
extend type User {
twitter: String
}
`;
`
```
现在,在查询用户时,我们需要指定 `twitter` 是本地字段:
@@ -85,7 +85,7 @@ const userQuery = gql`
age
twitter @client
}
`;
`
```
## 初始化 Apollo 缓存
@@ -147,7 +147,7 @@ cache.writeData({
```js
// App.vue
import gql from 'graphql-tag';
import gql from 'graphql-tag'
const todoItemsQuery = gql`
{
@@ -157,7 +157,7 @@ const todoItemsQuery = gql`
done
}
}
`;
`
```
`@client` 指令是与发送到远程 API 的查询的主要区别。该指令指定 Apollo 客户端不应在远程 GraqhQL API 上执行此查询,而是应该从本地缓存中获取结果。
@@ -165,13 +165,13 @@ const todoItemsQuery = gql`
现在,我们可以在 Vue 组件中像普通的 Apollo 查询一样使用此查询:
```js
// App.vue
apollo: {
todoItems: {
query: todoItemsQuery
}
},
export default {
apollo: {
todoItems: {
query: todoItemsQuery
}
},
}
```
## 使用变更修改本地数据
@@ -209,7 +209,7 @@ const checkItemMutation = gql`
mutation($id: ID!) {
checkItem(id: $id) @client
}
`;
`
```
我们定义了一个**本地**变更(因为在这里写了一个 `@client` 指令),它将接受一个唯一的标识符作为参数。现在,我们需要一个**解析器**:一个解析 schema 中类型或字段的值的函数。
@@ -224,13 +224,14 @@ const checkItemMutation = gql`
const resolvers = {
Mutation: {
checkItem: (_, { id }, { cache }) => {
const data = cache.readQuery({ query: todoItemsQuery });
const currentItem = data.todoItems.find(item => item.id === id);
currentItem.done = !currentItem.done;
cache.writeQuery({ query: todoItemsQuery, data });
return currentItem.done;
const data = cache.readQuery({ query: todoItemsQuery })
const currentItem = data.todoItems.find(item => item.id === id)
currentItem.done = !currentItem.done
cache.writeQuery({ query: todoItemsQuery, data })
return currentItem.done
},
};
}
}
```
在这里我们做了什么?
@@ -255,6 +256,7 @@ const resolvers = {
cache.writeQuery({ query: todoItemsQuery, data });
return currentItem.done;
},
}
};
const apolloClient = new ApolloClient({
@@ -267,14 +269,14 @@ const apolloClient = new ApolloClient({
现在,我们可以在 Vue 组件中像普通的 Apollo [变更](../guide-option/mutations.md) 一样使用此变更:
```js
// App.vue
methods: {
checkItem(id) {
this.$apollo.mutate({
mutation: checkItemMutation,
variables: { id }
});
},
export default {
methods: {
checkItem(id) {
this.$apollo.mutate({
mutation: checkItemMutation,
variables: { id }
})
},
}
}
```
@@ -71,7 +71,7 @@ export default {
description
}
}`,
variables () {
variables() {
return {
id: this.id,
}
@@ -64,7 +64,6 @@ const sourceSchema = `
twitter: String
}
type Query {
allHeroes: [VueHero]
}
@@ -72,14 +71,14 @@ const sourceSchema = `
type Mutation {
addHero(hero: HeroInput!): VueHero!
deleteHero(name: String!): Boolean
}
}
`
```
下一步是使用 `graphql-tools` 方法创建可执行的 schema
```js
import { makeExecutableSchema } from 'graphql-tools'
...
// ...
const schema = makeExecutableSchema({
typeDefs: sourceSchema,
})
@@ -88,7 +87,7 @@ const schema = makeExecutableSchema({
```js
import { addMockFunctionsToSchema } from 'graphql-tools'
...
// ...
addMockFunctionsToSchema({
schema,
})
@@ -111,7 +110,7 @@ const query = `
在测试用例中调用 GraphQL 查询,保存响应到组件数据中,然后检查渲染完成的组件是否与快照匹配:
```js
graphql(schema, query).then(result => {
graphql(schema, query).then((result) => {
wrapper.setData(result.data)
expect(wrapper.element).toMatchSnapshot()
})
@@ -149,4 +148,4 @@ addMockFunctionsToSchema({
```
你可以用同样的方法来测试变更。
---
---
@@ -96,19 +96,19 @@ export default {
// vue.config.js
module.exports = {
chainWebpack: config => {
chainWebpack: (config) => {
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
options.transpileOptions = {
transforms: {
dangerousTaggedTemplateString: true,
},
}
return options
})
.loader('vue-loader')
.tap((options) => {
options.transpileOptions = {
transforms: {
dangerousTaggedTemplateString: true,
},
}
return options
})
}
}
```
@@ -25,7 +25,6 @@ const apolloClient = new ApolloClient({
cache,
uri: 'http://localhost:4042/graphql',
})
```
::: warning
@@ -63,15 +63,17 @@ export default {
将新项添加到缓存中:
```js
methods: {
onMessageAdded (previousResult, { subscriptionData }) {
export default {
methods: {
onMessageAdded(previousResult, { subscriptionData }) {
// 之前的结果是不可变的
const newResult = {
messages: [...previousResult.messages],
const newResult = {
messages: [...previousResult.messages],
}
// 添加问题到列表中
newResult.messages.push(subscriptionData.data.messageAdded)
return newResult
}
// 添加问题到列表中
newResult.messages.push(subscriptionData.data.messageAdded)
return newResult
}
}
```
@@ -79,22 +81,25 @@ methods: {
从缓存中删除一项:
```js
methods: {
onMessageAdded (previousResult, { subscriptionData }) {
const removedMessage = subscriptionData.data.messageRemoved
const index = previousResult.messages.findIndex(
m => m.id === removedMessage.id
)
export default {
methods: {
onMessageAdded(previousResult, { subscriptionData }) {
const removedMessage = subscriptionData.data.messageRemoved
const index = previousResult.messages.findIndex(
m => m.id === removedMessage.id
)
if (index === -1) return previousResult
if (index === -1)
return previousResult
// 之前的结果是不可变的
const newResult = {
messages: [...previousResult.messages],
// 之前的结果是不可变的
const newResult = {
messages: [...previousResult.messages],
}
// 从列表中移除问题
newResult.messages.splice(index, 1)
return newResult
}
// 从列表中移除问题
newResult.messages.splice(index, 1)
return newResult
}
}
```
@@ -61,14 +61,16 @@ export default {
import { onError } from '@apollo/client/link/error'
const link = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
)
}
if (networkError) console.log(`[Network error]: ${networkError}`)
if (networkError)
console.log(`[Network error]: ${networkError}`)
})
```
@@ -78,7 +80,7 @@ const link = onError(({ graphQLErrors, networkError }) => {
import { onError } from '@apollo/client/link/error'
import { logErrorMessages } from '@vue/apollo-util'
const link = onError(error => {
const link = onError((error) => {
logErrorMessages(error)
})
```
@@ -93,7 +95,7 @@ const link = onError(error => {
import { onError } from '@apollo/client/link/error'
import { logErrorMessages } from '@vue/apollo-util'
const link = onError(error => {
const link = onError((error) => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error)
}
@@ -120,8 +120,8 @@ export const entryFragment = gql`
如果我们的片段包含子片段,那么我们可以将它们传递到 `gql` 中:
```js
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'
import { entryFragment as RepoInfoEntryFragment } from './RepoInfo.vue'
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'
export const entryFragment = gql`
fragment FeedEntry on Entry {
@@ -216,11 +216,11 @@ query {
2. 在构建过程中,使用 `possibleTypes.json` 来配置缓存。然后,将新配置的缓存传递给 `ApolloClient` 以完成这个过程。
```js
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core'
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
import possibleTypes from './possibleTypes.json'
const cache = new InMemoryCache({ possibleTypes })
const httpLink = createHttpLink({ uri });
const httpLink = createHttpLink({ uri })
const client = new ApolloClient({
cache,
@@ -304,7 +304,7 @@ editMessage()
### 更新所有其他缓存
如果某个变更修改了多个实体,或者它创建或删除了一个或多个实体,Apollo Client 将**不会**自动更新缓存以反映该变更所做的更改。相反,你应该使用选项中的 `update` 函数来更新缓存。
如果某个变更修改了多个实体,或者它创建或删除了一个或多个实体,Apollo Client 将**不会**自动更新缓存以反映该变更所做的更改。相反,你应该使用选项中的 `update` 函数来更新缓存。
这个 `update` 函数的目的是修改缓存的数据,以匹配服务端变更所做出的更改。
@@ -470,9 +470,9 @@ const { mutate: sendMessage, error: sendMessageError } = useMutation(gql`
在变更成功完成时调用。
```js
const { onDone } = useMutation(...)
const { onDone } = useMutation(/* ... */)
onDone(result => {
onDone((result) => {
console.log(result.data)
})
```
@@ -552,9 +552,9 @@ export default {
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useMutation(...)
const { onError } = useMutation(/* ... */)
onError(error => {
onError((error) => {
logErrorMessages(error)
})
```
@@ -11,7 +11,7 @@
在 Apollo 中,最简单的分页方法是使用由 `useQuery` 组合函数返回的 `fetchMore` 函数。这基本上可以使你执行新的 GraphQL 查询并将结果合并到原始结果中。
```js
const { fetchMore } = useQuery(...)
const { fetchMore } = useQuery(/* ... */)
```
你可以指定要用于新查询的查询和变量,以及如何将新查询结果与客户端上的现有数据合并。具体的操作将决定你要实现什么样的分页。
@@ -422,7 +422,7 @@ function selectUser (id) {
export default {
props: ['id'],
setup (props) {
setup(props) {
const { result } = useQuery(gql`
query getUserById ($id: ID!) {
user (id: $id) {
@@ -486,7 +486,7 @@ const { result } = useQuery(gql`
id: id.value
}))
function selectUser (id) {
function selectUser(id) {
id.value = id
}
```
@@ -563,7 +563,7 @@ const { result } = useQuery(gql`
enabled: enabled.value,
}))
function enableQuery () {
function enableQuery() {
enabled.value = true
}
```
@@ -670,9 +670,9 @@ export default {
将在有新结果可用时调用。
```js
const { onResult } = useQuery(...)
const { onResult } = useQuery(/* ... */)
onResult(queryResult => {
onResult((queryResult) => {
console.log(queryResult.data)
console.log(queryResult.loading)
console.log(queryResult.networkStatus)
@@ -695,9 +695,9 @@ useQuery(gql`
发生错误时触发:
```js
const { onError } = useQuery(...)
const { onError } = useQuery(/* ... */)
onError(error => {
onError((error) => {
console.log(error.graphQLErrors)
console.log(error.networkError)
})
@@ -708,9 +708,9 @@ onError(error => {
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useQuery(...)
const { onError } = useQuery(/* ... */)
onError(error => {
onError((error) => {
logErrorMessages(error)
})
```
@@ -724,9 +724,9 @@ onError(error => {
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useQuery(...)
const { onError } = useQuery(/* ... */)
onError(error => {
onError((error) => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error)
}
@@ -19,11 +19,11 @@ yarn add @vue/apollo-composable
你需要在根实例中提供一个默认的 Apollo Client 实例:
```js
import { provide } from 'vue'
import { DefaultApolloClient } from '@vue/apollo-composable'
import { provide } from 'vue'
const app = new Vue({
setup () {
setup() {
provide(DefaultApolloClient, apolloClient)
},
@@ -36,11 +36,11 @@ const app = new Vue({
你也可以提供为应用提供多个可用的 Apollo Client 实例。在这种情况下,建议提供一个 `default` 值:
```js
import { provide } from 'vue'
import { ApolloClients } from '@vue/apollo-composable'
import { provide } from 'vue'
const app = new Vue({
setup () {
setup() {
provide(ApolloClients, {
default: apolloClient,
})
@@ -60,7 +60,7 @@ subscription onMessageAdded($channelId: ID!) {
首先,初始化 GraphQL websocket 连接:
```js
import { WebSocketLink } from "@apollo/client/link/ws"
import { WebSocketLink } from '@apollo/client/link/ws'
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
@@ -73,13 +73,13 @@ const wsLink = new WebSocketLink({
We need to either use the `WebSocketLink` or the `HttpLink` depending on the operation type:
```js
import { HttpLink, split } from "@apollo/client/core"
import { WebSocketLink } from "@apollo/client/link/ws"
import { getMainDefinition } from "@apollo/client/utilities"
import { HttpLink, split } from '@apollo/client/core'
import { WebSocketLink } from '@apollo/client/link/ws'
import { getMainDefinition } from '@apollo/client/utilities'
// 创建一个 http 连接:
const httpLink = new HttpLink({
uri: "http://localhost:3000/graphql"
uri: 'http://localhost:3000/graphql'
})
// 创建一个 WebSocket 连接:
@@ -96,8 +96,8 @@ const link = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription'
)
},
wsLink,
@@ -234,7 +234,7 @@ export default {
```js
const variables = ref({
channelId: "abc"
channelId: 'abc'
})
const { result } = useSubscription(
@@ -254,7 +254,7 @@ const { result } = useSubscription(
```js
const variables = reactive({
channelId: "abc"
channelId: 'abc'
})
const { result } = useSubscription(
@@ -273,7 +273,7 @@ const { result } = useSubscription(
使用函数(将会是响应式的):
```js
const channelId = ref("abc")
const channelId = ref('abc')
const { result } = useSubscription(
gql`
@@ -306,7 +306,7 @@ const { result } = useSubscription(
`,
null,
{
fetchPolicy: "no-cache"
fetchPolicy: 'no-cache'
}
)
```
@@ -325,7 +325,7 @@ const { result } = useSubscription(
`,
null,
() => ({
fetchPolicy: "no-cache"
fetchPolicy: 'no-cache'
})
)
```
@@ -359,7 +359,7 @@ function enableSub() {
你可以从 `useSubscription` 中检索加载和错误状态:
```js
const { loading, error } = useSubscription(...)
const { loading, error } = useSubscription(/* ... */)
```
### 事件钩子
@@ -369,9 +369,9 @@ const { loading, error } = useSubscription(...)
从服务端收到新结果时调用。
```js
const { onResult } = useSubscription(...)
const { onResult } = useSubscription(/* ... */)
onResult(result => {
onResult((result) => {
console.log(result.data)
})
```
@@ -383,9 +383,9 @@ onResult(result => {
```js
import { logErrorMessages } from '@vue/apollo-util'
const { onError } = useSubscription(...)
const { onError } = useSubscription(/* ... */)
onError(error => {
onError((error) => {
logErrorMessages(error)
})
```
@@ -483,7 +483,7 @@ subscribeToMore(() => ({
在后一种情况下,订阅将随着选项的更改自动重新启动。
现在你可以为 GraphQL 文档放置相关的订阅,并在必要时添加变量:
现在你可以为 GraphQL 文档放置相关的订阅,并在必要时添加变量:
```vue{21-33}
<script>
@@ -555,18 +555,19 @@ subscribeToMore(() => ({
在很多情况下,在允许客户端接收订阅结果之前,有必要对客户端进行身份验证。为此 `SubscriptionClient` 构造函数接受一个 `connectionParams` 字段,该字段传递一个自定义对象,服务端可以在设置任何订阅之前使用该对象来验证连接。
```js
import { WebSocketLink } from "@apollo/client/link/ws"
import { WebSocketLink } from '@apollo/client/link/ws'
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000/`,
options: {
reconnect: true,
connectionParams: {
authToken: user.authToken,
authToken: user.authToken,
},
},
})
```
::: tip
`connectionParams` 可以用于可能需要的任何其他用途,不仅可以进行身份验证,还可以使用 [SubscriptionsServer](https://www.apollographql.com/docs/graphql-subscriptions/authentication) 在服务端检查负载。
`connectionParams` 可以用于可能需要的任何其他用途,不仅可以进行身份验证,还可以使用 [SubscriptionsServer](https://www.apollographql.com/docs/graphql-subscriptions/authentication) 在服务端检查负载。
:::
@@ -19,8 +19,8 @@ const defaultOptions = {
}
const clientAOptions = {
// 你可以使用 `https` 进行安全连接(在生产环境中推荐)
httpEndpoint: 'http://localhost:4000/graphql',
// 你可以使用 `https` 进行安全连接(在生产环境中推荐)
httpEndpoint: 'http://localhost:4000/graphql',
}
const clientBOptions = {
@@ -28,28 +28,29 @@ const clientBOptions = {
}
// 在 Vue 应用程序文件中调用此方法
export function createProvider (options = {}) {
const createA= createApolloClient({
export function createProvider(options = {}) {
const createA = createApolloClient({
...defaultOptions,
...clientAOptions,
});
})
const createB = createApolloClient({
...defaultOptions,
...clientBOptions,
});
})
const a = createA.apolloClient;
const b = createB.apolloClient;
const a = createA.apolloClient
const b = createB.apolloClient
// 创建 vue apollo provider
const apolloProvider = createApolloProvider({
clients: {
a,
b
}
},
defaultClient: a,
})
})
}
```
在组件的 `apollo` 选项中,你可以使用 `$client` 为所有的查询、订阅和变更定义要使用的客户端(仅限在此组件内):
@@ -65,8 +66,12 @@ export default {
你也可以在单个查询,订阅和变更的选项中使用 `client` 属性来指定客户端:
```js
tags: {
query: gql`...`,
client: 'b',
export default {
apollo: {
tags: {
query: gql`...`,
client: 'b',
}
}
}
```
@@ -11,61 +11,66 @@
:::
```js
methods: {
addTag() {
export default {
methods: {
addTag() {
// 保存用户输入以防止错误
const newTag = this.newTag
// 将其清除以尽早更新用户页面
this.newTag = ''
// 调用 graphql 变更
this.$apollo.mutate({
const newTag = this.newTag
// 将其清除以尽早更新用户页面
this.newTag = ''
// 调用 graphql 变更
this.$apollo.mutate({
// 查询语句
mutation: gql`mutation ($label: String!) {
mutation: gql`mutation ($label: String!) {
addTag(label: $label) {
id
label
}
}`,
// 参数
variables: {
label: newTag,
},
// 用结果更新缓存
// 查询将先通过乐观响应、然后再通过真正的变更结果更新
update: (store, { data: { addTag } }) => {
// 从缓存中读取这个查询的数据
const data = store.readQuery({ query: TAGS_QUERY })
// 将变更中的标签添加到最后
data.tags.push(addTag)
// 将数据写回缓存
store.writeQuery({ query: TAGS_QUERY, data })
},
// 乐观 UI
// 将在请求产生时作为“假”结果,使用户界面能够快速更新
optimisticResponse: {
__typename: 'Mutation',
addTag: {
__typename: 'Tag',
id: -1,
// 参数
variables: {
label: newTag,
},
},
}).then((data) => {
// 用结果更新缓存
// 查询将先通过乐观响应、然后再通过真正的变更结果更新
update: (store, { data: { addTag } }) => {
// 从缓存中读取这个查询的数据
const data = store.readQuery({ query: TAGS_QUERY })
// 将变更中的标签添加到最后
data.tags.push(addTag)
// 将数据写回缓存
store.writeQuery({ query: TAGS_QUERY, data })
},
// 乐观 UI
// 将在请求产生时作为“假”结果,使用户界面能够快速更新
optimisticResponse: {
__typename: 'Mutation',
addTag: {
__typename: 'Tag',
id: -1,
label: newTag,
},
},
}).then((data) => {
// 结果
console.log(data)
}).catch((error) => {
console.log(data)
}).catch((error) => {
// 错误
console.error(error)
// 恢复初始用户输入
this.newTag = newTag
})
console.error(error)
// 恢复初始用户输入
this.newTag = newTag
})
},
},
},
}
```
## 服务端示例
```js
// 假数据生成器
import faker from 'faker'
export const schema = `
type Tag {
id: Int
@@ -86,18 +91,15 @@ schema {
}
`
// 假数据生成器
import faker from 'faker'
// 生成一些标签
var id = 0
var tags = []
let id = 0
const tags = []
for (let i = 0; i < 42; i++) {
addTag(faker.random.word())
}
function addTag (label) {
let t = {
function addTag(label) {
const t = {
id: id++,
label,
}
@@ -107,15 +109,15 @@ function addTag (label) {
export const resolvers = {
Query: {
tags (root, args, context) {
tags(root, args, context) {
return tags
},
},
Mutation: {
addTag (root, { label }, context) {
addTag(root, { label }, context) {
console.log(`adding tag '${label}'`)
return addTag(label)
},
},
}
```
```
+128 -97
View File
@@ -23,10 +23,12 @@ import gql from 'graphql-tag'
直接将 [gql](https://github.com/apollographql/graphql-tag) 查询作为值:
```js
apollo: {
export default {
apollo: {
// 简单的查询,将更新 'hello' 这个 vue 属性
hello: gql`{hello}`,
},
hello: gql`{hello}`,
},
}
```
接下来你可以通过 `this.$apollo.queries.<name>` 访问这个查询。
@@ -34,12 +36,14 @@ apollo: {
你可以在 vue 组件的 `data` 钩子中初始化属性:
```js
data () {
return {
export default {
data() {
return {
// 初始化你的 apollo 数据
hello: '',
hello: '',
}
},
},
}
```
在服务端添加相应的 schema 和解析器:
@@ -57,7 +61,7 @@ schema {
export const resolvers = {
Query: {
hello (root, args, context) {
hello(root, args, context) {
return 'Hello world!'
},
},
@@ -84,22 +88,26 @@ export const resolvers = {
请注意,初学者常见的错误是使用与查询中的字段名不相同的数据名称,例如:
```js
apollo: {
world: gql`query {
export default {
apollo: {
world: gql`query {
hello
}`
}
}
```
注意 `world``hello` 的不同之处:`vue-apollo` 不会去猜测你想要将哪些数据从查询结果中放入组件中。默认情况下,它只会尝试你在组件中使用的数据名称(即 `apollo` 对象中的键),在本例中为 `world`。如果名称不匹配,你可以使用 `update` 选项来告诉 `vue-apollo` 在结果中使用什么样的数据:
```js
apollo: {
world: {
query: gql`query {
export default {
apollo: {
world: {
query: gql`query {
hello
}`,
update: data => data.hello
update: data => data.hello
}
}
}
```
@@ -107,10 +115,12 @@ apollo: {
你也可以直接在 GraphQL 文档中重命名该字段:
```js
apollo: {
world: gql`query {
export default {
apollo: {
world: gql`query {
world: hello
}`
}
}
```
@@ -121,20 +131,22 @@ apollo: {
你可以通过在对象中声明 `query``variables` 将变量(及其他参数)添加到 `gql` 查询中:
```js
export default {
// Apollo 具体选项
apollo: {
apollo: {
// 带参数的查询
ping: {
ping: {
// gql 查询
query: gql`query PingMessage($message: String!) {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// 静态参数
variables: {
message: 'Meow',
// 静态参数
variables: {
message: 'Meow',
},
},
},
},
}
```
你可以在这个对象中使用 apollo 的 `watchQuery` 中的选项,比如:
@@ -147,30 +159,34 @@ apollo: {
例如,你可以像这样添加 `fetchPolicy` apollo 选项:
```js
apollo: {
export default {
apollo: {
// 带参数的查询
ping: {
query: gql`query PingMessage($message: String!) {
ping: {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
variables: {
message: 'Meow'
variables: {
message: 'Meow'
},
// 在这里加入其他选项
fetchPolicy: 'cache-and-network',
},
// 在这里加入其他选项
fetchPolicy: 'cache-and-network',
},
},
}
```
同样的,你可以在 vue 组件中初始化属性:
```js
data () {
return {
export default {
data() {
return {
// 初始化你的 apollo 数据
ping: '',
}
},
ping: '',
}
},
}
```
在服务端添加相应的 schema 和解析器:
@@ -188,7 +204,7 @@ schema {
export const resolvers = {
Query: {
ping (root, { message }, context) {
ping(root, { message }, context) {
return `Answering ${message}`
},
},
@@ -227,24 +243,26 @@ export const resolvers = {
你可以使用将在创建组件时被调用一次的函数,并且它必须返回选项对象:
```js
export default {
// Apollo 具体选项
apollo: {
apollo: {
// 带参数的查询
ping () {
ping() {
// 它将在创建组件时被调用一次
// 必须返回选项对象
return {
return {
// gql 查询
query: gql`query PingMessage($message: String!) {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// 静态参数
variables: {
message: 'Meow',
},
}
// 静态参数
variables: {
message: 'Meow',
},
}
},
},
},
}
```
::: tip
@@ -256,31 +274,36 @@ apollo: {
你可以使用函数定义 `query` 选项。这将自动更新 graphql 查询的定义:
```js
// 特定标签可以是随机标签或最后添加的标签
featuredTag: {
query () {
// 这里你可以用'this' 访问组件实例
if (this.showTag === 'random') {
return gql`{
export default {
apollo: {
// 特定标签可以是随机标签或最后添加的标签
featuredTag: {
query() {
// 这里你可以用'this' 访问组件实例
if (this.showTag === 'random') {
return gql`{
randomTag {
id
label
type
}
}`
} else if (this.showTag === 'last') {
return gql`{
}
else if (this.showTag === 'last') {
return gql`{
lastTag {
id
label
type
}
}`
}
},
// 为 'featuredTag' 这个组件属性赋值
update: data => data.randomTag || data.lastTag,
},
}
},
// 为 'featuredTag' 这个组件属性赋值
update: data => data.randomTag || data.lastTag,
},
}
}
```
::: tip
@@ -292,22 +315,24 @@ featuredTag: {
使用函数使 vue 属性能够响应式的提供给参数:
```js
export default {
// Apollo 具体选项
apollo: {
apollo: {
// 带参数的查询
ping: {
query: gql`query PingMessage($message: String!) {
ping: {
query: gql`query PingMessage($message: String!) {
ping(message: $message)
}`,
// 响应式参数
variables () {
// 响应式参数
variables() {
// 在这里使用 vue 响应式属性
return {
return {
message: this.pingInput,
}
}
},
},
},
},
}
```
在每次参数更改时,将重新获取查询,例如:
@@ -329,28 +354,30 @@ apollo: {
如果查询被跳过,它将被禁用且结果将不再被更新。你可以使用 `skip` 选项:
```js
export default {
// Apollo 具体选项
apollo: {
tags: {
apollo: {
tags: {
// GraphQL 查询
query: gql`query tagList ($type: String!) {
query: gql`query tagList ($type: String!) {
tags(type: $type) {
id
label
}
}`,
// 响应式变量
variables () {
return {
type: this.type,
}
},
// 禁用这个查询
skip () {
return this.skipQuery
// 响应式变量
variables() {
return {
type: this.type,
}
},
// 禁用这个查询
skip() {
return this.skipQuery
},
},
},
},
}
```
在这里,当 `skipQuery` 组件属性改变时,`skip` 将被自动调用。
@@ -368,24 +395,29 @@ this.$apollo.queries.tags.skip = true
这里是一个使用轮询的响应式查询示例:
```js
export default {
// Apollo 具体选项
apollo: {
apollo: {
// vue 实例上的 'tags' 数据属性
tags: {
query: gql`query tagList {
tags: {
query: gql`query tagList {
tags {
id,
label
}
}`,
pollInterval: 300, // 毫秒
pollInterval: 300, // 毫秒
},
},
},
}
```
这里是服务端的定义:
```js
// 假数据生成器
import casual from 'casual'
export const schema = `
type Tag {
id: Int
@@ -401,18 +433,15 @@ schema {
}
`
// 假数据生成器
import casual from 'casual'
// 生成一些标签
var id = 0
var tags = []
let id = 0
const tags = []
for (let i = 0; i < 42; i++) {
addTag(casual.word)
}
function addTag (label) {
let t = {
function addTag(label) {
const t = {
id: id++,
label,
}
@@ -422,7 +451,7 @@ function addTag (label) {
export const resolvers = {
Query: {
tags (root, args, context) {
tags(root, args, context) {
return tags
},
},
@@ -434,10 +463,12 @@ export const resolvers = {
你可以使用 `$apollo.addSmartQuery(key, options)` 方法手动添加智能查询:
```js
created () {
this.$apollo.addSmartQuery('comments', {
export default {
created() {
this.$apollo.addSmartQuery('comments', {
// 选项同上文
})
})
}
}
```
@@ -25,7 +25,6 @@ const apolloClient = new ApolloClient({
cache,
uri: 'http://localhost:4042/graphql',
})
```
::: warning
@@ -58,17 +58,21 @@ this.$apollo.skipAll = true
你也可以在组件的 `apollo` 选项中声明这些属性。它们可以是布尔值:
```js
apollo: {
$skipAll: true
export default {
apollo: {
$skipAll: true
}
}
```
或是响应式函数:
```js
apollo: {
$skipAll () {
return this.foo === 42
export default {
apollo: {
$skipAll() {
return this.foo === 42
}
}
}
```
@@ -28,8 +28,8 @@ const link = split(
// 根据操作类型分割
({ query }) => {
const definition = getMainDefinition(query)
return definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
return definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription'
},
wsLink,
httpLink
@@ -48,27 +48,29 @@ const apolloClient = new ApolloClient({
如果你需要更新一个来自订阅的智能查询结果,最好的方式是使用 `subscribeToMore` 查询方法。它将创建链接到智能查询的 [智能订阅](../api/smart-subscription.md)。你只需要将 `subscribeToMore` 添加到智能查询中:
```js
apollo: {
tags: {
query: TAGS_QUERY,
subscribeToMore: {
document: gql`subscription name($param: String!) {
export default {
apollo: {
tags: {
query: TAGS_QUERY,
subscribeToMore: {
document: gql`subscription name($param: String!) {
itemAdded(param: $param) {
id
label
}
}`,
// 传递给订阅的变量
// 由于我们使用了函数,因此它们是响应式的
variables () {
return {
param: this.param,
}
},
// 变更之前的结果
updateQuery: (previousResult, { subscriptionData }) => {
// 传递给订阅的变量
// 由于我们使用了函数,因此它们是响应式的
variables() {
return {
param: this.param,
}
},
// 变更之前的结果
updateQuery: (previousResult, { subscriptionData }) => {
// 在这里用之前的结果和新数据组合成新的结果
},
},
}
}
}
}
@@ -164,34 +166,36 @@ this.$watch(() => this.type, (type, oldType) => {
你可以在 `apollo` 选项中使用 `$subscribe` 关键字来声明 [智能订阅](../api/smart-subscription.md)
```js
apollo: {
export default {
apollo: {
// 订阅
$subscribe: {
$subscribe: {
// 当添加一个标签时
tagAdded: {
query: gql`subscription tags($type: String!) {
tagAdded: {
query: gql`subscription tags($type: String!) {
tagAdded(type: $type) {
id
label
type
}
}`,
// 响应式变量
variables () {
// 响应式变量
variables() {
// 像常规查询一样运作
// 在每次改变值时都会使用正确的变量重新订阅
return {
type: this.type,
}
},
// 结果钩子
// 不要忘记对 `data` 进行解构
result ({ data }) {
console.log(data.tagAdded)
return {
type: this.type,
}
},
// 结果钩子
// 不要忘记对 `data` 进行解构
result({ data }) {
console.log(data.tagAdded)
},
},
},
},
},
}
```
你可以使用 `this.$apollo.subscriptions.<name>` 访问这个订阅。
@@ -203,27 +207,29 @@ apollo: {
当服务端支持实时查询并使用订阅更新它们时,例如 [Hasura](https://hasura.io/),你可以使用简单订阅来实现响应式查询:
```js
data () {
return {
tags: [],
};
},
apollo: {
$subscribe: {
tags: {
query: gql`subscription {
export default {
data() {
return {
tags: [],
}
},
apollo: {
$subscribe: {
tags: {
query: gql`subscription {
tags {
id
label
type
}
}`,
result ({ data }) {
this.tags = data.tags;
result({ data }) {
this.tags = data.tags
},
},
},
},
},
}
```
## 跳过订阅
@@ -231,37 +237,39 @@ apollo: {
如果订阅被跳过,它将被禁用且不再被更新。你可以使用 `skip` 选项:
```js
export default {
// Apollo 具体选项
apollo: {
apollo: {
// 订阅
$subscribe: {
$subscribe: {
// 当添加一个标签时
tags: {
query: gql`subscription tags($type: String!) {
tags: {
query: gql`subscription tags($type: String!) {
tagAdded(type: $type) {
id
label
type
}
}`,
// 响应式变量
variables () {
return {
type: this.type,
// 响应式变量
variables() {
return {
type: this.type,
}
},
// 结果钩子
result(data) {
// 更新本地数据
this.tags.push(data.tagAdded)
},
// 跳过这个订阅
skip() {
return this.skipSubscription
}
},
// 结果钩子
result (data) {
// 更新本地数据
this.tags.push(data.tagAdded)
},
// 跳过这个订阅
skip () {
return this.skipSubscription
}
},
},
},
}
```
在这里,当 `skipSubscription` 组件属性改变时,`skip` 将被自动调用。
@@ -277,10 +285,12 @@ this.$apollo.subscriptions.tags.skip = true
你可以使用 `$apollo.addSmartSubscription(key, options)` 方法手动添加智能订阅:
```js
created () {
this.$apollo.addSmartSubscription('tagAdded', {
export default {
created() {
this.$apollo.addSmartSubscription('tagAdded', {
// 选项同 '$subscribe'
})
})
}
}
```
@@ -293,8 +303,9 @@ created () {
使用 `$apollo.subscribe()` 方法来创建一个 GraphQL 订阅,当组件被销毁时将自动终止。它**不会**创建智能订阅。
```js
mounted () {
const subQuery = gql`subscription tags($type: String!) {
export default {
mounted() {
const subQuery = gql`subscription tags($type: String!) {
tagAdded(type: $type) {
id
label
@@ -302,20 +313,21 @@ mounted () {
}
}`
const observer = this.$apollo.subscribe({
query: subQuery,
variables: {
type: 'City',
},
})
const observer = this.$apollo.subscribe({
query: subQuery,
variables: {
type: 'City',
},
})
observer.subscribe({
next (data) {
console.log(data)
},
error (error) {
console.error(error)
},
})
},
observer.subscribe({
next(data) {
console.log(data)
},
error(error) {
console.error(error)
},
})
},
}
```
@@ -4,6 +4,7 @@ node_modules
/tests/e2e/videos/
/tests/e2e/screenshots/
/tests/e2e/downloads/
# local env files
@@ -1,5 +0,0 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
],
}
@@ -1,22 +1,26 @@
import { defineConfig } from 'cypress'
import axios from 'axios'
import { defineConfig } from 'cypress'
import vitePreprocessor from 'cypress-vite'
module.exports = defineConfig({
export default defineConfig({
fixturesFolder: 'tests/e2e/fixtures',
screenshotsFolder: 'tests/e2e/screenshots',
videosFolder: 'tests/e2e/videos',
downloadsFolder: 'tests/e2e/downloads',
e2e: {
baseUrl: 'http://localhost:8080',
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents (on, config) {
setupNodeEvents(on) {
on('task', {
async 'db:reset' () {
'db:reset': async function () {
await axios.get('http://localhost:4042/_reset')
return true
},
})
on('file:preprocessor', vitePreprocessor())
},
specPattern: 'tests/e2e/specs/**/*.cy.{js,jsx,ts,tsx}',
supportFile: 'tests/e2e/support/index.js',
supportFile: 'tests/e2e/support/index.ts',
},
})
@@ -4,14 +4,13 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
<strong> We're sorry but dashboard doesn't work properly without JavaScript enabled. Please enable it to continue. </strong>
</noscript>
<div id="app"></div>
<script type="module" src="./src/main.ts"></script>
<!-- built files will be auto injected -->
</body>
</html>
+28 -30
View File
@@ -3,42 +3,40 @@
"version": "4.0.0-alpha.16",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test": "pnpm run test:e2e && kill-port 4042",
"test:e2e": "start-server-and-test api http://localhost:4042/.well-known/apollo/server-health test:e2e:client",
"test:e2e:client": "vue-cli-service test:e2e --mode production --headless",
"test:e2e:dev": "start-server-and-test api http://localhost:4042/.well-known/apollo/server-health test:e2e:dev:client",
"test:e2e:dev:client": "vue-cli-service test:e2e --mode development",
"api": "node server.js"
"dev": "vite --port 8080",
"build": "vite build",
"preview": "vite preview --port 8080",
"test": "pnpm run build && pnpm run test:e2e",
"test:e2e": "start-server-and-test preview http://localhost:8080 test:e2e:run",
"test:e2e:run": "start-server-and-test api 'http-get://localhost:4042/graphql?query=%7B__typename%7D' test:e2e:cy",
"test:e2e:cy": "cypress run --headless",
"test:e2e:dev": "cypress open",
"api": "test-server --simulate-latency 50",
"api:dev": "test-server --simulate-latency 500"
},
"dependencies": {
"@apollo/client": "^3.6.9",
"@apollo/client": "^3.7.16",
"@vue/apollo-composable": "workspace:*",
"@vue/apollo-util": "workspace:*",
"apollo-server-express": "^2.25.4",
"core-js": "^3.23.2",
"cors": "^2.8.5",
"express": "^4.18.1",
"graphql": "^15.8.0",
"graphql": "^16.7.1",
"graphql-tag": "^2.12.6",
"regenerator-runtime": "^0.13.9",
"shortid": "^2.2.16",
"vue": "^3.2.37",
"vue-demi": "^0.13.1",
"vue-router": "^4.0.16"
"graphql-ws": "^5.15.0",
"pinia": "^2.1.6",
"test-server": "workspace:*",
"vue": "^3.3.4",
"vue-router": "^4.2.4"
},
"devDependencies": {
"@babel/core": "^7.18.5",
"@types/shortid": "^0.0.29",
"@vue/cli-plugin-babel": "^5.0.6",
"@vue/cli-plugin-e2e-cypress": "^5.0.6",
"@vue/cli-plugin-typescript": "^5.0.6",
"@vue/cli-service": "^5.0.6",
"axios": "^0.24.0",
"cypress": "^10.2.0",
"kill-port": "^1.6.1",
"start-server-and-test": "^1.14.0",
"tailwindcss": "^1.9.6"
"@vitejs/plugin-vue": "^4.2.3",
"autoprefixer": "^10.4.14",
"axios": "^1.4.0",
"cypress": "^12.17.0",
"cypress-vite": "^1.4.1",
"postcss": "^8.4.25",
"start-server-and-test": "^2.0.0",
"tailwindcss": "^3.3.2",
"typescript": "^5.0.2",
"vite": "^4.4.2",
"vue-tsc": "^1.8.3"
}
}
@@ -1,8 +1,6 @@
/* eslint-disable */
module.exports = {
plugins: [
require('tailwindcss')(),
require('autoprefixer')(),
],
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

@@ -1,6 +1,9 @@
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client/core'
import { ApolloClient, createHttpLink, InMemoryCache, split } from '@apollo/client/core'
import { onError } from '@apollo/client/link/error'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { getMainDefinition } from '@apollo/client/utilities'
import { logErrorMessages } from '@vue/apollo-util'
import { createClient } from 'graphql-ws'
const cache = new InMemoryCache()
@@ -10,12 +13,32 @@ const httpLink = createHttpLink({
uri: 'http://localhost:4042/graphql',
})
const wsLink = new GraphQLWsLink(createClient({
url: 'ws://localhost:4042/graphql',
}))
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
if (definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription') {
console.log(`Subscribing to ${definition.name?.value ?? 'anonymous'}`)
}
return (
definition.kind === 'OperationDefinition'
&& definition.operation === 'subscription'
)
},
wsLink,
httpLink,
)
// Handle errors
const errorLink = onError(error => {
const errorLink = onError((error) => {
logErrorMessages(error)
})
export const apolloClient = new ApolloClient({
cache,
link: errorLink.concat(httpLink),
link: errorLink.concat(splitLink),
})
@@ -2,4 +2,4 @@
@tailwind components;
@tailwind utilities;
@tailwind utilities;
@@ -1,19 +1,21 @@
<script lang="ts" setup>
import { computed } from 'vue'
import ChannelList from './ChannelList.vue'
import GlobalLoading from './GlobalLoading.vue'
import { useRoute } from 'vue-router'
const route = useRoute()
const displayChannels = computed(() => !route.matched.some(r => r.meta?.layout === 'blank'))
</script>
<template>
<GlobalLoading />
<div class="flex h-screen items-stretch bg-gray-100">
<ChannelList class="w-1/4 border-r border-gray-200" />
<ChannelList
v-if="displayChannels"
class="w-1/4 border-r border-gray-200"
/>
<router-view class="flex-1 overflow-auto" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import ChannelList from './ChannelList.vue'
export default defineComponent({
name: 'App',
components: {
ChannelList,
},
})
</script>
@@ -1,31 +1,26 @@
<script lang="ts">
<script lang="ts" setup>
import gql from 'graphql-tag'
import { useQuery } from '@vue/apollo-composable'
import { defineComponent, computed } from 'vue'
import { computed } from 'vue'
interface Channel {
id: string
label: string
}
export default defineComponent({
setup () {
const { result, loading } = useQuery<{ channels: Channel[] }>(gql`
query channels {
channels {
id
label
}
}
`)
const channels = computed(() => result.value?.channels ?? [])
return {
loading,
channels,
const { result, loading } = useQuery<{ channels: Channel[] }>(gql`
query channels {
channels {
...channel
}
},
})
}
fragment channel on Channel {
id
label
}
`)
const channels = computed(() => result.value?.channels ?? [])
</script>
<template>
@@ -0,0 +1,43 @@
<script lang="ts" setup>
import { useChannels } from '@/stores/channel'
const channelStore = useChannels()
</script>
<template>
<div
v-if="channelStore.loading"
class="p-12 text-gray-500"
>
Loading channels...
</div>
<div
v-else
class="flex flex-col bg-white"
>
<router-link
v-for="channel of channelStore.channels"
:key="channel.id"
v-slot="{ href, navigate, isActive }"
:to="{
name: 'channel',
params: {
id: channel.id,
},
}"
custom
>
<a
:href="href"
class="channel-link px-4 py-2 hover:bg-green-100 text-green-700"
:class="{
'bg-green-200 hover:bg-green-300 text-green-900': isActive,
}"
@click="navigate"
>
# {{ channel.label }}
</a>
</router-link>
</div>
</template>
@@ -0,0 +1,51 @@
<script lang="ts" setup>
import { useChannels } from '@/stores/channel'
import { onBeforeUnmount, ref, watch } from 'vue'
const channels = ref<any[]>([])
let unwatch: (() => void) | undefined
setTimeout(() => {
const channelStore = useChannels()
unwatch = watch(() => channelStore.channels, (newChannels) => {
channels.value = newChannels
}, {
immediate: true,
})
}, 0)
onBeforeUnmount(() => {
unwatch?.()
})
</script>
<template>
<div
v-if="channels"
class="flex flex-col bg-white"
>
<router-link
v-for="channel of channels"
:key="channel.id"
v-slot="{ href, navigate, isActive }"
:to="{
name: 'channel',
params: {
id: channel.id,
},
}"
custom
>
<a
:href="href"
class="channel-link px-4 py-2 hover:bg-green-100 text-green-700"
:class="{
'bg-green-200 hover:bg-green-300 text-green-900': isActive,
}"
@click="navigate"
>
# {{ channel.label }}
</a>
</router-link>
</div>
</template>
@@ -0,0 +1,25 @@
<script lang="ts" setup>
import { ref } from 'vue'
import ChannelListPinia from './ChannelListPinia.vue'
const isComponentVisible = ref(true)
</script>
<template>
<div>
<button
data-test-id="channel-list-toggle"
@click="isComponentVisible = !isComponentVisible"
>
Toggle ChannelListPinia component
</button>
<div
v-if="isComponentVisible"
data-test-id="channel-list-container"
>
<ChannelListPinia />
</div>
</div>
</template>
@@ -22,12 +22,16 @@ export default defineComponent({
const { result, loading, refetch } = useQuery(gql`
query channel ($id: ID!) {
channel (id: $id) {
...channelView
}
}
fragment channelView on Channel {
id
label
messages {
id
label
messages {
id
text
}
text
}
}
`, () => ({
@@ -63,42 +67,46 @@ export default defineComponent({
</script>
<template>
<div
v-if="loading"
class="loading-channel"
>
Loading channel...
</div>
<div
v-else
class="flex flex-col"
>
<div class="flex-none p-6 border-b border-gray-200 bg-white">
Currently viewing # {{ channel.label }}
<a
class="text-green-500 cursor-pointer"
data-test-id="refetch"
@click="refetch()"
>Refetch</a>
<div>
<div
v-if="loading"
class="loading-channel fixed top-0 left-0 w-full flex"
>
<div class="px-4 py-2 rounded-b mx-auto bg-white border-b border-l border-r border-gray-200">
Loading channel...
</div>
</div>
<div
ref="messagesEl"
class="flex-1 overflow-y-auto"
v-if="channel"
class="flex flex-col h-full"
>
<MessageItem
v-for="message of channel.messages"
:key="message.id"
:message="message"
class="m-2"
<div class="flex-none p-6 border-b border-gray-200 bg-white">
Currently viewing # {{ channel.label }}
<a
class="text-green-500 cursor-pointer"
data-test-id="refetch"
@click="refetch()"
>Refetch</a>
</div>
<div
ref="messagesEl"
class="flex-1 overflow-y-auto"
>
<MessageItem
v-for="message of channel.messages"
:key="message.id"
:message="message"
class="m-2"
/>
</div>
<MessageForm
:channel-id="channel.id"
class="flex-none m-2 mt-0"
/>
</div>
<MessageForm
:channel-id="channel.id"
class="flex-none m-2 mt-0"
/>
</div>
</template>
@@ -0,0 +1,68 @@
<script lang="ts">
import gql from 'graphql-tag'
import { useQuery } from '@vue/apollo-composable'
import { defineComponent, computed, ref } from 'vue'
export default defineComponent({
setup () {
const selectedId = ref<{ id: string } | null>(null)
const { result, loading } = useQuery(gql`
query channel ($id: ID!) {
channel (id: $id) {
id
label
messages {
id
}
}
}
`, () => ({
// Should not throw since it will not be called if the query is disabled
id: selectedId.value!.id,
}), () => ({
fetchPolicy: 'no-cache',
enabled: !!selectedId.value,
}))
const channel = computed(() => result.value?.channel)
function load () {
selectedId.value = { id: 'general' }
}
return {
load,
loading,
channel,
}
},
})
</script>
<template>
<div class="m-6">
<div>
<button
class="bg-green-200 rounded-lg p-4"
@click="load()"
>
Load channel
</button>
</div>
<div
v-if="loading"
class="loading"
>
Loading...
</div>
<div
v-if="channel"
data-test-id="data"
>
<div>Loaded channel: {{ channel.label }}</div>
<div>Messages: {{ channel.messages.length }}</div>
</div>
</div>
</template>
@@ -0,0 +1,16 @@
<script lang="ts" setup>
import { useGlobalQueryLoading } from '@vue/apollo-composable'
const loading = useGlobalQueryLoading()
</script>
<template>
<div
class="fixed bg-white p-4 rounded-br top-0 right-0"
data-test-id="global-loading"
>
<div v-if="loading">
Global loading...
</div>
</div>
</template>
@@ -0,0 +1,115 @@
<script lang="ts" setup>
import { useApolloClient, useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'
import { computed, ref } from 'vue'
import MessageItem from './MessageItem.vue'
interface Channel {
id: string
label: string
}
const keepPreviousResult = ref(false)
const channelsQuery = useQuery<{ channels: Channel[] }>(gql`
query channels {
channels {
id
label
}
}
`)
const channels = computed(() => channelsQuery.result.value?.channels ?? [])
const selectedChannelId = ref<string | null>(null)
const selectedChannelQuery = useQuery(gql`
query channel ($id: ID!) {
channel (id: $id) {
id
label
messages {
id
text
}
}
}
`, () => ({
id: selectedChannelId.value,
}), () => ({
enabled: !!selectedChannelId.value,
fetchPolicy: 'cache-and-network',
keepPreviousResult: keepPreviousResult.value,
}))
const selectedChannel = computed(() => selectedChannelQuery.result.value?.channel)
const { client: apolloClient } = useApolloClient()
function clearCache () {
apolloClient.clearStore()
}
</script>
<template>
<div class="h-full flex flex-col">
<div class="p-4">
<label>
<input
v-model="keepPreviousResult"
type="checkbox"
>
keepPreviousResult
</label>
<button
class="ml-4 underline"
@click="clearCache"
>
Clear cache
</button>
</div>
<div class="flex h-full">
<div class="flex flex-col">
<button
v-for="channel of channels"
:key="channel.id"
class="channel-btn p-4"
:class="{
'bg-green-200': selectedChannelId === channel.id,
}"
@click="selectedChannelId = channel.id"
>
{{ channel.label }}
</button>
</div>
<div
v-if="selectedChannel"
class="the-channel flex flex-col w-full h-full overflow-auto"
>
<div class="flex-none p-6 border-b border-gray-200 bg-white">
# {{ selectedChannel.label }}
</div>
<div class="flex-1 overflow-y-auto">
<MessageItem
v-for="message of selectedChannel.messages"
:key="message.id"
:message="message"
class="m-2"
/>
</div>
</div>
<div
v-else
class="no-data"
>
No data
</div>
</div>
</div>
</template>
@@ -1,33 +1,48 @@
<script lang="ts">
<script lang="ts" setup>
import gql from 'graphql-tag'
import { useLazyQuery } from '@vue/apollo-composable'
import { defineComponent, computed } from 'vue'
import { computed, ref } from 'vue'
export default defineComponent({
setup () {
const { result, load } = useLazyQuery(gql`
query list {
list
}
`)
const list = computed(() => result.value?.list ?? [])
const { result, loading, load, refetch } = useLazyQuery(gql`
query list {
list
}
`)
const list = computed(() => result.value?.list ?? [])
return {
load,
list,
}
},
})
const refetched = ref(false)
function r () {
refetched.value = true
refetch()
}
function loadOrRefetch () {
load() || r()
}
</script>
<template>
<div class="m-6">
<button
class="bg-green-200 rounded-lg p-4"
@click="load()"
<div>
<button
class="bg-green-200 rounded-lg p-4"
@click="loadOrRefetch()"
>
Load list
</button>
<span data-test-id="refetched">
Refetched: {{ refetched }}
</span>
</div>
<div
v-if="loading"
class="loading"
>
Load list
</button>
Loading...
</div>
<ul class="my-4">
<li

Some files were not shown because too many files have changed in this diff Show More