数据在节点之间怎么流动:item 与 item 数组

n8n 里有个东西,文档写得很轻,用起来天天绊人:节点之间传的不是一份数据,是一叠。

你从接口拉回 12 条工单,后面每个节点会自动跑 12 次,你写的表达式每次只看见其中一条。搞不清这层,就会撞上最难查的那类问题——流程显示成功,表格里只有一行。

这篇只讲数怎么变。1 条怎么变 3 条,3 条怎么变回 1 条,0 条的时候下游为什么一声不响。看完你应该能在画布上直接数出条数,而不是靠猜。

前置:连过两三个节点,知道表达式框在哪、怎么从左边拖字段。文里有两段 Code,照抄就行,不用会写 JavaScript。

目录

每一层传的都是一叠

官方对数据结构的说法很干脆:节点之间传的所有数据,都是一个数组。数组里每个元素叫一个 item,正文塞在 json 键下面,文件类的东西塞在 binary 下面。所以一个节点输出两条订单,实际长这样:

[
  { "json": { "order_no": "SO-2031", "qty": 2 } },
  { "json": { "order_no": "SO-2032", "qty": 1 } }
]

说"这个节点输出了一条数据"其实不准确。准确的说法是:它输出了一叠,这一叠现在有 1 条。

下面这些都是在自托管的 2.43.7 上跑的。界面是中文,节点里的参数名还是英文,两种混着看,习惯就好。

节点拿到这一叠之后,行为是逐条跑。文档原话是 Nodes process multiple items automatically:给 3 条就跑 3 遍,每遍只处理当前那条。你在编辑字段里写 {{ $json.qty }},这个 $json 指的不是整叠,是正在处理的那条——它是 $input.item.json 的简写。这句是整篇的地基,值得读两遍。

容易混的是"跑 3 遍"和"这个节点执行了 3 次"不是一回事。执行记录里它只出现一次,3 条是这一次里的 3 个 item。我在自己那台实例上加了两列验证过:{{ $itemIndex }} 得到 0、1、2,{{ $runIndex }} 三行全是 0。数执行记录条数去判断处理量,只会数出 1。

条数在链路中间会变

一条流程走下来,条数经常在变。变的节点就那么几个,记住它们,排错能省一半时间。

链路里发生的事 进来 出去
HTTP 拉一页订单 - 20
Split Out 按明细拆开 1 3
编辑字段加一列金额 3 3
Code 汇总成一条 3 1
Filter 过滤掉不合格的 3 0 或 2

这张表我建议贴在显示器边上。哪天数据不对,先照着它把每一段的条数数一遍,通常数到一半就知道问题在哪了。

最右一列出现 0 的时候要格外小心,后面单独说。

拆出来的字段会多套一层

Split Out 是新手最容易踩的地方,因为它有两个参数看着不相干,实际决定了你下游的表达式怎么写。

Include 有三个值:No Other Fields、All Other Fields、Selected Other Fields。Destination Field Name 不在主面板上,藏在下方的 Options 里,默认是空的。

我这台实例上的实测结果,同样一份输入 {order_no, details:[...]}:

Include Destination Field Name 拆完每条长什么样
No Other Fields 留空 {"sku":"MD-VB-ZOE-M","qty":2,"price":39.9} 平铺
All Other Fields 留空 {"order_no":"SO-2031","details":{"sku":...}} 套回原字段名
All Other Fields line {"order_no":"SO-2031","line":{"sku":...}} 套在你起的名字下

只有第一种是平铺的。你想保留订单号,就得选 All Other Fields,那字段一定会多一层——Destination Field Name 留空,它套回 details;填了 line,就套在 line 下面。没有"又要保留别的字段、又要平铺"的选项。

这一步不报错,界面也不提醒。等你到下一个节点顺手写 {{ $json.qty }},拿到的是 undefined,乘出来就是 NaN。我第一次遇到时盯着输出表格看了半天:qty 2、price 39.9 明明都在,表达式就是取不到——因为它们在 line 里面,不在顶层。

一条订单拆成三条:完整走一遍

拿一张有三个明细的订单当例子,链路是:手动触发 → 造一条订单 → 拆出明细 → 算行金额 → 汇总成一条 → 判断是不是大单。六个节点,条数会是 1 → 1 → 3 → 3 → 1 → 1。

