
Web框架前端后端【免费下载链接】waku⛩️ The minimal React framework项目地址https://gitcode.com/gh_mirrors/wa/waku点击查看免费下载本文以 Waku 仓库中的ssr-target-bundle端到端测试夹具fixture为主体剖析 SSR 构建时如何依据后端部署目标node | webworker与客户端浏览器环境通过 NPM 包的exports条件导出字段选择正确代码分支并厘清资产内联、多环境产物与 e2e 验证的完整链路。读完本文你将掌握在 Waku 中配置与验证服务端不携带浏览器专属代码、客户端不携带服务端专属代码的分环境打包方案。问题背景为什么 SSR 打包需要环境感知现代 React 应用在 SSR 场景下同一份代码要跑在三种截然不同的运行时里Node.js 服务端执行 RSC 渲染与 HTML 流式输出拥有完整的 Node APIWebWorker 服务端如 Cloudflare Workers 等边缘运行时无fs、无完整 DOM只暴露fetch等 Web API浏览器客户端负责水合hydration与交互绝不能携带仅服务端可用的代码。很多 NPM 包正是通过package.json中的exports字段声明多个条件分支为不同环境提供不同实现。如果打包器bundler选择分支出错轻则包体膨胀重则运行时崩溃——例如把依赖process、Buffer的服务端实现打进浏览器包或把依赖window的浏览器实现打进 Worker 构建。Waku 仓库中的端到端夹具 e2e/fixtures/ssr-target-bundle/README.md 就是为了验证这一机制而设计的它以react-textarea-autosize模块为样本基于后端部署目标node | webworker与客户端浏览器从该模块的export即exports段中选择正确的代码同时确保没有浏览器专属代码被打进服务端包也没有服务端专属代码被打进浏览器包。夹具全景一个最小可验证的 SSR 应用整个夹具是一个标准的最小 Waku 应用目录结构如下e2e/fixtures/ssr-target-bundle/ ├── README.md # 本主题的说明文档 ├── package.json # 依赖与脚本waku / react / react-textarea-autosize ├── tsconfig.json # strict 模式 TypeScript 配置 └── src/ ├── type.d.ts # 资产模块声明jpg / ?url ├── waku.server.tsx # 服务端入口adapter 配置 ├── waku.client.tsx # 客户端入口水合/挂载 └── components/ ├── App.tsx # 根组件SSR 组件 ├── Textarea.tsx # use client 客户端组件 ├── image-not-inlined.jpg # 超过内联阈值的图片资产 ├── json-private-not-inlined.json # 私有 JSON服务端读取 └── json-public-linked-not-inlined.json # 公开 JSON?url 外链其 package.json 暴露了 Waku 的三个标准命令scripts: { dev: waku dev, build: waku build, start: waku start }依赖方面除了react/react-dom/react-server-dom-webpack均为~19.3.0与waku之外唯一的外来依赖就是react-textarea-autosize: ^8.5.9——它正是用来验证条件导出选择机制的样本模块。客户端组件条件导出的第一层验证核心验证点在 src/components/Textarea.tsxuse client; import TextareaAutosize from react-textarea-autosize; export const Textarea () { return ( div TextareaAutosize>import { Textarea } from ./Textarea.js; // ... Textarea /服务端负责输出包含该组件结构的 HTML但其中不携带任何浏览器专属逻辑——这正是No browser only code should be bundled with server only code的体现。服务端入口与请求分发src/waku.server.tsx 使用waku/adapters/default适配器声明三种请求处理分支import adapter from waku/adapters/default; import { Slot_UNSTABLE as Slot } from waku/minimal/client; import App from ./components/App.js; export default adapter({ handleRequest: async (input, { renderRsc, renderHtml }) { if (input.type rsc) { return renderRsc({ App: App name{input.rscPath || Waku} / }); } if (input.type call) { const value await input.fn(...input.args); return renderRsc({}, { value }); } if (input.type http input.pathname /) { return renderHtml( await renderRsc({ App: App nameWaku / }), Slot idApp /, { rscPath: }, ); } }, handleBuild: async () {}, });这里的三类请求与环境选择直接相关rsc请求返回 RSC 渲染结果Server Actions / 数据流call请求执行客户端调用的服务端函数input.fn(...input.args)走renderRsc({}, { value })回传http请求对根路径返回完整 HTML——先用renderRsc渲染出 RSC 流再用Slot idApp /指定水合挂载点。值得注意的是waku/adapters/default的底层实现 packages/waku/src/adapters/default.ts 是一个占位入口构建过程中它会被替换为真实适配器并依据环境变量VERCEL、NETLIFY、CLOUDFLARE/WORKERS_CI分别选择 Vercel、Netlify、CloudflareWorker 生态或 Node 适配器默认回落到 Node——这就是后端部署目标node | webworker在适配器层面的映射。Edge/Worker 部署对应webworker类目标传统 Node 服务器对应node目标。客户端入口 src/waku.client.tsx 则根据globalThis.__WAKU_HYDRATE__决定是hydrateRootSSR 水合还是createRoot纯客户端挂载保证首屏无 JS 也能渲染。资产边界assetsInlineLimit 与内联/外链的取舍夹具的第二大验证点是静态资产的处理边界全部集中在 src/components/App.tsx 的三行导入中import SampleImage from ./image-not-inlined.jpg; // build.assetsInlineLimit - default 4096 Bytes import SampleJsonPrivate from ./json-private-not-inlined.json; // build.assetsInlineLimit - default 4096 Bytes import SampleJsonPublic from ./json-public-linked-not-inlined.json?url; // build.assetsInlineLimit - default 4096 Bytes三种导入形态对应三种截然不同的产物策略导入形态示例产物去向用途图片默认导入image-not-inlined.jpg400×2004.34 KB超过内联阈值默认 4096 字节→ 以image-not-inlined-*哈希名输出到public/assets运行时由img引用大体积二进制资产不塞进 JS 包走 HTTP 加载JSON 默认导入json-private-not-inlined.json超过阈值 →不输出为静态资产而是被打包进服务端模块由服务端代码直接读取见下方getData私有数据内含password: SECRET绝不能暴露到public目录?url后缀导入json-public-linked-not-inlined.json?url作为公开 URL 输出到public/assets运行时被a href引用需要公开访问的资产显式声明为 URLApp.tsx中对两类 JSON 的处理印证了这一设计const getData async () { const data { lengthOfPassword: SampleJsonPrivate.password.length, // 服务端读取私有 JSON type: SampleJsonPrivate.type, }; return data; };服务端组件在渲染期通过getData()读取私有 JSON 中的password.length页面只输出数字结果公开 JSON 则以链接形式暴露。也就是说内联阈值只决定资产是否内联进 JS 包而?url后缀与导入位置共同决定资产是私有数据还是公开文件——私密数据随服务端包走公开资产进public/assets。配套的 src/type.d.ts 为.jpg与*?url提供了模块声明文件内自注FIXME this is a hack for now确保 TypeScript 严格模式下tsconfig.json开启了strict与noUncheckedIndexedAccess这些导入有合法类型declare module *.jpg { const src: string; export default src; } declare module *?url { const src: string; export default src; }Waku 的多环境构建机制从源码看Waku 的构建本质上是 Vite 的多环境multi-environment构建。packages/waku/src/lib/vite-plugins/environments.ts 中定义了三个构建环境client浏览器端包入口为vite-entries/entry.browser.tsx产物输出到dist/publicssrSSR HTML 渲染包入口为vite-entries/entry.ssr.tsx产物输出到dist/server/ssrrscRSC 服务端包含构建期入口入口为vite-entries/entry.server.tsx与entry.build.tsx产物输出到dist/server。各环境的outDir在configEnvironment钩子中按名称分配见 environments.ts并且构建时对第三方依赖一律noExternal: trueenv.command build时意味着react-textarea-autosize这类依赖会被完整打入对应环境的产物——这正是条件导出选择发挥作用的时刻三个环境各自按自己的target解析依赖的exports分支从而让服务端包与浏览器包拿到同一包的不同实现。换言之ssr-target-bundle验证的结论可以落地为工程实践服务端rsc/ssr环境解析依赖的node或 Worker 对应导出分支客户端client环境解析浏览器导出分支二者互不污染即文档所说的No browser only code should be bundled with server only code。e2e 验证矩阵如何证明打包结果正确该夹具配套了端到端测试 e2e/ssr-target-bundle.spec.ts从三个层面验证上述机制确实生效1. 构建产物断言PRD 专用——直接检查dist/public/assets目录test(image exists in folder public/assets, async () { const imagePath path.join(fixtureDir, dist, public, assets); const files readdirSync(imagePath); const imageExists files.some((file) file.startsWith(image-not-inlined-)); expect(imageExists).toBe(true); });同样的模式分别断言json-public-linked-*存在于public/assets而json-private-*不存在——从产物层面证明私有 JSON 未被错误地公开输出。2. 运行时行为断言——在真实浏览器中验证向 textarea 输入三行文本后clientHeight变大证明react-textarea-autosize的浏览器实现自动高度正常工作img的complete true且naturalWidth ! 0证明外链图片资产可被加载私有 JSON 渲染出6SECRET.length公开 JSON 链接的href匹配json-public-linked。3. 无 JS 首屏断言——关闭javaScriptEnabled后仍能渲染出app-name: Waku与textarea: EMPTY证明 SSR 产物完整可用且不依赖任何浏览器专属代码执行。在自己的 Waku 项目中落地如果你也想在自己的 Waku 项目里复现这套环境感知打包可按以下步骤操作搭建最小应用参照夹具的 package.json 与 waku.server.tsx准备waku dev/waku build/waku start三件套脚本引入条件导出依赖在依赖中加入一个带exports多分支的包如react-textarea-autosize并用use client组件消费它验证浏览器交互逻辑只在客户端生效设计资产边界把需要公开的资产用?url显式外链把私密 JSON 作为普通导入交给服务端读取并留意 4096 字节的内联阈值超过阈值的默认资产会落到dist/public/assets验证产物在waku build之后检查dist/public/assets的文件清单确认私有资产未泄露、公开资产已哈希输出跑通 e2e参考 e2e/ssr-target-bundle.spec.ts 的写法分别断言产物目录、运行时交互与无 JS 首屏三个维度。小结ssr-target-bundle夹具用一个小而完整的例子回答了 SSR 工程中的关键问题当 NPM 包为不同运行时提供多个导出分支时打包器如何为 Node / WebWorker / 浏览器各取所需。它同时演示了资产内联阈值、私有与公开资产的产物边界以及如何用 e2e 测试把打包结果正确固化为可回归的断言。理解这条链路是写出一套代码、多环境安全运行的 Waku SSR 应用的基础。赞分享Web框架前端后端【免费下载链接】waku⛩️ The minimal React framework项目地址https://gitcode.com/gh_mirrors/wa/waku点击查看免费下载相关推荐Fabric.js 浏览器入口包 fabricjs/browser环境解耦、SSR 安全与工程化导入指南Fabric.js 浏览器入口包 fabricjs/browser环境解耦、SSR 安全与工程化导入指南 本篇技术指南聚焦 Fabric.js 仓库中的 p前端图形学基于 LiveKit Agents 的 Agent Builder 实战浏览器内原型、一键部署与代码导出基于 LiveKit Agents 的 Agent Builder 实战浏览器内原型、一键部署与代码导出 Agent Builder 是 LiveKit 生态AI Agent人工智能语音AI 应用多模态enzyme 与 Webpack 集成指南浏览器环境测试的打包配置与条件 require 问题解析enzyme 与 Webpack 集成指南浏览器环境测试的打包配置与条件 require 问题解析 enzyme 是 React 的 JavaScript 测测试前端上一篇【亲测免费】 Vue-CTK-Date-Time-Picker 常见问题解决方案下一篇OpenZiti API参考完整的管理接口使用手册创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考