【问题标题】:How to initialize ApolloClient in SvelteKit to work on both SSR and client side如何在 SvelteKit 中初始化 ApolloClient 以同时在 SSR 和客户端上工作
【发布时间】:2021-07-12 01:36:10
【问题描述】:

我试过但没有用。出现错误:评估 SSR 模块时出错 /node_modules/cross-fetch/dist/browser-ponyfill.js:

<script lang="ts">
import fetch from 'cross-fetch';
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client";

const client = new ApolloClient({
    ssrMode: true,
    link: new HttpLink({ uri: '/graphql', fetch }),
    uri: 'http://localhost:4000/graphql',
    cache: new InMemoryCache()
  });
</script>

【问题讨论】:

    标签: apollo svelte sveltekit


    【解决方案1】:

    对于 SvelteKit,CSR 与 SSR 的主题以及应该在何处获取数据比使用其他有点“相似”的解决方案更深入一些。下面的指南应该可以帮助您连接一些点,但需要先说明几件事。

    要定义服务器端路由,请在 src/routes 目录树的任意位置创建一个扩展名为 .js 的文件。这个.js 文件可以包含所有需要的导入语句,而不会将它们引用的 JS 包发送到 Web 浏览器。

    @apollo/client 非常庞大,因为它包含 react 依赖项。相反,您可能想考虑只导入 @apollo/client/core,即使您将 Apollo 客户端设置为仅在服务器端使用,如下面的演示所示。 @apollo/client 不是 ESM 包。请注意它是如何导入下面的,以便使用节点适配器成功构建项目。

    尝试执行以下步骤。

    1. 创建一个新的 SvelteKit 应用并在 SvelteKit 设置向导的第一步中选择“SvelteKit 演示应用”。回答“使用 TypeScript?”向N 提问以及之后的所有问题。
    npm init svelte@next demo-app
    cd demo-app
    
    1. 相应地修改package.json。可以选择使用npx npm-check-updates -u 检查所有软件包更新
    {
        "name": "demo-app",
        "version": "0.0.1",
        "scripts": {
            "dev": "svelte-kit dev",
            "build": "svelte-kit build --verbose",
            "preview": "svelte-kit preview"
        },
        "devDependencies": {
            "@apollo/client": "^3.3.15",
            "@sveltejs/adapter-node": "next",
            "@sveltejs/kit": "next",
            "graphql": "^15.5.0",
            "node-fetch": "^2.6.1",
            "svelte": "^3.37.0"
        },
        "type": "module",
        "dependencies": {
            "@fontsource/fira-mono": "^4.2.2",
            "@lukeed/uuid": "^2.0.0",
            "cookie": "^0.4.1"
        }
    }
    
    1. 相应地修改svelte.config.js
    import node from '@sveltejs/adapter-node';
    
    export default {
        kit: {
            // By default, `npm run build` will create a standard Node app.
            // You can create optimized builds for different platforms by
            // specifying a different adapter
            adapter: node(),
    
            // hydrate the <div id="svelte"> element in src/app.html
            target: '#svelte'
        }
    };
    
    1. 使用以下内容创建src/lib/Client.js 文件。这是 Apollo 客户端设置文件。
    import fetch from 'node-fetch';
    import { ApolloClient, HttpLink } from '@apollo/client/core/core.cjs.js';
    import { InMemoryCache } from '@apollo/client/cache/cache.cjs.js';
    
    class Client {
        constructor() {
            if (Client._instance) {
                return Client._instance
            }
            Client._instance = this;
    
            this.client = this.setupClient();
        }
    
        setupClient() {
            const link = new HttpLink({
                uri: 'http://localhost:4000/graphql',
                fetch
            });
    
            const client = new ApolloClient({
                link,
                cache: new InMemoryCache()
            });
            return client;
        }
    }
    
    export const client = (new Client()).client;
    
    
    1. 使用以下内容创建src/routes/qry/test.js。这是服务器端路由。如果 graphql 架构没有 double 函数,请指定不同的查询、输入和输出。
    import { client } from '$lib/Client.js';
    import { gql } from '@apollo/client/core/core.cjs.js';
    
    export const post = async request => {
        const { num } = request.body;
    
        try {
            const query = gql`
                query Doubled($x: Int) {
                    double(number: $x)
                }
            `;
            const result = await client.query({
                query,
                variables: { x: num }
            });
    
            return {
                status: 200,
                body: {
                    nodes: result.data.double
                }
            }
        } catch (err) {
            return {
                status: 500,
                error: 'Error retrieving data'
            }
        }
    }
    
    
    1. routes/todos/index.svelte文件的load函数&lt;script context="module"&gt;...&lt;/script&gt;标签内添加以下内容。
        try {
            const res = await fetch('/qry/test', {
                method: 'POST',
                credentials: 'same-origin',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    num: 19
                })
            });
            const data = await res.json();
            console.log(data);
        } catch (err) {
            console.error(err);
        }
    
    1. 最后执行npm installnpm run dev命令。在您的 Web 浏览器中加载该站点,并在您将鼠标悬停在导航栏上的 TODOS 链接上时查看正在从客户端查询的服务器端路由。在控制台的网络选项卡中,请注意 test 路由对每秒和后续请求的响应速度有多快,这要归功于 Apollo client 实例是单例。

    【讨论】:

      【解决方案2】:

      在使用上述 phaeth 解决方案时要记住两件事:缓存和经过身份验证的请求。

      由于客户端在端点 /qry/test.js 中使用,具有缓存行为的单例模式使您的服务器有状态。因此,如果 A 然后 B 进行相同的查询 B 最终可能会看到 A 的一些数据。

      如果您在查询中需要授权标头,也会出现同样的问题。您需要像这样在 setupClient 方法中进行设置

      setupClient(sometoken) {
        ...
      
        const authLink = setContext((_, { headers }) => {
          return {
            headers: {
              ...headers,
              authorization: `Bearer ${sometoken}`
            }
          };
        });
      
        const client = new ApolloClient({
          credentials: 'include',
          link: authLink.concat(link),
          cache: new InMemoryCache()
        });
      }
      

      但是对于单例模式,如果您有多个用户,这就会出现问题。

      为了使您的服务器保持无状态,一种解决方法是避免单例模式并在端点中创建一个new Client(sometoken)

      这不是一个最佳解决方案:它在每个请求上重新创建客户端,基本上只是擦除缓存。但这解决了当您有多个用户时的缓存和授权问题。

      【讨论】:

      • 在这种情况下,我建议从@apollo/client/core 包切换到@urql/core 包,因为urql 自己持有客户端实例。 urql API 与 apollo 的 API 没有太大区别。如果你有兴趣看看他们的addAuthToOperation
      猜你喜欢
      • 2022-12-25
      • 2019-10-16
      • 1970-01-01
      • 1970-01-01
      • 2013-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多