拓冰建站拓冰建站
首页 / 资讯中心 / 正文

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

Enzyme ShallowWrapper 的.getElement()方法深入解析与实战指南【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme导读.getElement()是 Enzyme 中ShallowWrapper浅渲染包装器的核心读取方法之一用于取出当前包装器所包裹的 ReactElementReact 元素。它弥补了已废弃的getNode()/getNodes()方法的空缺为断言组件渲染输出、读写 ref、对比元素引用提供了标准化的官方入口。读完本文你将掌握.getElement()的返回值语义、调用约束、与.getElements()及.find()的组合用法并能通过源码理解其底层实现原理。一、方法签名与返回值语义在 getElement.md 中官方文档给出了如下定义方法签名.getElement() ReactElement返回值ReactElement即包装器当前包裹的 React 元素。根组件特例如果当前包装器正在包裹根组件即shallow(MyComponent /)返回的那个包装器则返回根组件最新一次渲染的输出the root components latest render output。这里的最新一次渲染输出很关键调用setProps、setState等方法触发重渲染之后再次调用.getElement()拿到的将是最新一次 render 产生的元素而不是初始快照。与之相对ShallowWrapper上还有.getElements() ArrayReactElement它返回包装元素组成的数组——当包装器同时包裹多个节点例如wrapper.find(span)命中多个元素时.getElements()仍然有效而.getElement()会直接抛错详见下文单一节点约束。二、官方示例与逐步拆解原文档提供了如下可运行的示例getElement.mdconst element ( div span / span / /div ); function MyComponent() { return element; } const wrapper shallow(MyComponent /); expect(wrapper.getElement()).to.equal(element);逐行拆解这个用例element是一个由 JSX 语法糖创建的普通 ReactElement 常量其结构为div包裹两个span /MyComponent的render直接返回该常量引用同一对象shallow(MyComponent /)创建浅渲染包装器wrapper.getElement()返回包装器当前包裹的根元素即MyComponent最新渲染输出的divexpect(...).to.equal(element)断言两者引用相等严格相等这是因为MyComponent直接返回了常量element所以浅渲染拿到的元素与element是同一个对象。这个示例揭示了一个重要事实.getElement()返回的是渲染产物本身的对象引用而不是序列化字符串或克隆副本。因此它既可以用于.to.equal的引用断言也可以直接作为 React 元素参与后续操作例如传给其它渲染函数、进行属性修改后再渲染。三、单一节点约束single()守卫机制.getElement()并非可以无条件调用。从源码实现看ShallowWrapper.jsgetElement() { return this.single(getElement, (n) getAdapter(this[OPTIONS]).nodeToElement(n)); }它通过this.single(...)执行。single是ShallowWrapper内部用于强制单节点约束的守卫工具ShallowWrapper.jssingle(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.jsnodeToElement(node) { if (!node || typeof node ! object) return null; const { type } node; return React.createElement(unmemoType(type), propsWithKeysAndRef(node)); }要点内部节点被还原为React.createElement(type, props)的标准元素结构propsWithKeysAndRef会从内部节点中恢复key与ref属性——因此即便元素没有显式指定 key转换后元素的key也会被规范化为null见下文测试验证对React.memo包装的类型会调用unmemoType解包确保拿到的type是可用的原始组件/标签类型对空值或非对象输入返回null。也就是说.getElement()的产物是一个结构上与原始 JSX 等价、且携带 key/ref 信息的全新 ReactElement 对象注意它是重建的元素只有组件直接返回同一常量时才与原对象。五、测试用例佐证ref 与 key 的行为仓库共享测试套件 getElement.jsx 针对.getElement()覆盖了三个核心行为可作为最佳实践参考1. 返回带 ref 的元素L17-L41class 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} classNamefoo / /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 元素附加nullkeyL69-L105const wrapper Wrap(Foo /); expect(wrapper.getElement()).to.have.property(key, null);对于带 ref 但未指定 key 的元素nodeToElement的propsWithKeysAndRef会将 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全量渲染模式下具有相同签名与语义shallow与mount的用例可互相迁移仅把shallow换成mount.getElements()ReactWrapper 版本同样返回ArrayReactElement相关读取方法还包括.get()按下标取节点、.first()、.last()等。七、实战要点总结单一性前置检查调用前确保包装器length 1否则捕获single抛出的错误多节点场景改用.getElements()。最新渲染语义根包装器上调用会自动update()返回的是最新一次 render 的输出配合setProps/setState后再次调用可断言重渲染产物。引用 vs 结构只有当组件 render 直接返回同一个元素常量时to.equal的引用断言才成立否则建议用.getElements()配合to.deep.equal做结构断言。元素可复用返回的 ReactElement 保留了key/refref可手动触发可用于模拟 ref 回调、二次渲染或属性检查。版本注意key 规范化行为与 React 版本相关15.0/15.1 有已知问题跨版本测试需留意。废弃 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),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门