Server Components取数函数在Client调用的合规性与环境变量安全
Next.js官方文档建议在React Server Components(RSC)中进行数据获取:
尽管可以在 Client Components 中获取数据,但除非有特定的客户端取数需求,否则我们推荐在 Server Components 中执行数据获取操作。将数据获取移至服务器端能带来更优的性能与用户体验。
以下是示例代码:
React Server Component(app/page.js)
const client = new ApolloClient({ link: createHttpLink({ uri: `https://graphql.contentful.com/content/v1/spaces/${process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID}`, credentials: "include", headers: { "Content-Type": "application/json", Authorization: `Bearer ${process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN}`, }, }), cache: new InMemoryCache(), }); export const fetch = () => { const response = client.query('some query'); // Executes a data fetch, via client object return response.data; } export default const Page = () => { <div>Test</div> }
React Client Component(app/RCC.js)
'use client' import { fetch } from './page'; export default const RCC = () => { const fetchedResponse = fetch(); return <div>{fetchedResponse}</div> }
问题1:上述代码架构是否违背了Next.js推荐在React Server Components中进行数据获取的原则?
完全违背。Next.js推荐的核心是在服务器端的RSC内部完成数据获取,将最终拿到的数据传递给Client Component渲染,而非把数据获取逻辑暴露给客户端执行。示例中fetch函数虽定义在RSC里,但实际执行是在Client Component中,数据获取操作会跑到客户端,完全没利用到RSC服务器端取数的性能优势(比如减少客户端请求、避免跨域、提前渲染内容等)。
问题2:此处的机密环境变量是否会暴露给浏览器?
会暴露。首先,带NEXT_PUBLIC_前缀的环境变量本身就是设计用来暴露给客户端的;更关键的是,当Client Component导入RSC中的fetch函数时,该函数依赖的Apollo Client实例会被序列化并发送到客户端,其中包含的环境变量值会直接出现在客户端代码中。哪怕用的是不带NEXT_PUBLIC_前缀的机密变量,只要被包含在客户端会导入的模块里,同样会被打包到客户端代码中导致泄露。
问题3:在Server Components中声明但在Client Components中调用的数据获取函数,是否会导致客户端能够访问环境变量?
会。当Client Component导入RSC中的函数时,整个函数的定义(包括它依赖的所有变量、实例)都会被序列化后发送到客户端执行。客户端会拿到完整的函数代码和其中用到的环境变量值,相当于把服务器端的取数逻辑直接搬到了客户端,不仅环境变量会暴露,还完全失去了RSC服务器端取数的意义。
内容的提问来源于stack exchange,提问作者Magnus

