Enzyme ShallowWrapper 的 `.getElement()` 方法:深入解析与实战指南
2026/9/20 15:57:41 网站建设 项目流程

Enzyme ShallowWrapper 的.getElement()方法:深入解析与实战指南

【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme

导读

.getElement()是 Enzyme 中ShallowWrapper(浅渲染包装器)的核心读取方法之一,用于取出当前包装器所包裹的 ReactElement(React 元素)。它弥补了已废弃的getNode()/getNodes()方法的空缺,为断言组件渲染输出、读写 ref、对比元素引用提供了标准化的官方入口。读完本文,你将掌握.getElement()的返回值语义、调用约束、与.getElements().find()的组合用法,并能通过源码理解其底层实现原理。

一、方法签名与返回值语义

在 getElement.md 中,官方文档给出了如下定义:

  • 方法签名.getElement() => ReactElement
  • 返回值ReactElement,即包装器当前包裹的 React 元素。
  • 根组件特例:如果当前包装器正在包裹根组件(即shallow(<MyComponent />)返回的那个包装器),则返回根组件最新一次渲染的输出(the root component's latest render output)。

这里的"最新一次渲染输出"很关键:调用setPropssetState等方法触发重渲染之后,再次调用.getElement()拿到的将是最新一次 render 产生的元素,而不是初始快照。与之相对,ShallowWrapper上还有.getElements() => Array<ReactElement>,它返回包装元素组成的数组——当包装器同时包裹多个节点(例如wrapper.find('span')命中多个元素)时,.getElements()仍然有效,而.getElement()会直接抛错(详见下文"单一节点约束")。

二、官方示例与逐步拆解

原文档提供了如下可运行的示例(getElement.md):

const element = ( <div> <span /> <span /> </div> ); function MyComponent() { return element; } const wrapper = shallow(<MyComponent />); expect(wrapper.getElement()).to.equal(element);

逐行拆解这个用例:

  1. element是一个由 JSX 语法糖创建的普通 ReactElement 常量,其结构为<div>包裹两个<span />
  2. MyComponentrender直接返回该常量(引用同一对象);
  3. shallow(<MyComponent />)创建浅渲染包装器;
  4. wrapper.getElement()返回包装器当前包裹的根元素,即MyComponent最新渲染输出的<div>
  5. expect(...).to.equal(element)断言两者引用相等(严格相等===),这是因为MyComponent直接返回了常量element,所以浅渲染拿到的元素与element是同一个对象。

这个示例揭示了一个重要事实:.getElement()返回的是渲染产物本身的对象引用,而不是序列化字符串或克隆副本。因此它既可以用于.to.equal的引用断言,也可以直接作为 React 元素参与后续操作(例如传给其它渲染函数、进行属性修改后再渲染)。

三、单一节点约束:single()守卫机制

.getElement()并非可以无条件调用。从源码实现看(ShallowWrapper.js):

getElement() { return this.single('getElement', (n) => getAdapter(this[OPTIONS]).nodeToElement(n)); }

它通过this.single(...)执行。singleShallowWrapper内部用于强制"单节点"约束的守卫工具(ShallowWrapper.js):

single(name, fn) { const fnName = typeof name === 'string' ? name : 'unknown'; const callback = typeof fn === 'function' ? fn : name; if (this.length !== 1) { throw new Error(`Method "${fnName}" is meant to be run on 1 node. ${this.length} found instead.`); } return callback.call(this, this.getNodeInternal()); }

从中可以提炼出三条硬性规则:

  • 包装器长度必须为 1this.length不等于 1(例如find('span')同时命中两个<span />,或find没有任何匹配导致长度为 0)时,调用.getElement()会抛出错误:Method "getElement" is meant to be run on 1 node. N found instead.
  • 自动触发更新single内部调用this.getNodeInternal(),而该方法(ShallowWrapper.js)在"当前包装器是根包装器"且长度恰好为 1 时会先执行this.update(),这正是"返回根组件最新渲染输出"语义的落点——它保证了返回的元素永远反映组件当前的最新状态;
  • 类型转换由适配器完成:拿到内部节点后,通过getAdapter(this[OPTIONS]).nodeToElement(n)把适配器内部的节点表示转换为标准的 ReactElement。

四、底层原理:适配器的nodeToElement

.getElement()的返回值形态取决于当前使用的 React 适配器。以enzyme-adapter-react-16为例,其nodeToElement实现位于 ReactSixteenAdapter.js:

nodeToElement(node) { if (!node || typeof node !== 'object') return null; const { type } = node; return React.createElement(unmemoType(type), propsWithKeysAndRef(node)); }

要点:

  • 内部节点被还原为React.createElement(type, props)的标准元素结构;
  • propsWithKeysAndRef会从内部节点中恢复keyref属性——因此即便元素没有显式指定 key,转换后元素的key也会被规范化为null(见下文测试验证);
  • React.memo包装的类型会调用unmemoType解包,确保拿到的type是可用的原始组件/标签类型;
  • 对空值或非对象输入返回null

也就是说,.getElement()的产物是一个结构上与原始 JSX 等价、且携带 key/ref 信息的全新 ReactElement 对象(注意:它是重建的元素,只有组件直接返回同一常量时才与原对象===)。

五、测试用例佐证:ref 与 key 的行为

仓库共享测试套件 getElement.jsx 针对.getElement()覆盖了三个核心行为,可作为最佳实践参考:

1. 返回带 ref 的元素(L17-L41)

class Foo extends React.Component { constructor(props) { super(props); this.setRef = this.setRef.bind(this); this.node = null; } setRef(node) { this.node = node; } render() { return ( <div> <div ref={this.setRef} className="foo" /> </div> ); } } const wrapper = Wrap(<Foo />); const mockNode = { mock: true }; wrapper.find('.foo').getElement().ref(mockNode); expect(wrapper.instance().node).to.equal(mockNode);

这个用例演示了一个高级技巧:.getElement()返回的元素当作普通 ReactElement 手动调用其ref回调,从而在不依赖真实 DOM 挂载的情况下模拟 ref 的触发。注意:该测试在isShallow与全量渲染(mount)两种模式间存在行为差异——浅渲染不会真实调用 ref(测试中isShallow分支断言current仍为null),而mount模式下 ref 已被真实挂载,这正是浅渲染"不渲染子组件、不触发生命周期/ref"的设计使然。

2. 返回带createRef的元素(L43-L66):适用于 React >= 16.3,验证返回元素上的 ref 属性是createRef()创建的 ref 对象。

3. 不给无 key 元素附加"null"key(L69-L105)

const wrapper = Wrap(<Foo />); expect(wrapper.getElement()).to.have.property('key', null);

对于带 ref 但未指定 key 的元素,nodeToElementpropsWithKeysAndRef会将 key 规范化为null而非字符串"null"。需要注意,这一用例在 React 15.0/15.1 上存在已知失败(测试注释FIXME),说明该规范化行为与 React 版本相关。

六、getElement()与相关方法的取舍

.getElements():多节点场景

.find()命中多个节点时,使用.getElements()拿数组:

const one = <span />; const two = <span />; function Test() { return ( <div> {one} {two} </div> ); } const wrapper = shallow(<Test />); expect(wrapper.find('span').getElements()).to.deep.equal([one, two]);

其底层实现(ShallowWrapper.js)为:

getElements() { return this.getNodesInternal().map((n) => getAdapter(this[OPTIONS]).nodeToElement(n)); }

对比可见:.getElement()single守卫(强制单节点、且根包装器时先update()),.getElements()直接映射全部内部节点。二者最终都经由同一个nodeToElement转换,保证元素形态一致。

与已废弃 API 的关系

从源码看,getNode()getNodes()已不再受支持(ShallowWrapper.js),调用会直接抛出:

getNode() { throw new Error('ShallowWrapper::getNode() is no longer supported. Use ShallowWrapper::getElement() instead'); } getNodes() { throw new Error('ShallowWrapper::getNodes() is no longer supported. Use ShallowWrapper::getElements() instead'); }

如果你在旧代码中见到wrapper.node/wrapper.nodes的私有属性访问,也会收到"请改用getElement()"的提示(ShallowWrapper.js 处的privateWarning)。迁移路径非常明确:单节点场景用.getElement(),多节点场景用.getElements()

兄弟 API 参考

  • .getElement()(ReactWrapper 版本):在mount全量渲染模式下具有相同签名与语义,shallowmount的用例可互相迁移(仅把shallow换成mount);
  • .getElements()(ReactWrapper 版本):同样返回Array<ReactElement>
  • 相关读取方法还包括.get()(按下标取节点)、.first().last()等。

七、实战要点总结

  1. 单一性前置检查:调用前确保包装器length === 1,否则捕获single抛出的错误;多节点场景改用.getElements()
  2. 最新渲染语义:根包装器上调用会自动update(),返回的是最新一次 render 的输出;配合setProps/setState后再次调用可断言重渲染产物。
  3. 引用 vs 结构:只有当组件 render 直接返回同一个元素常量时,to.equal的引用断言才成立;否则建议用.getElements()配合to.deep.equal做结构断言。
  4. 元素可复用:返回的 ReactElement 保留了key/refref可手动触发),可用于模拟 ref 回调、二次渲染或属性检查。
  5. 版本注意:key 规范化行为与 React 版本相关(15.0/15.1 有已知问题),跨版本测试需留意。
  6. 废弃 API 迁移getNode()/getNodes()已移除,统一改用.getElement()/.getElements()

参考资料:getElement.md、getElements.md、ShallowWrapper 源码、React 16 适配器、共享测试用例。

【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询