第一步,Code 节点造订单。模式用默认的 Run Once for All Items,返回一个只有一条的数组,明细塞在 details 里。

return [{
  json: {
    order_no: 'SO-2031',
    details: [
      { sku: 'MD-VB-ZOE-M', qty: 2, price: 39.9 },
      { sku: 'MD-VB-ZOE-L', qty: 1, price: 45.5 },
      { sku: 'MD-VB-ZOE-XL', qty: 3, price: 52.0 }
    ]
  }
}];

第二步,Split Out。Fields To Split Out 填 details,Include 选 All Other Fields,展开 Options 把 Destination Field Name 填成 line。这一步之后画布上的标记从 1 个项目变成 3 个项目,输入 1 条、输出 3 条,界面上看得清清楚楚。

拆出明细节点:输入 1 个项目,输出 3 个项目,每条的明细在 line 字段下
左边输入只有 1 个项目,右边输出 3 个项目,每条都带 order_no,明细缩在 line 里

第三步,编辑字段加金额。字段名 amount,类型 Number,值写 {{ $json.line.qty * $json.line.price }},把 Include Other Input Fields 打开。写的时候表达式下面会直接给出预览值 79.8,这是当前这条算出来的,不是第一行也不是合计。

算行金额节点:表达式按当前条目求值,三条分别算出 79.8、45.5、156
同一个表达式,三条各自算一次:79.8、45.5、156

第四步,汇总回一条。Code 节点,模式 Run Once for All Items,用 $input.all() 一次拿全量,再 reduce 累加。这一步是 3 条变 1 条。

const rows = $input.all();
const total = rows.reduce((s, r) => s + r.json.amount, 0);
return [{
  json: {
    order_no: rows[0].json.order_no,
    lines: rows.length,
    total: Math.round(total * 100) / 100
  }
}];
汇总成一条节点:输入 3 个项目,输出 1 个项目,合计 281.3
输入 3 个项目,输出 1 个项目。79.8 + 45.5 + 156 = 281.3

第五步,IF 判断 {{ $json.total }} 大于 200。281.3 走 true,false 那条出口是 0 条。画布上六个节点连起来,边上的标记就是下面这样,1、1、3、3、1、1,从左往右读一遍就知道每一段处理了多少条。

n8n 画布上节点之间的「1 个项目」「3 个项目」标记
节点之间的数字是上一条流向下一条的条数,不是累计

顺带说一句,画布上这些标记只在跑完之后出现。想留档就把执行记录存下来,或者截图。

怎么确认它真的按条跑了

四个地方可以看,按省事程度排:

  1. 画布节点之间的「N 个项目」。跑完就有,最直观。
  2. 节点详情右上角的输出面板。写 3 个项目就是 3 条,表格里有 3 行,切到 JSON 能看到外面那层方括号里有 3 个对象。
  3. 在节点里加一列 {{ $itemIndex }}。跑完看到 0、1、2,说明确实是逐条处理,而不是一条被处理了三遍。
  4. Code 里写 console.log($input.all().length),去浏览器控制台看。缺点是每次都要开控制台。

别用"执行记录里有几条"来判断。执行记录记的是 run,一个节点处理 3 条只有一条记录。前面说 $runIndex 三行全是 0,就是这个原因。

0 条不是错误

这条最坑人,因为它连报错都没有。

把上游那个 Code 节点的最后一行改成 return [];,再跑一次。工作流照样结束,状态是成功,但下游取字段的节点根本没执行——画布上它是灰的,连过去的那根线也没有条数标记。

造数据返回空数组后,下游取字段节点没有执行
造数据后面没有「0 个项目」,因为压根没有数据往下走;取字段是灰的

打开那个返回空数组的节点,输出面板会写一句「未返回任何输出数据」,下面还有一行小字:当节点没有输出数据时,n8n 会停止执行工作流。

Code 节点输出为空时提示「未返回任何输出数据」
输出区空白,右侧给出原因,还给了改法

所以"跑成功了"和"数据都处理完了"是两件事。监控只看执行状态的话,一个上游接口偶尔返回空列表,你这边看到的是连续几天的绿色。

