Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| abe8089fdd | |||
| 773260b97e | |||
| 9830ea7607 | |||
| 7e06de77aa |
@@ -0,0 +1,92 @@
|
||||
# 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
|
||||
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
@@ -1,29 +0,0 @@
|
||||
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/*'
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Check PR title
|
||||
name: "Check PR title"
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
@@ -8,9 +8,8 @@ on:
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
check-pr-title:
|
||||
main:
|
||||
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,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,12 +13,13 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all tags
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Create Release for Tag
|
||||
id: release_tag
|
||||
uses: Akryum/release-tag@v4.0.7
|
||||
uses: Akryum/release-tag@conventional
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
preset: angular
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
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
|
||||
@@ -1,63 +0,0 @@
|
||||
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
|
||||
@@ -1,63 +0,0 @@
|
||||
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
|
||||
@@ -1,44 +0,0 @@
|
||||
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
|
||||
@@ -1,5 +1,2 @@
|
||||
.idea
|
||||
node_modules/
|
||||
dist/
|
||||
cache/
|
||||
.eslintcache
|
||||
|
||||
@@ -1,429 +1,3 @@
|
||||
# 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)
|
||||
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ pnpm install
|
||||
|
||||
Go to a package in `packages`.
|
||||
|
||||
Build the library:
|
||||
Build the library with watching:
|
||||
|
||||
```
|
||||
pnpm run build
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<p align="center">
|
||||
<img src="./packages/docs/src/public/hero.svg" width="256">
|
||||
</p>
|
||||
|
||||
# Apollo and GraphQL for Vue.js
|
||||
|
||||
[ ](https://www.npmjs.com/package/@vue/apollo-composable)
|
||||
[](https://www.apollographql.com/)
|
||||
[](https://vuejs.org/)
|
||||

|
||||
[](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>
|
||||
|
||||
|
||||
:book: Documentation [**for Vue 3**](http://v4.apollo.vuejs.org) | [for Vue 2](https://apollo.vuejs.org/)
|
||||
|
||||
@@ -15,12 +16,6 @@
|
||||
|
||||
[: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 |
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// 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',
|
||||
},
|
||||
})
|
||||
@@ -1,34 +1,35 @@
|
||||
{
|
||||
"name": "vue-apollo-monorepo",
|
||||
"version": "4.2.2",
|
||||
"version": "4.0.0-beta.1",
|
||||
"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 --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"
|
||||
"test": "pnpm run -r test",
|
||||
"lint": "eslint . --ext js,vue,ts",
|
||||
"release": "pnpm run build && pnpm run test && sheep release -b v4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@akryum/sheep": "^0.5.1",
|
||||
"@antfu/eslint-config": "^4.7.0",
|
||||
"@akryum/sheep": "^0.3.3",
|
||||
"@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.25.0",
|
||||
"esbuild-node-externals": "^1.18.0",
|
||||
"eslint": "^9.21.0",
|
||||
"typescript": "^5.8.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"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"eslint-scope": "^5",
|
||||
"graphql": "^15",
|
||||
"serialize-javascript": "^6.0.0",
|
||||
"subscriptions-transport-ws": "^0.9"
|
||||
},
|
||||
@@ -50,18 +51,6 @@
|
||||
"esbuild": "*",
|
||||
"vue": "*"
|
||||
}
|
||||
},
|
||||
"onlyBuiltDependencies": [
|
||||
"@apollo/protobufjs",
|
||||
"core-js",
|
||||
"core-js-pure",
|
||||
"cypress",
|
||||
"esbuild",
|
||||
"nodemon",
|
||||
"vue-demi"
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"js-yaml": "^3.13.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,341 +7,334 @@ 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': {
|
||||
label: '简体中文',
|
||||
'/zh-cn/': {
|
||||
lang: 'zh-CN',
|
||||
title: 'Vue Apollo',
|
||||
description: '🚀 在你的 Vue.js 应用中集成 GraphQL!',
|
||||
themeConfig: {
|
||||
lastUpdated: {
|
||||
message: '上次更新时间',
|
||||
},
|
||||
},
|
||||
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/',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
'/zh-cn/': {
|
||||
selectText: '选择语言',
|
||||
label: '简体中文',
|
||||
lastUpdated: '上次更新时间',
|
||||
nav: [
|
||||
{
|
||||
text: '指南',
|
||||
@@ -385,6 +378,7 @@ export default defineConfig({
|
||||
link: 'https://github.com/sponsors/Akryum',
|
||||
},
|
||||
],
|
||||
sidebarDepth: 3,
|
||||
sidebar: {
|
||||
'/zh-cn/guide/': [
|
||||
{
|
||||
@@ -400,7 +394,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '选项 API 指南',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Introduction',
|
||||
link: '/zh-cn/guide-option/',
|
||||
@@ -414,7 +408,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '基础',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Usage in Vue components',
|
||||
link: '/zh-cn/guide-option/usage',
|
||||
@@ -436,7 +430,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '进阶',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Special options',
|
||||
link: '/zh-cn/guide-option/special-options',
|
||||
@@ -456,7 +450,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '组合 API 指南',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Introduction',
|
||||
link: '/zh-cn/guide-composable/',
|
||||
@@ -470,7 +464,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '获取数据',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Queries',
|
||||
link: '/zh-cn/guide-composable/query',
|
||||
@@ -502,7 +496,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '组件指南',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Introduction',
|
||||
link: '/zh-cn/guide-components/',
|
||||
@@ -516,7 +510,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '用法',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Queries',
|
||||
link: '/zh-cn/guide-components/query',
|
||||
@@ -536,7 +530,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '进阶主题',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'Local state',
|
||||
link: '/zh-cn/guide-advanced/local-state',
|
||||
@@ -556,7 +550,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '选项 API',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'createApolloProvider',
|
||||
link: '/zh-cn/api/apollo-provider',
|
||||
@@ -578,7 +572,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '组合 API',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'useQuery',
|
||||
link: '/zh-cn/api/use-query',
|
||||
@@ -608,17 +602,17 @@ export default defineConfig({
|
||||
{
|
||||
text: '组件',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
@@ -626,7 +620,7 @@ export default defineConfig({
|
||||
{
|
||||
text: '进阶',
|
||||
collapsable: false,
|
||||
items: [
|
||||
children: [
|
||||
{
|
||||
text: 'ApolloSSR',
|
||||
link: '/zh-cn/api/ssr',
|
||||
@@ -643,6 +637,11 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
algolia: {
|
||||
appId: 'X6FFODVB9N',
|
||||
apiKey: 'cc89b1eff7e2fc6e6c0bbf8b066ab488',
|
||||
indexName: 'apollo-vuejs',
|
||||
},
|
||||
},
|
||||
|
||||
vite: {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import DefaultTheme from 'vitepress/theme'
|
||||
import SponsorButton from './components/SponsorButton.vue'
|
||||
|
||||
import './styles/index.pcss'
|
||||
|
||||
import DefaultTheme from 'vitepress/dist/client/theme-default'
|
||||
import SponsorButton from './components/SponsorButton.vue'
|
||||
|
||||
export default {
|
||||
...DefaultTheme,
|
||||
enhanceApp({ app }) {
|
||||
enhanceApp ({ app }) {
|
||||
app.component('SponsorButton', SponsorButton)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
: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);
|
||||
.home .hero img {
|
||||
max-width: 80vw;
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ See [apollo context](https://www.apollographql.com/docs/react/api/link/apollo-li
|
||||
Signature:
|
||||
|
||||
```ts
|
||||
function mutate(options = null): Promise<FetchResult>
|
||||
mutate(options = null): Promise<FetchResult>
|
||||
```
|
||||
|
||||
- `options`: [mutation options](https://www.apollographql.com/docs/react/api/core/ApolloClient/#ApolloClient.mutate).
|
||||
|
||||
@@ -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)
|
||||
},
|
||||
|
||||
@@ -24,63 +24,61 @@ 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:
|
||||
@@ -92,18 +90,14 @@ update: data => data.ping
|
||||
Manual mode example:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
myQuery: {
|
||||
query: gql`...`,
|
||||
manual: true,
|
||||
result({ data, loading }) {
|
||||
if (!loading) {
|
||||
this.items = data.items
|
||||
}
|
||||
},
|
||||
{
|
||||
query: gql`...`,
|
||||
manual: true,
|
||||
result ({ data, loading }) {
|
||||
if (!loading) {
|
||||
this.items = data.items
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -4,24 +4,4 @@ Extends [useQuery](./use-query.md)
|
||||
|
||||
## Additional Return
|
||||
|
||||
- `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
|
||||
}
|
||||
}
|
||||
```
|
||||
- `load(document?, variables?, options?)`: function to start querying.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -47,6 +47,6 @@
|
||||
|
||||
- `called`: boolean `Ref` holding `true` if the mutation was already called.
|
||||
|
||||
- `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).
|
||||
- `onDone`: Event hook called when the mutation successfully completes.
|
||||
|
||||
- `onError(handler)`: Event hook called when an error occurs. Handler is called with: `error` and `context` which is an object with `client` (ApolloClient instance).
|
||||
- `onError`: Event hook called when an error occurs.
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
- `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.
|
||||
@@ -40,8 +42,6 @@
|
||||
|
||||
- `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,6 +58,7 @@
|
||||
|
||||
- `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. Handler is called with: `result` (query result) and `context` which is an object with `client` (ApolloClient instance).
|
||||
- `onResult(handler)`: Event hook called when a new result is available.
|
||||
|
||||
- `onError(handler)`: 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).
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
- `variables`: Ref holding the variables object.
|
||||
|
||||
- `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).
|
||||
- `onResult(handler)`: Event hook called when a new result is available.
|
||||
|
||||
- `onError(handler)`: 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).
|
||||
|
||||
@@ -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 @@ const obj = {
|
||||
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:
|
||||
|
||||
```gql
|
||||
```js
|
||||
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
|
||||
export default {
|
||||
apollo: {
|
||||
todoItems: {
|
||||
query: todoItemsQuery
|
||||
}
|
||||
},
|
||||
}
|
||||
// App.vue
|
||||
|
||||
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,14 +228,13 @@ 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?
|
||||
@@ -260,27 +259,26 @@ 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
|
||||
export default {
|
||||
methods: {
|
||||
checkItem(id) {
|
||||
this.$apollo.mutate({
|
||||
mutation: checkItemMutation,
|
||||
variables: { id }
|
||||
})
|
||||
},
|
||||
}
|
||||
// App.vue
|
||||
|
||||
methods: {
|
||||
checkItem(id) {
|
||||
this.$apollo.mutate({
|
||||
mutation: checkItemMutation,
|
||||
variables: { id }
|
||||
});
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -75,7 +75,7 @@ export default {
|
||||
description
|
||||
}
|
||||
}`,
|
||||
variables() {
|
||||
variables () {
|
||||
return {
|
||||
id: this.id,
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ const sourceSchema = `
|
||||
twitter: String
|
||||
}
|
||||
|
||||
|
||||
type Query {
|
||||
allHeroes: [VueHero]
|
||||
}
|
||||
@@ -75,14 +76,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,
|
||||
})
|
||||
@@ -91,7 +92,7 @@ After this you need to add mock functions to schema:
|
||||
|
||||
```js
|
||||
import { addMockFunctionsToSchema } from 'graphql-tools'
|
||||
// ...
|
||||
...
|
||||
addMockFunctionsToSchema({
|
||||
schema,
|
||||
})
|
||||
@@ -114,7 +115,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()
|
||||
})
|
||||
@@ -152,4 +153,4 @@ addMockFunctionsToSchema({
|
||||
```
|
||||
You can test mutations in the same way.
|
||||
|
||||
---
|
||||
---
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -25,6 +25,7 @@ const apolloClient = new ApolloClient({
|
||||
cache,
|
||||
uri: 'http://localhost:4042/graphql',
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
::: warning
|
||||
@@ -36,8 +37,6 @@ 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,20 +66,18 @@ See [API Reference](../api/apollo-subscribe-to-more.md) for all the available op
|
||||
Add a new item to the cache:
|
||||
|
||||
```js
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -87,26 +85,23 @@ export default {
|
||||
Remove an item from the cache:
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
onMessageAdded(previousResult, { subscriptionData }) {
|
||||
const removedMessage = subscriptionData.data.messageRemoved
|
||||
const index = previousResult.messages.findIndex(
|
||||
m => m.id === removedMessage.id
|
||||
)
|
||||
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],
|
||||
}
|
||||
// Remove the question from the list
|
||||
newResult.messages.splice(index, 1)
|
||||
return newResult
|
||||
// The previous result is immutable
|
||||
const newResult = {
|
||||
...previousResult,
|
||||
messages: [...previousResult.messages],
|
||||
}
|
||||
// Remove the question from the list
|
||||
newResult.messages.splice(index, 1)
|
||||
return newResult
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -61,16 +61,14 @@ 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}`)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -80,7 +78,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)
|
||||
})
|
||||
```
|
||||
@@ -95,7 +93,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)
|
||||
}
|
||||
@@ -107,19 +105,19 @@ That way it will be dropped when compiling the project for production.
|
||||
Full example:
|
||||
|
||||
```js
|
||||
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
|
||||
import { ApolloClient, InMemoryCache, createHttpLink } 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
|
||||
const httpLink = createHttpLink({
|
||||
let 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 RepoInfoEntryFragment } from './RepoInfo.vue'
|
||||
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'
|
||||
import { entryFragment as RepoInfoEntryFragment } from './RepoInfo.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, createHttpLink, InMemoryCache } from '@apollo/client/core'
|
||||
import { ApolloClient, InMemoryCache, createHttpLink } 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.
|
||||
|
||||
@@ -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,63 +700,6 @@ 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.
|
||||
@@ -766,9 +709,9 @@ If you provide new values for **some** of your original query's variables but no
|
||||
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)
|
||||
@@ -791,9 +734,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)
|
||||
})
|
||||
@@ -804,9 +747,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)
|
||||
})
|
||||
```
|
||||
@@ -820,9 +763,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)
|
||||
}
|
||||
@@ -881,42 +824,3 @@ 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()
|
||||
}
|
||||
```
|
||||
|
||||
@@ -21,11 +21,11 @@ yarn add @vue/apollo-composable
|
||||
In your root instance, you need to provide a default Apollo Client instance:
|
||||
|
||||
```js
|
||||
import { DefaultApolloClient } from '@vue/apollo-composable'
|
||||
import { provide } from '@vue/composition-api'
|
||||
import { DefaultApolloClient } from '@vue/apollo-composable'
|
||||
|
||||
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 { ApolloClients } from '@vue/apollo-composable'
|
||||
import { provide } from 'vue'
|
||||
import { ApolloClients } from '@vue/apollo-composable'
|
||||
|
||||
const app = new Vue({
|
||||
setup() {
|
||||
setup () {
|
||||
provide(ApolloClients, {
|
||||
default: apolloClient,
|
||||
})
|
||||
@@ -90,20 +90,15 @@ 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";
|
||||
|
||||
const query = provideApolloClient(apolloClient)(() => useQuery(gql`
|
||||
query hello {
|
||||
hello
|
||||
}
|
||||
`))
|
||||
const hello = computed(() => query.result.value?.hello ?? '')
|
||||
provideApolloClient(apolloClient)
|
||||
```
|
||||
|
||||
Use `provideApolloClients` for multiple clients:
|
||||
|
||||
```js
|
||||
import { provideApolloClients } from '@vue/apollo-composable'
|
||||
import { provideApolloClients } from "@vue/apollo-composable";
|
||||
|
||||
provideApolloClients({
|
||||
default: apolloClient,
|
||||
|
||||
@@ -55,80 +55,12 @@ 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/).
|
||||
|
||||
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.
|
||||
Let's look at how to add support for this transport to Apollo Client.
|
||||
|
||||
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:
|
||||
First, initialize a GraphQL web socket link:
|
||||
|
||||
```js
|
||||
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'
|
||||
import { createClient } from 'graphql-ws'
|
||||
|
||||
// Create an http link:
|
||||
const httpLink = new HttpLink({
|
||||
uri: 'http://localhost:3000/graphql'
|
||||
})
|
||||
|
||||
// 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
|
||||
const link = split(
|
||||
// split based on operation type
|
||||
({ query }) => {
|
||||
const definition = getMainDefinition(query)
|
||||
return (
|
||||
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
|
||||
import { WebSocketLink } from "@apollo/client/link/ws"
|
||||
|
||||
const wsLink = new WebSocketLink({
|
||||
uri: `ws://localhost:5000/`,
|
||||
@@ -137,7 +69,44 @@ const wsLink = new WebSocketLink({
|
||||
}
|
||||
})
|
||||
```
|
||||
The rest of the configuration (creating a httpLink and link) is the same as described above for graphql-ws.
|
||||
|
||||
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"
|
||||
|
||||
// Create an http link:
|
||||
const httpLink = new HttpLink({
|
||||
uri: "http://localhost:3000/graphql"
|
||||
})
|
||||
|
||||
// Create a WebSocket link:
|
||||
const wsLink = new WebSocketLink({
|
||||
uri: `ws://localhost:5000/`,
|
||||
options: {
|
||||
reconnect: true
|
||||
}
|
||||
})
|
||||
|
||||
// using the ability to split links, you can send data to each link
|
||||
// depending on what kind of operation is being sent
|
||||
const link = split(
|
||||
// split based on operation type
|
||||
({ query }) => {
|
||||
const definition = getMainDefinition(query)
|
||||
return (
|
||||
definition.kind === "OperationDefinition" &&
|
||||
definition.operation === "subscription"
|
||||
)
|
||||
},
|
||||
wsLink,
|
||||
httpLink
|
||||
)
|
||||
```
|
||||
|
||||
Now, queries and mutations will go over HTTP as normal, but subscriptions will be done over the websocket transport.
|
||||
|
||||
## useSubscription
|
||||
|
||||
@@ -266,7 +235,7 @@ With a ref:
|
||||
|
||||
```js
|
||||
const variables = ref({
|
||||
channelId: 'abc'
|
||||
channelId: "abc"
|
||||
})
|
||||
|
||||
const { result } = useSubscription(
|
||||
@@ -286,7 +255,7 @@ With a reactive object:
|
||||
|
||||
```js
|
||||
const variables = reactive({
|
||||
channelId: 'abc'
|
||||
channelId: "abc"
|
||||
})
|
||||
|
||||
const { result } = useSubscription(
|
||||
@@ -305,7 +274,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`
|
||||
@@ -338,7 +307,7 @@ const { result } = useSubscription(
|
||||
`,
|
||||
null,
|
||||
{
|
||||
fetchPolicy: 'no-cache'
|
||||
fetchPolicy: "no-cache"
|
||||
}
|
||||
)
|
||||
```
|
||||
@@ -357,7 +326,7 @@ const { result } = useSubscription(
|
||||
`,
|
||||
null,
|
||||
() => ({
|
||||
fetchPolicy: 'no-cache'
|
||||
fetchPolicy: "no-cache"
|
||||
})
|
||||
)
|
||||
```
|
||||
@@ -391,7 +360,7 @@ function enableSub() {
|
||||
You can retrieve the loading and error stats from `useSubscription`:
|
||||
|
||||
```js
|
||||
const { loading, error } = useSubscription(/* ... */)
|
||||
const { loading, error } = useSubscription(...)
|
||||
```
|
||||
|
||||
### Event hooks
|
||||
@@ -401,9 +370,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, context) => {
|
||||
onResult(result => {
|
||||
console.log(result.data)
|
||||
})
|
||||
```
|
||||
@@ -415,50 +384,13 @@ This is triggered when an error occurs:
|
||||
```js
|
||||
import { logErrorMessages } from '@vue/apollo-util'
|
||||
|
||||
const { onError } = useSubscription(/* ... */)
|
||||
const { onError } = useSubscription(...)
|
||||
|
||||
onError((error, context) => {
|
||||
onError(error => {
|
||||
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:
|
||||
@@ -613,7 +545,7 @@ subscribeToMore(() => ({
|
||||
channelId: props.channelId
|
||||
},
|
||||
updateQuery: (previousResult, { subscriptionData }) => {
|
||||
const tmp = [...previousResult]
|
||||
const tmp = [...previousResult]
|
||||
tmp.messages.push(subscriptionData.data.messageAdded)
|
||||
return tmp
|
||||
}
|
||||
@@ -625,16 +557,15 @@ 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,29 +28,28 @@ 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):
|
||||
@@ -66,12 +65,8 @@ export default {
|
||||
You can also specify the client in individual queries, subscriptions and mutations with the `client` property in the options:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
tags: {
|
||||
query: gql`...`,
|
||||
client: 'b',
|
||||
}
|
||||
}
|
||||
tags: {
|
||||
query: gql`...`,
|
||||
client: 'b',
|
||||
}
|
||||
```
|
||||
|
||||
@@ -11,74 +11,69 @@ You shouldn't send the `__typename` fields in the variables, so it is not recomm
|
||||
:::
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
addTag() {
|
||||
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: {
|
||||
// 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,
|
||||
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,
|
||||
label: newTag,
|
||||
},
|
||||
},
|
||||
}).then((data) => {
|
||||
},
|
||||
}).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
|
||||
@@ -99,15 +94,18 @@ schema {
|
||||
}
|
||||
`
|
||||
|
||||
// Fake word generator
|
||||
import faker from 'faker'
|
||||
|
||||
// Let's generate some tags
|
||||
let id = 0
|
||||
const tags = []
|
||||
var id = 0
|
||||
var tags = []
|
||||
for (let i = 0; i < 42; i++) {
|
||||
addTag(faker.random.word())
|
||||
}
|
||||
|
||||
function addTag(label) {
|
||||
const t = {
|
||||
function addTag (label) {
|
||||
let t = {
|
||||
id: id++,
|
||||
label,
|
||||
}
|
||||
@@ -117,12 +115,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)
|
||||
},
|
||||
|
||||
@@ -23,14 +23,12 @@ import gql from 'graphql-tag'
|
||||
Put the [gql](https://github.com/apollographql/graphql-tag) query directly as the value:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
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>`.
|
||||
@@ -38,14 +36,12 @@ 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
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data () {
|
||||
return {
|
||||
// Initialize your apollo data
|
||||
hello: '',
|
||||
}
|
||||
hello: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
Server-side, add the corresponding schema and resolver:
|
||||
@@ -63,7 +59,7 @@ schema {
|
||||
|
||||
export const resolvers = {
|
||||
Query: {
|
||||
hello(root, args, context) {
|
||||
hello (root, args, context) {
|
||||
return 'Hello world!'
|
||||
},
|
||||
},
|
||||
@@ -90,26 +86,22 @@ 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
|
||||
export default {
|
||||
apollo: {
|
||||
world: gql`query {
|
||||
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
|
||||
export default {
|
||||
apollo: {
|
||||
world: {
|
||||
query: gql`query {
|
||||
apollo: {
|
||||
world: {
|
||||
query: gql`query {
|
||||
hello
|
||||
}`,
|
||||
update: data => data.hello
|
||||
}
|
||||
update: data => data.hello
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -117,12 +109,10 @@ export default {
|
||||
You can also rename the field in the GraphQL document directly:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
world: gql`query {
|
||||
apollo: {
|
||||
world: gql`query {
|
||||
world: hello
|
||||
}`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -133,22 +123,20 @@ 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:
|
||||
@@ -161,34 +149,30 @@ 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
|
||||
export default {
|
||||
apollo: {
|
||||
apollo: {
|
||||
// Query with parameters
|
||||
ping: {
|
||||
query: gql`query PingMessage($message: String!) {
|
||||
ping: {
|
||||
query: gql`query PingMessage($message: String!) {
|
||||
ping(message: $message)
|
||||
}`,
|
||||
variables: {
|
||||
message: 'Meow'
|
||||
},
|
||||
// Additional options here
|
||||
fetchPolicy: 'cache-and-network',
|
||||
variables: {
|
||||
message: 'Meow'
|
||||
},
|
||||
// Additional options here
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
Again, you can initialize your property in your vue component:
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data () {
|
||||
return {
|
||||
// Initialize your apollo data
|
||||
ping: '',
|
||||
}
|
||||
},
|
||||
}
|
||||
ping: '',
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
Server-side, add the corresponding schema and resolver:
|
||||
@@ -206,7 +190,7 @@ schema {
|
||||
|
||||
export const resolvers = {
|
||||
Query: {
|
||||
ping(root, { message }, context) {
|
||||
ping (root, { message }, context) {
|
||||
return `Answering ${message}`
|
||||
},
|
||||
},
|
||||
@@ -231,24 +215,22 @@ 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:
|
||||
@@ -284,26 +266,24 @@ 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
|
||||
@@ -315,36 +295,31 @@ 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
|
||||
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`{
|
||||
// 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
|
||||
@@ -356,30 +331,28 @@ 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.
|
||||
@@ -397,29 +370,24 @@ 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
|
||||
@@ -435,15 +403,18 @@ schema {
|
||||
}
|
||||
`
|
||||
|
||||
// Fake word generator
|
||||
import casual from 'casual'
|
||||
|
||||
// Let's generate some tags
|
||||
let id = 0
|
||||
const tags = []
|
||||
var id = 0
|
||||
var tags = []
|
||||
for (let i = 0; i < 42; i++) {
|
||||
addTag(casual.word)
|
||||
}
|
||||
|
||||
function addTag(label) {
|
||||
const t = {
|
||||
function addTag (label) {
|
||||
let t = {
|
||||
id: id++,
|
||||
label,
|
||||
}
|
||||
@@ -465,12 +436,10 @@ export const resolvers = {
|
||||
You can manually add a smart query with the `$apollo.addSmartQuery(key, options)` method:
|
||||
|
||||
```js
|
||||
export default {
|
||||
created() {
|
||||
this.$apollo.addSmartQuery('comments', {
|
||||
created () {
|
||||
this.$apollo.addSmartQuery('comments', {
|
||||
// Same options like above
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ const apolloClient = new ApolloClient({
|
||||
cache,
|
||||
uri: 'http://localhost:4042/graphql',
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
|
||||
# Special options
|
||||
|
||||
The special options begin with `$` in the `apollo` object.
|
||||
|
||||
- `$skipAll` to disable all queries and subscriptions (see below)
|
||||
- `$skip` 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
|
||||
@@ -58,21 +59,17 @@ this.$apollo.skipAll = true
|
||||
You can also declare these properties in the `apollo` option of the component. They can be booleans:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
$skipAll: true
|
||||
}
|
||||
apollo: {
|
||||
$skipAll: true
|
||||
}
|
||||
```
|
||||
|
||||
Or reactive functions:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
$skipAll() {
|
||||
return this.foo === 42
|
||||
}
|
||||
apollo: {
|
||||
$skipAll () {
|
||||
return this.foo === 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,47 +1,26 @@
|
||||
# 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 { 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 { ApolloClient, HttpLink, InMemoryCache, split } from '@apollo/client/core'
|
||||
import { WebSocketLink } from '@apollo/client/link/ws'
|
||||
import { getMainDefinition } from '@apollo/client/utilities'
|
||||
|
||||
// Create an http link:
|
||||
const httpLink = new HttpLink({
|
||||
uri: 'http://localhost:3000/graphql'
|
||||
// You should use an absolute URL here
|
||||
uri: 'http://localhost:3020/graphql',
|
||||
})
|
||||
|
||||
// Create a GraphQLWsLink link:
|
||||
const wsLink = new GraphQLWsLink(
|
||||
createClient({
|
||||
url: 'ws://localhost:5000/',
|
||||
})
|
||||
)
|
||||
// Create the subscription websocket link
|
||||
const wsLink = new WebSocketLink({
|
||||
uri: 'ws://localhost:3000/subscriptions',
|
||||
options: {
|
||||
reconnect: true,
|
||||
},
|
||||
})
|
||||
|
||||
// using the ability to split links, you can send data to each link
|
||||
// depending on what kind of operation is being sent
|
||||
@@ -49,70 +28,47 @@ 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 with cache implementation.
|
||||
// Create the apollo client
|
||||
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
|
||||
export default {
|
||||
apollo: {
|
||||
tags: {
|
||||
query: TAGS_QUERY,
|
||||
subscribeToMore: {
|
||||
document: gql`subscription name($param: String!) {
|
||||
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
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,37 +164,35 @@ 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
|
||||
export default {
|
||||
apollo: {
|
||||
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>`.
|
||||
@@ -251,29 +205,27 @@ When server supports live queries and uses subscriptions to update them, like [H
|
||||
use simple subscriptions for reactive queries:
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tags: [],
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
$subscribe: {
|
||||
tags: {
|
||||
query: gql`subscription {
|
||||
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
|
||||
@@ -281,39 +233,37 @@ export default {
|
||||
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,
|
||||
}
|
||||
},
|
||||
// Result hook
|
||||
result(data) {
|
||||
// Let's update the local data
|
||||
this.tags.push(data.tagAdded)
|
||||
},
|
||||
// Skip the subscription
|
||||
skip() {
|
||||
return this.skipSubscription
|
||||
// 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
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
Here, `skip` will be called automatically when the `skipSubscription` component property changes.
|
||||
@@ -329,12 +279,10 @@ this.$apollo.subscriptions.tags.skip = true
|
||||
You can manually add a smart subscription with the `$apollo.addSmartSubscription(key, options)` method:
|
||||
|
||||
```js
|
||||
export default {
|
||||
created() {
|
||||
this.$apollo.addSmartSubscription('tagAdded', {
|
||||
created () {
|
||||
this.$apollo.addSmartSubscription('tagAdded', {
|
||||
// Same options like '$subscribe' above
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
@@ -347,9 +295,8 @@ 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
|
||||
export default {
|
||||
mounted() {
|
||||
const subQuery = gql`subscription tags($type: String!) {
|
||||
mounted () {
|
||||
const subQuery = gql`subscription tags($type: String!) {
|
||||
tagAdded(type: $type) {
|
||||
id
|
||||
label
|
||||
@@ -357,21 +304,20 @@ export default {
|
||||
}
|
||||
}`
|
||||
|
||||
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)
|
||||
},
|
||||
})
|
||||
},
|
||||
```
|
||||
|
||||
@@ -46,12 +46,3 @@ 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) & [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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
```json
|
||||
```graphqlconfig
|
||||
{
|
||||
"name": "Untitled GraphQL Schema",
|
||||
"schemaPath": "./path/to/schema.graphql",
|
||||
|
||||
@@ -1,31 +1,22 @@
|
||||
---
|
||||
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/
|
||||
home: true
|
||||
sidebar: false
|
||||
heroImage: /logo.png
|
||||
actionText: Get Started →
|
||||
actionLink: /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
|
||||
icon: 🌐
|
||||
footer: LICENCE MIT - Created by Guillaume CHAU (@Akryum)
|
||||
---
|
||||
|
||||
<SponsorButton/>
|
||||
|
||||
<h2 style="text-align: center; font-size: 2rem;">Sponsors</h2>
|
||||
## Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="https://guillaume-chau.info/sponsors/" target="_blank">
|
||||
|
||||
@@ -23,19 +23,19 @@ npm install --save @vue/apollo-option @apollo/client
|
||||
Before:
|
||||
|
||||
```js
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||
import Vue from 'vue'
|
||||
import { ApolloClient } from 'apollo-client'
|
||||
import { HttpLink } from 'apollo-link-http'
|
||||
import Vue from 'vue'
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||
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
|
||||
|
||||
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 3.4 KiB |
@@ -1,81 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
@@ -1,51 +0,0 @@
|
||||
<?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>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -209,7 +209,7 @@ export default {
|
||||
签名:
|
||||
|
||||
```ts
|
||||
function mutate(options = null): Promise<FetchResult>
|
||||
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)
|
||||
},
|
||||
|
||||
@@ -24,58 +24,56 @@
|
||||
示例:
|
||||
|
||||
```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` 也可以这样写:
|
||||
@@ -87,18 +85,14 @@ update: data => data.ping
|
||||
手动模式示例:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
myData: {
|
||||
query: gql`...`,
|
||||
manual: true,
|
||||
result({ data, loading }) {
|
||||
if (!loading) {
|
||||
this.items = data.items
|
||||
}
|
||||
},
|
||||
{
|
||||
query: gql`...`,
|
||||
manual: true,
|
||||
result ({ data, loading }) {
|
||||
if (!loading) {
|
||||
this.items = data.items
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -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__',
|
||||
// 变量设置到的全局对象
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
- `network-only`:从网络返回结果并保存到缓存,如果网络调用未成功则失败。
|
||||
- `no-cache`:从网络返回结果但不保存到缓存,如果网络调用未成功则失败。
|
||||
|
||||
- `fetchResults`:是否获取结果。
|
||||
|
||||
- `metadata`:当前查询在存储中的任意元数据。可用于调试、开发人员工具等场景。
|
||||
|
||||
- `notifyOnNetworkStatusChange`:网络状态更新时是否应在此查询的观察者上触发下一步。
|
||||
@@ -59,3 +61,4 @@
|
||||
- `onResult(handler)`:有新结果可用时调用的事件钩子。
|
||||
|
||||
- `onError(handler)`:发生错误时调用的事件钩子。
|
||||
|
||||
|
||||
@@ -36,3 +36,4 @@
|
||||
- `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 @@ const item = {
|
||||
现在我们可以将 `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` 类型:
|
||||
|
||||
```gql
|
||||
```js
|
||||
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
|
||||
export default {
|
||||
apollo: {
|
||||
todoItems: {
|
||||
query: todoItemsQuery
|
||||
}
|
||||
},
|
||||
}
|
||||
// App.vue
|
||||
|
||||
apollo: {
|
||||
todoItems: {
|
||||
query: todoItemsQuery
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
## 使用变更修改本地数据
|
||||
@@ -209,7 +209,7 @@ const checkItemMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
checkItem(id: $id) @client
|
||||
}
|
||||
`
|
||||
`;
|
||||
```
|
||||
|
||||
我们定义了一个**本地**变更(因为在这里写了一个 `@client` 指令),它将接受一个唯一的标识符作为参数。现在,我们需要一个**解析器**:一个解析 schema 中类型或字段的值的函数。
|
||||
@@ -224,14 +224,13 @@ 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;
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
在这里我们做了什么?
|
||||
@@ -256,7 +255,6 @@ const resolvers = {
|
||||
cache.writeQuery({ query: todoItemsQuery, data });
|
||||
return currentItem.done;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
const apolloClient = new ApolloClient({
|
||||
@@ -269,14 +267,14 @@ const apolloClient = new ApolloClient({
|
||||
现在,我们可以在 Vue 组件中像普通的 Apollo [变更](../guide-option/mutations.md) 一样使用此变更:
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
checkItem(id) {
|
||||
this.$apollo.mutate({
|
||||
mutation: checkItemMutation,
|
||||
variables: { id }
|
||||
})
|
||||
},
|
||||
}
|
||||
// App.vue
|
||||
|
||||
methods: {
|
||||
checkItem(id) {
|
||||
this.$apollo.mutate({
|
||||
mutation: checkItemMutation,
|
||||
variables: { id }
|
||||
});
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
@@ -71,7 +71,7 @@ export default {
|
||||
description
|
||||
}
|
||||
}`,
|
||||
variables() {
|
||||
variables () {
|
||||
return {
|
||||
id: this.id,
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ const sourceSchema = `
|
||||
twitter: String
|
||||
}
|
||||
|
||||
|
||||
type Query {
|
||||
allHeroes: [VueHero]
|
||||
}
|
||||
@@ -71,14 +72,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,
|
||||
})
|
||||
@@ -87,7 +88,7 @@ const schema = makeExecutableSchema({
|
||||
|
||||
```js
|
||||
import { addMockFunctionsToSchema } from 'graphql-tools'
|
||||
// ...
|
||||
...
|
||||
addMockFunctionsToSchema({
|
||||
schema,
|
||||
})
|
||||
@@ -110,7 +111,7 @@ const query = `
|
||||
在测试用例中调用 GraphQL 查询,保存响应到组件数据中,然后检查渲染完成的组件是否与快照匹配:
|
||||
|
||||
```js
|
||||
graphql(schema, query).then((result) => {
|
||||
graphql(schema, query).then(result => {
|
||||
wrapper.setData(result.data)
|
||||
expect(wrapper.element).toMatchSnapshot()
|
||||
})
|
||||
@@ -148,4 +149,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,6 +25,7 @@ const apolloClient = new ApolloClient({
|
||||
cache,
|
||||
uri: 'http://localhost:4042/graphql',
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
@@ -63,17 +63,15 @@ export default {
|
||||
将新项添加到缓存中:
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
onMessageAdded(previousResult, { subscriptionData }) {
|
||||
methods: {
|
||||
onMessageAdded (previousResult, { subscriptionData }) {
|
||||
// 之前的结果是不可变的
|
||||
const newResult = {
|
||||
messages: [...previousResult.messages],
|
||||
}
|
||||
// 添加问题到列表中
|
||||
newResult.messages.push(subscriptionData.data.messageAdded)
|
||||
return newResult
|
||||
const newResult = {
|
||||
messages: [...previousResult.messages],
|
||||
}
|
||||
// 添加问题到列表中
|
||||
newResult.messages.push(subscriptionData.data.messageAdded)
|
||||
return newResult
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -81,25 +79,22 @@ export default {
|
||||
从缓存中删除一项:
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
onMessageAdded(previousResult, { subscriptionData }) {
|
||||
const removedMessage = subscriptionData.data.messageRemoved
|
||||
const index = previousResult.messages.findIndex(
|
||||
m => m.id === removedMessage.id
|
||||
)
|
||||
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],
|
||||
}
|
||||
// 从列表中移除问题
|
||||
newResult.messages.splice(index, 1)
|
||||
return newResult
|
||||
// 之前的结果是不可变的
|
||||
const newResult = {
|
||||
messages: [...previousResult.messages],
|
||||
}
|
||||
// 从列表中移除问题
|
||||
newResult.messages.splice(index, 1)
|
||||
return newResult
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -61,16 +61,14 @@ 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}`)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -80,7 +78,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)
|
||||
})
|
||||
```
|
||||
@@ -95,7 +93,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 RepoInfoEntryFragment } from './RepoInfo.vue'
|
||||
import { entryFragment as VoteButtonsEntryFragment } from './VoteButtons.vue'
|
||||
import { entryFragment as RepoInfoEntryFragment } from './RepoInfo.vue'
|
||||
|
||||
export const entryFragment = gql`
|
||||
fragment FeedEntry on Entry {
|
||||
@@ -216,11 +216,11 @@ query {
|
||||
2. 在构建过程中,使用 `possibleTypes.json` 来配置缓存。然后,将新配置的缓存传递给 `ApolloClient` 以完成这个过程。
|
||||
|
||||
```js
|
||||
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core'
|
||||
import { ApolloClient, InMemoryCache, createHttpLink } 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 { DefaultApolloClient } from '@vue/apollo-composable'
|
||||
import { provide } from 'vue'
|
||||
import { DefaultApolloClient } from '@vue/apollo-composable'
|
||||
|
||||
const app = new Vue({
|
||||
setup() {
|
||||
setup () {
|
||||
provide(DefaultApolloClient, apolloClient)
|
||||
},
|
||||
|
||||
@@ -36,11 +36,11 @@ const app = new Vue({
|
||||
你也可以提供为应用提供多个可用的 Apollo Client 实例。在这种情况下,建议提供一个 `default` 值:
|
||||
|
||||
```js
|
||||
import { ApolloClients } from '@vue/apollo-composable'
|
||||
import { provide } from 'vue'
|
||||
import { ApolloClients } from '@vue/apollo-composable'
|
||||
|
||||
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,19 +555,18 @@ 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,29 +28,28 @@ 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` 为所有的查询、订阅和变更定义要使用的客户端(仅限在此组件内):
|
||||
@@ -66,12 +65,8 @@ export default {
|
||||
你也可以在单个查询,订阅和变更的选项中使用 `client` 属性来指定客户端:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
tags: {
|
||||
query: gql`...`,
|
||||
client: 'b',
|
||||
}
|
||||
}
|
||||
tags: {
|
||||
query: gql`...`,
|
||||
client: 'b',
|
||||
}
|
||||
```
|
||||
|
||||
@@ -11,66 +11,61 @@
|
||||
:::
|
||||
|
||||
```js
|
||||
export default {
|
||||
methods: {
|
||||
addTag() {
|
||||
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: {
|
||||
// 参数
|
||||
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,
|
||||
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,
|
||||
label: newTag,
|
||||
},
|
||||
},
|
||||
}).then((data) => {
|
||||
},
|
||||
}).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
|
||||
@@ -91,15 +86,18 @@ schema {
|
||||
}
|
||||
`
|
||||
|
||||
// 假数据生成器
|
||||
import faker from 'faker'
|
||||
|
||||
// 生成一些标签
|
||||
let id = 0
|
||||
const tags = []
|
||||
var id = 0
|
||||
var tags = []
|
||||
for (let i = 0; i < 42; i++) {
|
||||
addTag(faker.random.word())
|
||||
}
|
||||
|
||||
function addTag(label) {
|
||||
const t = {
|
||||
function addTag (label) {
|
||||
let t = {
|
||||
id: id++,
|
||||
label,
|
||||
}
|
||||
@@ -109,15 +107,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)
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
```
|
||||
@@ -23,12 +23,10 @@ import gql from 'graphql-tag'
|
||||
直接将 [gql](https://github.com/apollographql/graphql-tag) 查询作为值:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
apollo: {
|
||||
// 简单的查询,将更新 'hello' 这个 vue 属性
|
||||
hello: gql`{hello}`,
|
||||
},
|
||||
}
|
||||
hello: gql`{hello}`,
|
||||
},
|
||||
```
|
||||
|
||||
接下来你可以通过 `this.$apollo.queries.<name>` 访问这个查询。
|
||||
@@ -36,14 +34,12 @@ export default {
|
||||
你可以在 vue 组件的 `data` 钩子中初始化属性:
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data () {
|
||||
return {
|
||||
// 初始化你的 apollo 数据
|
||||
hello: '',
|
||||
}
|
||||
hello: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
在服务端添加相应的 schema 和解析器:
|
||||
@@ -61,7 +57,7 @@ schema {
|
||||
|
||||
export const resolvers = {
|
||||
Query: {
|
||||
hello(root, args, context) {
|
||||
hello (root, args, context) {
|
||||
return 'Hello world!'
|
||||
},
|
||||
},
|
||||
@@ -88,26 +84,22 @@ export const resolvers = {
|
||||
请注意,初学者常见的错误是使用与查询中的字段名不相同的数据名称,例如:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
world: gql`query {
|
||||
apollo: {
|
||||
world: gql`query {
|
||||
hello
|
||||
}`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
注意 `world` 与 `hello` 的不同之处:`vue-apollo` 不会去猜测你想要将哪些数据从查询结果中放入组件中。默认情况下,它只会尝试你在组件中使用的数据名称(即 `apollo` 对象中的键),在本例中为 `world`。如果名称不匹配,你可以使用 `update` 选项来告诉 `vue-apollo` 在结果中使用什么样的数据:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
world: {
|
||||
query: gql`query {
|
||||
apollo: {
|
||||
world: {
|
||||
query: gql`query {
|
||||
hello
|
||||
}`,
|
||||
update: data => data.hello
|
||||
}
|
||||
update: data => data.hello
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -115,12 +107,10 @@ export default {
|
||||
你也可以直接在 GraphQL 文档中重命名该字段:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
world: gql`query {
|
||||
apollo: {
|
||||
world: gql`query {
|
||||
world: hello
|
||||
}`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -131,22 +121,20 @@ export default {
|
||||
你可以通过在对象中声明 `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` 中的选项,比如:
|
||||
@@ -159,34 +147,30 @@ export default {
|
||||
例如,你可以像这样添加 `fetchPolicy` apollo 选项:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
apollo: {
|
||||
// 带参数的查询
|
||||
ping: {
|
||||
query: gql`query PingMessage($message: String!) {
|
||||
ping: {
|
||||
query: gql`query PingMessage($message: String!) {
|
||||
ping(message: $message)
|
||||
}`,
|
||||
variables: {
|
||||
message: 'Meow'
|
||||
},
|
||||
// 在这里加入其他选项
|
||||
fetchPolicy: 'cache-and-network',
|
||||
variables: {
|
||||
message: 'Meow'
|
||||
},
|
||||
// 在这里加入其他选项
|
||||
fetchPolicy: 'cache-and-network',
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
同样的,你可以在 vue 组件中初始化属性:
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
data () {
|
||||
return {
|
||||
// 初始化你的 apollo 数据
|
||||
ping: '',
|
||||
}
|
||||
},
|
||||
}
|
||||
ping: '',
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
在服务端添加相应的 schema 和解析器:
|
||||
@@ -204,7 +188,7 @@ schema {
|
||||
|
||||
export const resolvers = {
|
||||
Query: {
|
||||
ping(root, { message }, context) {
|
||||
ping (root, { message }, context) {
|
||||
return `Answering ${message}`
|
||||
},
|
||||
},
|
||||
@@ -243,26 +227,24 @@ 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
|
||||
@@ -274,36 +256,31 @@ export default {
|
||||
你可以使用函数定义 `query` 选项。这将自动更新 graphql 查询的定义:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
// 特定标签可以是随机标签或最后添加的标签
|
||||
featuredTag: {
|
||||
query() {
|
||||
// 这里你可以用'this' 访问组件实例
|
||||
if (this.showTag === 'random') {
|
||||
return gql`{
|
||||
// 特定标签可以是随机标签或最后添加的标签
|
||||
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
|
||||
@@ -315,24 +292,22 @@ export default {
|
||||
使用函数使 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,
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
在每次参数更改时,将重新获取查询,例如:
|
||||
@@ -354,30 +329,28 @@ export default {
|
||||
如果查询被跳过,它将被禁用且结果将不再被更新。你可以使用 `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` 将被自动调用。
|
||||
@@ -395,29 +368,24 @@ 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
|
||||
@@ -433,15 +401,18 @@ schema {
|
||||
}
|
||||
`
|
||||
|
||||
// 假数据生成器
|
||||
import casual from 'casual'
|
||||
|
||||
// 生成一些标签
|
||||
let id = 0
|
||||
const tags = []
|
||||
var id = 0
|
||||
var tags = []
|
||||
for (let i = 0; i < 42; i++) {
|
||||
addTag(casual.word)
|
||||
}
|
||||
|
||||
function addTag(label) {
|
||||
const t = {
|
||||
function addTag (label) {
|
||||
let t = {
|
||||
id: id++,
|
||||
label,
|
||||
}
|
||||
@@ -451,7 +422,7 @@ function addTag(label) {
|
||||
|
||||
export const resolvers = {
|
||||
Query: {
|
||||
tags(root, args, context) {
|
||||
tags (root, args, context) {
|
||||
return tags
|
||||
},
|
||||
},
|
||||
@@ -463,12 +434,10 @@ export const resolvers = {
|
||||
你可以使用 `$apollo.addSmartQuery(key, options)` 方法手动添加智能查询:
|
||||
|
||||
```js
|
||||
export default {
|
||||
created() {
|
||||
this.$apollo.addSmartQuery('comments', {
|
||||
created () {
|
||||
this.$apollo.addSmartQuery('comments', {
|
||||
// 选项同上文
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ const apolloClient = new ApolloClient({
|
||||
cache,
|
||||
uri: 'http://localhost:4042/graphql',
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
::: warning
|
||||
|
||||
@@ -58,21 +58,17 @@ this.$apollo.skipAll = true
|
||||
你也可以在组件的 `apollo` 选项中声明这些属性。它们可以是布尔值:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
$skipAll: true
|
||||
}
|
||||
apollo: {
|
||||
$skipAll: true
|
||||
}
|
||||
```
|
||||
|
||||
或是响应式函数:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
$skipAll() {
|
||||
return this.foo === 42
|
||||
}
|
||||
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,29 +48,27 @@ const apolloClient = new ApolloClient({
|
||||
如果你需要更新一个来自订阅的智能查询结果,最好的方式是使用 `subscribeToMore` 查询方法。它将创建链接到智能查询的 [智能订阅](../api/smart-subscription.md)。你只需要将 `subscribeToMore` 添加到智能查询中:
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
tags: {
|
||||
query: TAGS_QUERY,
|
||||
subscribeToMore: {
|
||||
document: gql`subscription name($param: String!) {
|
||||
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 }) => {
|
||||
// 在这里用之前的结果和新数据组合成新的结果
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,36 +164,34 @@ this.$watch(() => this.type, (type, oldType) => {
|
||||
你可以在 `apollo` 选项中使用 `$subscribe` 关键字来声明 [智能订阅](../api/smart-subscription.md):
|
||||
|
||||
```js
|
||||
export default {
|
||||
apollo: {
|
||||
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>` 访问这个订阅。
|
||||
@@ -207,29 +203,27 @@ export default {
|
||||
当服务端支持实时查询并使用订阅更新它们时,例如 [Hasura](https://hasura.io/),你可以使用简单订阅来实现响应式查询:
|
||||
|
||||
```js
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tags: [],
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
$subscribe: {
|
||||
tags: {
|
||||
query: gql`subscription {
|
||||
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;
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
## 跳过订阅
|
||||
@@ -237,39 +231,37 @@ export default {
|
||||
如果订阅被跳过,它将被禁用且不再被更新。你可以使用 `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,
|
||||
}
|
||||
},
|
||||
// 结果钩子
|
||||
result(data) {
|
||||
// 更新本地数据
|
||||
this.tags.push(data.tagAdded)
|
||||
},
|
||||
// 跳过这个订阅
|
||||
skip() {
|
||||
return this.skipSubscription
|
||||
// 响应式变量
|
||||
variables () {
|
||||
return {
|
||||
type: this.type,
|
||||
}
|
||||
},
|
||||
// 结果钩子
|
||||
result (data) {
|
||||
// 更新本地数据
|
||||
this.tags.push(data.tagAdded)
|
||||
},
|
||||
// 跳过这个订阅
|
||||
skip () {
|
||||
return this.skipSubscription
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
在这里,当 `skipSubscription` 组件属性改变时,`skip` 将被自动调用。
|
||||
@@ -285,12 +277,10 @@ this.$apollo.subscriptions.tags.skip = true
|
||||
你可以使用 `$apollo.addSmartSubscription(key, options)` 方法手动添加智能订阅:
|
||||
|
||||
```js
|
||||
export default {
|
||||
created() {
|
||||
this.$apollo.addSmartSubscription('tagAdded', {
|
||||
created () {
|
||||
this.$apollo.addSmartSubscription('tagAdded', {
|
||||
// 选项同 '$subscribe'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
@@ -303,9 +293,8 @@ export default {
|
||||
使用 `$apollo.subscribe()` 方法来创建一个 GraphQL 订阅,当组件被销毁时将自动终止。它**不会**创建智能订阅。
|
||||
|
||||
```js
|
||||
export default {
|
||||
mounted() {
|
||||
const subQuery = gql`subscription tags($type: String!) {
|
||||
mounted () {
|
||||
const subQuery = gql`subscription tags($type: String!) {
|
||||
tagAdded(type: $type) {
|
||||
id
|
||||
label
|
||||
@@ -313,21 +302,20 @@ export default {
|
||||
}
|
||||
}`
|
||||
|
||||
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,7 +4,6 @@ node_modules
|
||||
|
||||
/tests/e2e/videos/
|
||||
/tests/e2e/screenshots/
|
||||
/tests/e2e/downloads/
|
||||
|
||||
|
||||
# local env files
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset',
|
||||
],
|
||||
}
|
||||
@@ -1,26 +1,22 @@
|
||||
import axios from 'axios'
|
||||
import { defineConfig } from 'cypress'
|
||||
import vitePreprocessor from 'cypress-vite'
|
||||
import axios from 'axios'
|
||||
|
||||
export default defineConfig({
|
||||
module.exports = 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) {
|
||||
setupNodeEvents (on, config) {
|
||||
on('task', {
|
||||
'db:reset': async function () {
|
||||
async 'db:reset' () {
|
||||
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.ts',
|
||||
supportFile: 'tests/e2e/support/index.js',
|
||||
},
|
||||
})
|
||||
|
||||
@@ -3,40 +3,42 @@
|
||||
"version": "4.0.0-alpha.16",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"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"
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.16",
|
||||
"@apollo/client": "^3.6.9",
|
||||
"@vue/apollo-composable": "workspace:*",
|
||||
"@vue/apollo-util": "workspace:*",
|
||||
"graphql": "^16.7.1",
|
||||
"apollo-server-express": "^2.25.4",
|
||||
"core-js": "^3.23.2",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.1",
|
||||
"graphql": "^15.8.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"graphql-ws": "^5.15.0",
|
||||
"pinia": "^2.1.6",
|
||||
"test-server": "workspace:*",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4"
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"shortid": "^2.2.16",
|
||||
"vue": "^3.2.37",
|
||||
"vue-demi": "^0.13.1",
|
||||
"vue-router": "^4.0.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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"
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/* eslint-disable */
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
plugins: [
|
||||
require('tailwindcss')(),
|
||||
require('autoprefixer')(),
|
||||
],
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -4,13 +4,14 @@
|
||||
<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 dashboard doesn't work properly without JavaScript enabled. Please enable it to continue. </strong>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> 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>
|
||||
@@ -1,11 +1,7 @@
|
||||
import { makeExecutableSchema } from '@graphql-tools/schema'
|
||||
import { PubSub, withFilter } from 'graphql-subscriptions'
|
||||
import gql from 'graphql-tag'
|
||||
import shortid from 'shortid'
|
||||
import { channels } from './data.js'
|
||||
import { GraphQLErrorWithCode } from './util.js'
|
||||
|
||||
const pubsub = new PubSub()
|
||||
const express = require('express')
|
||||
const cors = require('cors')
|
||||
const { gql, ApolloServer, ApolloError, PubSub, withFilter } = require('apollo-server-express')
|
||||
const shortid = require('shortid')
|
||||
|
||||
const typeDefs = gql`
|
||||
type Channel {
|
||||
@@ -51,38 +47,48 @@ type Subscription {
|
||||
}
|
||||
`
|
||||
|
||||
interface AddMessageInput {
|
||||
channelId: string
|
||||
text: string
|
||||
const pubsub = new PubSub()
|
||||
|
||||
let channels = []
|
||||
|
||||
function resetDatabase () {
|
||||
channels = [
|
||||
{
|
||||
id: 'general',
|
||||
label: 'General',
|
||||
messages: [],
|
||||
},
|
||||
{
|
||||
id: 'random',
|
||||
label: 'Random',
|
||||
messages: [],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
interface UpdateMessageInput {
|
||||
id: string
|
||||
channelId: string
|
||||
text: string
|
||||
}
|
||||
resetDatabase()
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
hello: () => 'Hello world!',
|
||||
channels: () => channels,
|
||||
channel: (root: any, { id }: { id: string }) => channels.find(c => c.id === id),
|
||||
channel: (root, { id }) => channels.find(c => c.id === id),
|
||||
list: () => ['a', 'b', 'c'],
|
||||
good: () => 'good',
|
||||
bad: async () => {
|
||||
bad: () => {
|
||||
throw new Error('An error')
|
||||
},
|
||||
},
|
||||
|
||||
Mutation: {
|
||||
addMessage: (root: any, { input }: { input: AddMessageInput }) => {
|
||||
addMessage: (root, { input }) => {
|
||||
const channel = channels.find(c => c.id === input.channelId)
|
||||
if (!channel) {
|
||||
throw new GraphQLErrorWithCode(`Channel ${input.channelId} not found`, 'not-found')
|
||||
throw new ApolloError(`Channel ${input.channelId} not found`, 'not-found')
|
||||
}
|
||||
const message = {
|
||||
id: shortid(),
|
||||
channel,
|
||||
channel: channel,
|
||||
text: input.text,
|
||||
}
|
||||
channel.messages.push(message)
|
||||
@@ -90,15 +96,12 @@ const resolvers = {
|
||||
return message
|
||||
},
|
||||
|
||||
updateMessage: (root: any, { input }: { input: UpdateMessageInput }) => {
|
||||
updateMessage: (root, { input }) => {
|
||||
const channel = channels.find(c => c.id === input.channelId)
|
||||
if (!channel) {
|
||||
throw new GraphQLErrorWithCode(`Channel ${input.channelId} not found`, 'not-found')
|
||||
throw new ApolloError(`Channel ${input.channelId} not found`, 'not-found')
|
||||
}
|
||||
const message = channel.messages.find(m => m.id === input.id)
|
||||
if (!message) {
|
||||
throw new GraphQLErrorWithCode(`Message ${input.id} not found`, 'not-found')
|
||||
}
|
||||
Object.assign(message, {
|
||||
text: input.text,
|
||||
})
|
||||
@@ -124,7 +127,27 @@ const resolvers = {
|
||||
},
|
||||
}
|
||||
|
||||
export const schema = makeExecutableSchema({
|
||||
const app = express()
|
||||
|
||||
app.use(cors('*'))
|
||||
|
||||
app.get('/_reset', (req, res) => {
|
||||
resetDatabase()
|
||||
res.status(200).end()
|
||||
})
|
||||
|
||||
const server = new ApolloServer({
|
||||
typeDefs,
|
||||
resolvers,
|
||||
context: () => new Promise(resolve => {
|
||||
setTimeout(() => resolve({}), 50)
|
||||
}),
|
||||
})
|
||||
|
||||
server.applyMiddleware({ app })
|
||||
|
||||
app.listen({
|
||||
port: 4042,
|
||||
}, () => {
|
||||
console.log('🚀 Server ready at http://localhost:4042')
|
||||
})
|
||||
@@ -1,9 +1,6 @@
|
||||
import { ApolloClient, createHttpLink, InMemoryCache, split } from '@apollo/client/core'
|
||||
import { ApolloClient, InMemoryCache, createHttpLink } 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()
|
||||
|
||||
@@ -13,32 +10,12 @@ 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(splitLink),
|
||||
link: errorLink.concat(httpLink),
|
||||
})
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
@tailwind components;
|
||||
|
||||
@tailwind utilities;
|
||||
@tailwind utilities;
|
||||
@@ -1,21 +1,19 @@
|
||||
<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
|
||||
v-if="displayChannels"
|
||||
class="w-1/4 border-r border-gray-200"
|
||||
/>
|
||||
<ChannelList 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,26 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts">
|
||||
import gql from 'graphql-tag'
|
||||
import { useQuery } from '@vue/apollo-composable'
|
||||
import { computed } from 'vue'
|
||||
import { defineComponent, computed } from 'vue'
|
||||
|
||||
interface Channel {
|
||||
id: string
|
||||
label: string
|
||||
}
|
||||
|
||||
const { result, loading } = useQuery<{ channels: Channel[] }>(gql`
|
||||
query channels {
|
||||
channels {
|
||||
...channel
|
||||
}
|
||||
}
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const { result, loading } = useQuery<{ channels: Channel[] }>(gql`
|
||||
query channels {
|
||||
channels {
|
||||
id
|
||||
label
|
||||
}
|
||||
}
|
||||
`)
|
||||
const channels = computed(() => result.value?.channels ?? [])
|
||||
|
||||
fragment channel on Channel {
|
||||
id
|
||||
label
|
||||
}
|
||||
`)
|
||||
const channels = computed(() => result.value?.channels ?? [])
|
||||
return {
|
||||
loading,
|
||||
channels,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
<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>
|
||||
@@ -1,51 +0,0 @@
|
||||
<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>
|
||||
@@ -1,25 +0,0 @@
|
||||
<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,16 +22,12 @@ 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
|
||||
text
|
||||
label
|
||||
messages {
|
||||
id
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
`, () => ({
|
||||
@@ -67,46 +63,42 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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
|
||||
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="channel"
|
||||
class="flex flex-col h-full"
|
||||
ref="messagesEl"
|
||||
class="flex-1 overflow-y-auto"
|
||||
>
|
||||
<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"
|
||||
<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>
|
||||
</template>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<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>
|
||||
@@ -1,16 +0,0 @@
|
||||
<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>
|
||||
@@ -1,115 +0,0 @@
|
||||
<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>
|
||||