资讯动态

Enzyme ShallowWrapper.forEach() 详解:遍历浅渲染节点并链式操作

发布时间:2026/9/20 14:14:56 来源:尧图企业网站定制
Enzyme ShallowWrapper.forEach() 详解遍历浅渲染节点并链式操作【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme导读.forEach(fn)是 EnzymeShallowWrapper提供的集合遍历方法用于对当前包装器中每一个匹配节点执行回调并以该节点的子包装器ShallowWrapper作为回调第一参数、以节点索引作为第二参数。它非常适合在 shallow 渲染后对一组同级节点批量断言、批量修改 props 或批量收集信息同时因为回调内部this指向原始包装器实例还可以在回调中安全地链式调用其他 ShallowWrapper 方法。读完本文你将掌握forEach的完整签名、执行语义、源码实现原理以及它与map、reduce、every、some等兄弟方法的取舍。一、API 签名与参数说明forEach的完整定义见官方文档 docs/api/ShallowWrapper/forEach.md.forEach(fn) Self项目说明参数fnFunction ( ShallowWrapper node, Number index )对集合中每个节点执行的回调。第一个参数是包裹当前节点的ShallowWrapper第二个参数是从 0 开始的节点索引。回调上下文this回调以原始包装器实例作为this上下文执行即调用forEach的那个 wrapper。返回值ShallowWrapper返回自身因此可以继续链式调用。两个容易忽视的语义点回调第一参数不是原始 React 节点而是节点的包装器。这意味着你可以在回调里直接调用node.hasClass()、node.text()、node.prop()等所有 ShallowWrapper 实例方法无需再手动包装。回调以原实例为this上下文。回调内部可以使用this引用原始 wrapper便于在批量遍历时读取父级信息或做汇总。二、最小可运行示例原文档给出的示例forEach.md如下const wrapper shallow(( div div classNamefoo bax / div classNamefoo bar / div classNamefoo baz / /div )); wrapper.find(.foo).forEach((node) { expect(node.hasClass(foo)).to.equal(true); });执行流程拆解shallow(...)对根div做浅渲染得到一个单节点ShallowWrapper.find(.foo)返回一个包含 3 个匹配节点bax、bar、baz的新包装器.forEach((node) {...})依次把每个节点包装成ShallowWrapper传给回调回调内node.hasClass(foo)逐一断言三个子节点都拥有foo类名。因为回调参数是包装器而非 DOM 节点所以这里不能直接使用node.classList之类的 DOM API而应使用hasClass等 wrapper 方法——这正是 Enzyme 测试风格的核心始终通过 ShallowWrapper 的 API 与节点交互。三、源码实现一次遍历、双重包装、原样返回forEach的实现位于 packages/enzyme/src/ShallowWrapper.jsforEach(fn) { this.getNodesInternal().forEach((n, i) fn.call(this, this.wrap(n), i)); return this; }逐行解读this.getNodesInternal()返回当前包装器持有的内部节点数组this[NODES]。值得注意当调用者是根包装器且只包裹一个节点时它还会先调用this.update()触发一次重新渲染确保遍历的是最新节点见 getNodesInternal 实现。这保证了forEach在组件状态/ props 变化后仍然能遍历到最新结构。对每个节点执行原生Array.prototype.forEach回调形式为(n, i) fn.call(this, this.wrap(n), i)this.wrap(n)把内部 React 节点包装成新的ShallowWrapper。wrap的实现ShallowWrapper.js会判断如果传入的已经是ShallowWrapper则直接复用否则new ShallowWrapper(node, root)新建其中root默认沿用当前包装器的根因此子包装器与原 wrapper 共享同一渲染根。fn.call(this, ...)显式将回调的this绑定为原始包装器实例与文档中will be run with a context of the original instance的描述完全对应。i为当前索引从 0 开始。return this返回调用者自身因此可以继续链式调用如wrapper.find(.foo).forEach(...).find(.bar)。从源码结构看forEach与 map 的实现 几乎同构——map只是把forEach的遍历换成了map并返回收集结果的数组map(fn) { return this.getNodesInternal().map((n, i) fn.call(this, this.wrap(n), i)); }区别只在返回值forEach返回原 wrapper 用于链式操作map返回回调返回值组成的数组用于数据收集。reduce、every、some、tap等也是同一套this.wrap(n) 索引 的遍历范式。四、测试用例印证参数形态与索引语义仓库的共享测试套件 packages/enzyme-test-suite/test/shared/methods/forEach.jsx 对该方法做了精确验证该用例同时被 ShallowWrapper 与 ReactWrapper 的 spec 复用const wrapper Wrap(( div div classNamefoo bax / div classNamefoo bar / div classNamefoo baz / /div )); const spy sinon.spy(); wrapper.find(.foo).forEach(spy); expect(spy).to.have.property(callCount, 3); expect(spy.args[0][0]).to.be.instanceOf(Wrapper); expect(spy.args[0][0].hasClass(bax)).to.equal(true); expect(spy.args[0][1]).to.equal(0); // ... index 1 - barindex 2 - baz这段测试从三个维度锁定了forEach的行为契约调用次数3 个匹配节点 ⇒ 回调恰好被调用 3 次callCount 3第一参数类型与内容spy.args[i][0]必须是Wrapper实例即ShallowWrapper且能正确反映对应节点的类名bax/bar/baz证明每个节点都被重新包装并正确关联第二参数为索引spy.args[i][1]依次为 0、1、2确认索引从 0 递增。可见forEach的包装节点 索引双参数约定是经过测试保障的公开契约编写回调时可直接依赖。五、实战模式断言、收集与链式组合1. 批量断言最典型的用途是对一组匹配节点做统一断言即原文档示例wrapper.find(.foo).forEach((node) expect(node.hasClass(foo)).to.equal(true))。由于回调参数是包装器断言时可以自由组合hasClass、text、prop、type等 ShallowWrapper API。2. 批量修改 props / 触发交互借助node.simulate可以在遍历中对每个节点触发事件wrapper.find(button).forEach((node) { node.simulate(click); });配合this上下文还可以在回调中访问原始 wrapper 做汇总校验let clicked 0; wrapper.find(button).forEach(function (node) { node.simulate(click); clicked this.find(button).length; // this 指向原始 wrapper });3. 链式组合因为forEach返回自身可在同一链上继续筛选或读取wrapper.find(.item) .forEach((node) expect(node.hasClass(enabled)).to.equal(true)) .hostNodes(); // 继续链式处理4. 与兄弟方法的选型需求推荐方法返回值参考文档遍历副作用 / 批量断言forEach原 wrapper可链式forEach.md把每个节点映射为数据map返回值数组map.md归约为单个值reduce/reduceRight任意归约结果reduce.md是否存在满足条件的节点someBooleansome.md是否全部满足条件everyBooleanevery.md查看节点而不影响链式结果tap原 wrappertap.md选型准则很简单要结果数据用map/reduce/some/every要执行副作用断言、触发事件且保持链式不断就用forEach。六、使用注意事项仅作用于当前包装器内的节点forEach遍历的是当前 wrapper 持有的节点集合不会递归深入子孙节点如需按选择器收集后再遍历先.find(selector)再.forEach(...)。回调中的this是原 wrapper如果使用箭头函数this遵循词法作用域只有普通函数声明才能拿到原始实例上下文两种写法不要混用以免产生误解。不要用forEach做数据收集它不返回任何数据收集结果请改用map二者实现同源但语义不同。空集合安全当前 wrapper 没有节点时原生forEach不会执行回调方法安全返回自身无需额外判空。只读语义forEach本身不修改包装器内容遍历期间对组件结构的修改应通过setProps/setState等显式方法并注意getNodesInternal的 update 行为。七、小结forEach是 Enzyme 集合型 API 中最朴素也最常用的遍历原语双参数子包装器 索引、以原始实例为上下文、返回自身支持链式。其实现仅需三行ShallowWrapper.js却与map、reduce、every、some共同构成了 ShallowWrapper 集合操作的一致范式。配合共享测试套件forEach.jsx锁定的行为契约你可以放心地在批量断言与交互触发场景中使用它并与 map、reduce 等兄弟方法按需组合写出简洁、可维护的 React 浅渲染测试。【免费下载链接】enzymeJavaScript Testing utilities for React项目地址: https://gitcode.com/gh_mirrors/en/enzyme创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价