完全指南:用 render() 生成并分析 React 组件 HTML)
enzyme 静态渲染 APIStatic Rendering完全指南用 render() 生成并分析 React 组件 HTML【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme静态渲染Static Rendering是 enzyme 三种渲染模式中最轻量、最贴近最终输出的一种它通过render()函数把 React 组件树一次性渲染成静态 HTML 字符串再用 Cheerio 解析成可查询、可遍历、可断言的 HTML 结构。读完本文你将掌握render(node[, options])的完整签名与选项语义、CheerioWrapper 的常用查询 API以及它与mount、shallow的适用边界还能通过源码理解render的底层调用链适配器字符串渲染器 →ReactDOMServer.renderToStaticMarkup→loadCheerioRoot。什么是静态渲染render是 enzyme 暴露的顶层函数之一其职责是从你的 React 树生成 HTML并分析最终得到的 HTML 结构。与mount完整 DOM 渲染和shallow浅渲染相比render返回的 wrapper 非常相似但有一个关键差异render依赖第三方 HTML 解析与遍历库 Cheerio。enzyme 官方文档明确说明之所以选用 Cheerio是因为它把 HTML 的解析与遍历做得非常好enzyme 没有必要重复造轮子源码注释中也将其表述为 would be recreating the wheel if we didnt use it。文档中约定将 Cheerio 的构造函数称为CheerioWrapper它与 enzyme 自身的ReactWrapper、ShallowWrapper构造函数是类比关系——也就是说你在CheerioWrapper实例上能调用的方法都来自 Cheerio 自身的 API如find、text、html、is、hasClass等。三种渲染模式速览渲染模式入口函数返回的 Wrapper底层机制适用场景浅渲染shallowShallowWrapper仅渲染组件本身不渲染子组件组件单元测试隔离子组件行为完整渲染mountReactWrapper挂载到真实 DOM通常配合 jsdom需要 DOM API 交互、生命周期、HOC 场景静态渲染renderCheerioWrapper渲染为静态 HTML 字符串后用 Cheerio 解析断言最终输出的 HTML 结构、内容、属性render 函数签名与选项render的调用形式与mount、shallow保持一致render(node[, options]) CheerioWrappernodeReactElement要渲染的 React 元素。optionsObject可选options.contextObject可选要传给组件及其后代的 Context。这是render选项中最常用的一项具体用法见下文传入 Context示例。从当前仓库源码看render的实际实现非常简洁完整逻辑集中在 packages/enzyme/src/render.jsexport default function render(node, options {}) { const adapter getAdapter(options); const renderer adapter.createRenderer({ mode: string, ...options }); const html renderer.render(node, options.context); return loadCheerioRoot(html); }整条调用链可以拆解为三步getAdapter(options)根据配置以及全局配置解析出当前 React 版本对应的适配器如 enzyme-adapter-react-16adapter.createRenderer({ mode: string, ...options })请求一个字符串渲染器。在 React 16 适配器中createRenderer会根据mode分发到 mount / shallow / string 三种渲染器见 ReactSixteenAdapter.js#L843-L851render对应EnzymeAdapter.MODES.STRING字符串渲染器内部调用ReactDOMServer.renderToStaticMarkup生成静态 HTML见 ReactSixteenAdapter.js#L821-L838随后render把这段 HTML 交给loadCheerioRoot解析成 Cheerio 根节点返回。值得注意的是字符串渲染器createStringRenderer对suspenseFallback选项会直接抛出TypeError提示该选项不应在字符串渲染模式下使用见 ReactSixteenAdapter.js#L822-L824——这说明render模式只关注一次性产出静态 HTML不涉及 Suspense 降级渲染等运行时行为。底层解析loadCheerioRoot最终返回的 CheerioWrapper 由 packages/enzyme/src/Utils.js#L411-L422 中的loadCheerioRoot构造export function loadCheerioRoot(html) { if (!html) { return cheerio.root(); } if (!isHtml(html)) { // use isDocumentfalse to create fragment return cheerio.load(html, null, false).root(); } return cheerio.load()(html); }其行为分三种情况渲染结果为空 → 直接返回空的 Cheerio 根渲染结果不是完整 HTML 文档如无html/body包裹的片段→ 以isDocumentfalse创建片段fragment解析避免强行补全为文档结构渲染结果是完整 HTML → 以标准文档模式解析。而 enzyme 所依赖的 Cheerio 版本被精确锁定在1.0.0-rc.3见 packages/enzyme/package.json#L44保证解析行为的一致性。典型用法示例以下是render的四个经典断言场景源自官方文档示例并补充了必要的组件定义与逐行说明import React from react; import { render } from enzyme; import PropTypes from prop-types; // 假设组件定义如下 function Foo({ title }) { return ( div span classNamefoo-bar / span classNamefoo-bar / span classNamefoo-bar / h1{title}/h1 /div ); } describe(Foo /, () { it(renders three .foo-bars, () { const wrapper render(Foo /); // render 返回 CheerioWrapper可直接用 Cheerio 的选择器语法查找 expect(wrapper.find(.foo-bar)).to.have.lengthOf(3); }); it(rendered the title, () { const wrapper render(Foo titleunique /); // .text() 提取整个子树中的文本内容 expect(wrapper.text()).to.contain(unique); }); it(renders a div, () { const wrapper render(div classNamemyClass /); // .html() 返回序列化后的 HTML 字符串 expect(wrapper.html()).to.contain(div); }); it(can pass in context, () { function SimpleComponent(props, context) { const { name } context; return div{name}/div; } SimpleComponent.contextTypes { name: PropTypes.string, }; const context { name: foo }; const wrapper render(SimpleComponent /, { context }); expect(wrapper.text()).to.equal(foo); }); });场景一按类名统计元素数量wrapper.find(.foo-bar)使用 CSS 选择器在渲染后的 HTML 中查找节点lengthOf(3)断言匹配个数。这与ReactWrapper/ShallowWrapper的.find()用法一致但查询对象是真实的 HTML 树而非 React 元素树。场景二断言文本内容wrapper.text()返回子树中所有文本节点的字符串拼接结果。对render(Foo titleunique /)而言text()会包含传入 props 渲染出的unique从而验证 props 是否正确地流向了输出 HTML。场景三直接断言 HTML 字符串render(div classNamemyClass /)会把无状态的原生元素也渲染成 HTMLwrapper.html()返回序列化结果可以用字符串包含contain(div)等方式做粗粒度断言。这也说明render并不要求根节点必须是自定义组件——任意 React 元素都可以。场景四通过 options.context 注入 Context这是render区别于shallow的一个重要能力通过第二个参数的context选项可以把上下文对象传给无状态函数组件SFC组件通过contextTypes声明后即可在第二个参数中读取。render(SimpleComponent /, { context })之后wrapper.text()即为foo。从源码层面看这正是 React 16 适配器createStringRenderer中createRenderWrapper的作用当检测到el.type.contextTypes或options.childContextTypes存在时会用createRenderWrapper包一层再交给ReactDOMServer.renderToStaticMarkup见 ReactSixteenAdapter.js#L827-L834。CheerioWrapper 的查询与遍历能力render返回的CheerioWrapper拥有 Cheerio 提供的整套查询 API因此你可以沿用 jQuery 风格的选择器与遍历习惯选择器查找.find(selector)、.filter(selector)、.is(selector)、.hasClass(className)、.closest(selector)等内容提取.text()、.html()、.attr(name)、.val()等遍历.children()、.parent()、.parents()、.first()、.last()、.eq(index)、.each(fn)等。从 enzyme-test-suite/test/shared/methods/render.jsx 中的共享测试用例可以看到.render()的实际断言模式const wrapper Wrap(Bar /); expect(wrapper.render().find(.in-foo)).to.have.lengthOf(1); const rendered wrapper.render(); expect(rendered.is(.in-bar)).to.equal(true); expect(rendered).to.have.lengthOf(1);注意.render()在ReactWrapper和ShallowWrapper上也是可用方法——它会取当前节点的 HTML 并交给同一个loadCheerioRoot构造 CheerioWrapper见 ReactWrapper.js#L654-L657 与 ShallowWrapper.js#L1120。因此wrapper.render()与顶层render(wrapper.node)的返回类型是一致的都可以在测试中途把已渲染的子树降级为纯 HTML 视图来做字符串级断言。何时使用 render与 mount / shallow 的取舍render的核心优势在于零 DOM 依赖 完全静态它不需要 jsdom 或真实浏览器环境——整个过程只发生React 元素 → HTML 字符串 → Cheerio 树的转换执行速度快、环境要求低非常适合在 Node 环境下的快照/结构断言它不执行组件挂载与副作用也不提供setState、setProps、simulate等交互能力这些能力属于mount的ReactWrapper因此适合做纯输出验证由于返回的是真实 HTML 结构.find()、.text()、.html()的断言结果与浏览器最终看到的 DOM 高度一致便于校验 SSR 输出、模板渲染结果等场景。反过来如果需要断言组件实例方法、state、生命周期行为 → 选mount隔离子组件、只测当前组件单元 → 选shallow只想验证渲染出来长什么样、统计标签数量、检查类名与文本 → 选render最合适。小结render为 enzyme 提供了第三条、也是最接近最终产物的断言路径。它的 API 与mount/shallow一脉相承底层却完全依赖 Cheerio 的解析能力与ReactDOMServer.renderToStaticMarkup的静态输出是编写 React 结构断言、SSR 输出校验时的利器。想进一步了解另外两种渲染方式可参阅 完整渲染 APImount 与 浅渲染 APIshallow若需要掌握render在 wrapper 实例上的用法对已渲染子树取 CheerioWrapper可参考ReactWrapper.render()与ShallowWrapper.render()。【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考