要让它继续往下走,节点设置里有个 Always Output Data(始终输出数据),开了之后没数据也吐一个空 item {}。文档专门提醒别在 IF 节点上开,可能死循环。我不建议随手开:它把"没数据"变成一个长得像数据的空对象,下游照样跑,字段全是 null,问题只是往后挪了一站。真要处理空数据,在 IF 上显式判断条数或者判空,比开这个开关干净。

五种报错,一个不报错的坑

下面这些报错原文都是我在 2.43.7 上撞出来的,界面是中文,报错还是英文。

1. 'amount' expects a number but we got 'NaN' [item 0]

字段路径少写了一层。拆完在 line 下面,表达式还在取顶层。[item 0] 说明第一条就挂了,不是因为第三条数据脏。

字段路径写错后,编辑字段节点报 expected a number but we got NaN
输入 3 个项目正常,输出直接报错,表达式那一行的预览值也是 NaN

顺手记一下它给的提示:To fix the error try to change the type for the field "amount" or activate the option "Ignore Type Conversion Errors"。别照着做。把类型改成字符串只是把错误藏起来,等你真正用这个数的时候再炸一次。

2. A 'json' property isn't an object [item 0]

在 Run Once for Each Item 模式里返回了数组。逐条模式每次只该返回一条,返回数组它就认为你给的 item 不合法。

// 逐条模式里这样写会报错
return [{ json: { n: $json.n * 10 } }];

// 这样才对
return { json: { n: $json.n * 10 } };

3. Can't use .all() here [line 1, for item 0]

逐条模式里调 $input.all()。描述写得很清楚:This is only available in 'Run Once for All Items' mode。想要全量就切模式,别在逐条模式里伸手要整叠。

4. items is not defined [line 1]

从旧教程抄来的写法。Code 节点里没有 items 这个变量,逐条模式用 $json,全量模式用 $input.all()。

5. Code doesn't return items properly

返回了字符串或者数字。return "hello"、return 42 都会撞这个,描述是 Please return an array of objects, one for each item you would like to output。顺便说一句,返回单个对象 { json: {...} } 是合法的,n8n 会自动帮你套上数组,这个不用改。

那个不报错的:null

两个分支用 Merge 的 Append 模式接起来,再引用其中一支的字段:{{ $('造两条').item.json.tag }}。我造了 2 条和 3 条,合并后 5 条,结果前 2 条有值,后 3 条是 null,执行状态成功。

原因在文档里:条数不相等、或者节点新建了 item,n8n 就配不上"这条是从哪条来的",.item 只能返回 null。它不会拦你,因为表达式本身没写错。

改法两种:用 .first()、.all()[i] 明确指定第几条,把 Merge 换成 Combine 模式让一条里同时带上两边数据。我倾向后一种——Append 出来的数据本来就没有对应关系,硬取一定错。

安全:一次请求可能变成两百次调用

Webhook 收到一个请求,body 里带 200 条明细,Split Out 之后就是 200 条。后面的节点如果是发短信、调第三方接口、写数据库,那就是 200 次真实动作、200 次扣费,还可能因为并发把对方接口打到限流。

三条我自己的做法:拆之前先加个 Limit,超过预期条数就直接截断或者报错;下游写库带上幂等键(订单号 + SKU 这种),重跑不会插两遍;拆分后的批量任务,能合就合,别一条一次请求。

还有一条不算安全但一样要命:0 条的时候下游不执行,别把"没执行"当成"已经处理完了"。对账类的流程,最后一定要有个地方核对条数,而不是只看工作流是不是绿的。

小结

节点之间传的是数组,节点对数组是逐条跑,$json 永远是当前这条。记住这三句,大半的"数据不对"都能自己排掉。

数不对的时候先数条数:进来几条,出去几条,哪一段变了。比读报错快。

真正难查的不是报错,是 0 条和 null。它们都不报错,都显示成功,都要你主动去看一眼输出面板才发现。

接着往下

上一篇:Workflow、Node、Connection、Execution:四个词讲清 n8n 的世界,讲这四个词各指什么。整个系列的目录在 n8n 中文课程,按阶段排。

参考资料

最后更新:2026 年 11 月 11 日,用的是 n8n 2.43.7。

THE END