3

我试过但没有用。出现错误:评估 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>
4

2 回答 2

8

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

要定义服务器端路由,请在目录树的任何位置创建一个.js扩展名为的文件。src/routes.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. 将以下内容添加到标签内的文件load功能中。routes/todos/index.svelte<script context="module">...</script>
    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导航栏上的链接上时查看正在从客户端查询的服务器端路由。在控制台的网络选项卡中,请注意由于 Apollo实例是单例test的,因此每秒和后续请求的路由响应要快多少。client
于 2021-04-19T18:59:03.840 回答
0

使用上述 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)在端点中创建一个。

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

于 2021-11-05T08:17:46.373 回答