You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Sapper后端代码会在浏览器中执行?context='module'脚本疑问

问题原因分析

你的核心困惑点在于Sapper中context="module"下的preload函数并非只在服务器端执行——Sapper的设计是:preload(不管是否在context="module"中)会在两个时机运行:

  1. 服务器端首次渲染页面时执行一次,用于生成初始HTML;
  2. 浏览器端通过路由导航到该页面时再次执行,用于保证前后端数据状态一致。

而你在calls.js中直接使用了Node.js的process全局变量,同时因为组件模块导入了calls.js,Sapper会把这个模块打包进客户端的index.js中。当浏览器执行这段代码时,自然会抛出process is not defined的错误——浏览器环境根本没有process这个变量。

解决方案

下面提供几种逐步优化的方案,从最直观到最符合Sapper最佳实践:

方案1:用Server Routes隔离后端调用(最推荐)

把后端API调用逻辑放到Sapper的Server Routes中,这类代码只会在服务器端运行,不会被打包到客户端:

  1. 在src/routes/api目录下新建get-stripe-pub.js:
import axios from "axios";

export async function get(req, res) {
  try {
    const backend = process.env.IP_BACK;
    const api_key = `${process.env.API_KEY_NAME}=${process.env.API_KEY}`;
    const response = await axios.get(`http://${backend}/stripe/getkeypub/?${api_key}`);
    
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ key: response.data.key }));
  } catch (e) {
    res.writeHead(500, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({ error: 'Failed to fetch Stripe public key' }));
  }
}
  1. 修改组件的preload函数,调用自己的Server Route:
<script context="module">
  export async function preload() {
    try {
      const response = await this.fetch('/api/get-stripe-pub');
      if (!response.ok) throw new Error('Request failed');
      const data = await response.json();
      return { api_key: data.key };
    } catch (e) {
      console.error(e);
      this.error(500, "Oops, unexpected error there");
    }
  }
</script>

<script>
  import Payplace from "./../../components/payplace/Payplace.svelte";
  export let api_key;
</script>

<Payplace {api_key} />

方案2:在calls.js中区分运行环境

如果你想保留calls.js的结构,可以用Sapper内置的process.browser变量判断环境,避免浏览器执行后端逻辑:

import axios from "axios";

export async function getStripePub() {
  // 如果在浏览器环境,直接抛出错误或调用Server Route
  if (process.browser) {
    const response = await fetch('/api/get-stripe-pub');
    if (!response.ok) throw new Error('Request failed');
    return await response.json();
  }
  
  // 服务器端逻辑
  const backend = process.env.IP_BACK;
  const api_key = `${process.env.API_KEY_NAME}=${process.env.API_KEY}`;
  return await axios.get(`http://${backend}/stripe/getkeypub/?${api_key}`);
}

这种方式虽然能解决报错,但会把axios打包进客户端代码,不如Server Route干净。

方案3:用Server-Only模块隔离代码(Sapper v0.28+)

Sapper允许你把服务器专属代码放在src/server目录下,这个目录的模块只会在服务器端加载,不会进入客户端打包:

  1. 新建src/server/stripe-api.js:
import axios from "axios";

export async function getStripePub() {
  const backend = process.env.IP_BACK;
  const api_key = `${process.env.API_KEY_NAME}=${process.env.API_KEY}`;
  return await axios.get(`http://${backend}/stripe/getkeypub/?${api_key}`);
}
  1. 在组件preload中动态导入这个模块:
<script context="module">
  export async function preload() {
    try {
      // 动态导入确保只在服务器端加载该模块
      const { getStripePub } = await import('../server/stripe-api.js');
      const response = await getStripePub();
      return { api_key: response.data.key };
    } catch (e) {
      console.error(e);
      this.error(500, "Oops, unexpected error there");
    }
  }
</script>

内容的提问来源于stack exchange,提问作者Greatzo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 12:37:34