为何Sapper后端代码会在浏览器中执行?context='module'脚本疑问
问题原因分析
你的核心困惑点在于Sapper中context="module"下的preload函数并非只在服务器端执行——Sapper的设计是:preload(不管是否在context="module"中)会在两个时机运行:
- 服务器端首次渲染页面时执行一次,用于生成初始HTML;
- 浏览器端通过路由导航到该页面时再次执行,用于保证前后端数据状态一致。
而你在calls.js中直接使用了Node.js的process全局变量,同时因为组件模块导入了calls.js,Sapper会把这个模块打包进客户端的index.js中。当浏览器执行这段代码时,自然会抛出process is not defined的错误——浏览器环境根本没有process这个变量。
解决方案
下面提供几种逐步优化的方案,从最直观到最符合Sapper最佳实践:
方案1:用Server Routes隔离后端调用(最推荐)
把后端API调用逻辑放到Sapper的Server Routes中,这类代码只会在服务器端运行,不会被打包到客户端:
- 在
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' })); } }
- 修改组件的
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目录下,这个目录的模块只会在服务器端加载,不会进入客户端打包:
- 新建
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}`); }
- 在组件
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
相关产品推荐
相关产品推荐

