ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Vue el-table多选实战:彻底解决分页、搜索下选中行丢失问题

Vue el-table多选实战:彻底解决分页、搜索下选中行丢失问题 1. 多选表格并不是加一列勾选框这么简单先拆业务场景在 Vue 项目里el-table 的多选功能几乎可以说是后台管理系统的标配了。不过每当我看到有人只花半分钟加一列typeselection、再监听一个selection-change就宣布多选搞定的时候我总会替他捏把汗——不是说他写错了而是这个最简实现只覆盖了全部需求场景里最简单的那一小撮。先说清楚获取选中行这件事真正的难点从来不在怎么取到数据而在于你怎么把用户勾了哪些行这一个状态在分页、搜索、弹窗复用、树形结构这些复杂场景下稳定、准确、实时地同步到业务逻辑里。同样的一个功能在静态表格里是三行代码的事到了服务端分页的表格里就可能变成一场选中状态保卫战。这篇内容不是什么 Element 文档翻译而是我从实际项目里折腾出来的经验汇总。我踩过的坑、试过的方案、最后沉淀下来的写法都会写出来。适合这几类读者刚接触 Vue 和 el-table 的新手想在项目里快速落地多选功能以及已经写了多选但总感觉哪里怪怪的、一到 QA 阶段就被翻页丢选中、回显错乱等问题折磨的中级开发者。无论你是用 Element UIVue2还是 Element PlusVue3这篇文章覆盖的场景都适用。在展开代码之前可以先对照一下自己的业务属于哪种情况表格是纯前端渲染的静态数据用户勾选几行后点批量删除或批量导出直接把选中行拿去用。表格走服务端分页每页加载 20 条用户翻页勾选最后要把所有页里勾选过的行汇总提交到后端。表格支持关键词搜索搜索条件变化后数据刷新但用户希望之前勾选的行在新结果里仍然保留选中状态。弹窗里放一个多选表格用户选完点确定关闭下次打开还要回显上一次的选择。表格是树形结构父节点勾选时子节点联动勾选但业务上只想要叶子节点或者只想要用户手动勾选的行。这五种场景代码的写法差异非常大。但好消息是它们的底层都围绕同一套核心 APIel-table 的选择列、selection-change 事件、以及 row-key reserve-selection 这套组合拳。把基础机制弄明白上面的复杂场景就是在这套机制上做增量。2. 基础实现selection 列 selection-change 事件把选中行拿全2.1 typeselection 这一列到底做了什么在 el-table 里开启多选第一步永远是加一个特殊类型的选择列template el-table :datatableData selection-changehandleSelectionChange el-table-column typeselection width55 / el-table-column propname label姓名 / el-table-column propage label年龄 / /el-table /templatetypeselection是由 Element 组件内置实现的一种列类型它会一次性渲染出两套交互控件表头位置一个全选框以及每一行行首的勾选框。表头的全选框控制的是当前数据源里所有行的全选/反选。注意这八个字当前数据源。这句话是理解后面所有坑的钥匙——全选的范围是现在表格 data 数组里的所有行而不是所有页的所有行更不是历史选中的所有行。这个选择列有几个值得知道的配置项很多人只用了默认值但其实它们各自有用武之地属性作用典型使用场景width列宽想压缩宽度时调到 40~50空间紧张的时候非常管用fixed固定列表格横向滚动时让勾选列固定在最左侧不跟着滚走reserve-selection是否保留选中记录数据更新后之前勾选过的行仍保持/恢复选中状态必须配合 row-keyselectable函数返回 Boolean控制哪些行能勾选、哪些行禁用比如树形表格里禁勾父节点2.2 selection-change 的触发机制与参数含义selection-change是获取选中行的核心事件。它会在两种情况下被触发一是用户手动勾选或取消某一行时二是表头全选框状态变化时。事件回调接收一个参数数组里面放着当前所有处于选中状态的行对象。const handleSelectionChange (val) { // val 就是当前选中的行数组 console.log(val) }这里有一个新手最容易搞错的点val 不是本次操作新变化的那一行而是当前全量选中的所有行。我给你拆个例子——表格里已经有 3 行被选中用户又勾了第 4 行事件触发后传进来的 val 是 4 行的数组而不是只包含第 4 行也不是包含第 3 行怎么变的信息。所以不要试图在事件回调里做把 val 的最后一项当作新增行这种操作思路一开始就错了。还有一点需要注意toggleRowSelection这类实例方法如果被主动调用一样会触发selection-change。这意味着如果你在页面初始化时调用方法设置默认选中回调里的业务逻辑比如把数据推送给父组件或发请求可能比你预期的时间更早执行。如果回调里有副作用操作建议加一个执行时机判断。2.3 辅助方案用 getSelectionRows 主动拉取选中行除了被动监听事件Element 的表格实例上还提供了一个主动获取选中行的方法// Vue2 写法 const selectedRows this.$refs.multipleTable.getSelectionRows()// Vue3 Element Plus 写法 const multipleTableRef ref(null) const selectedRows multipleTableRef.value.getSelectionRows()很多场景下这个方法比依赖事件参数更靠谱。尤其在某些 Element UI 的历史版本中selection-change回调里的 val 会和界面状态有短暂不同步的问题用getSelectionRows主动拉取可以绕过这个隐患。不过我通常在项目里是两条腿走路事件回调负责把选中状态同步进业务里的独立数据源后面会详细说为什么需要这么做主动拉取方法则用于提交瞬间获取最新快照两者职责清晰互不干扰。3. 分页保留选中与跨页回显row-key reserve-selection 的组合玩法3.1 为什么翻页之后选中状态全没了先把最典型的翻页丢选中问题完整复现一遍。假设表格数据来自服务端分页每页 10 条。用户在第一页勾了 2 行翻到第二页又勾了 1 行此时翻回第一页——之前选中的 2 行勾选框已经空了selection-change回调里再也拿不到那两行数据。想要理解这个现象就得知道 el-table 内部是怎么维护勾选状态的。默认情况下表格用行对象在 data 数组里的索引位置来标识一行。勾选状态和某一行数据的对应关系靠的是这个行对象当前是否还在表格数据数组里。当你把第一页数据替换成第二页数据第一页的行对象彻底从表格的数据源中消失对应的勾选状态自然也就被清掉了。这就像你在打印好的名单上给几个人画了对勾然后换了一沓新名单之前的对勾当然不会跟着过来。解决办法很简单给表格指定一个稳定、唯一的行标识让组件能认出这一行就是刚才那一行而不是靠数组位置认人。3.2 row-key 的设置要求与常见错误row-key是传给 el-table 的属性用来指定每一行的唯一标识。它可以是字符串表示行数据里的某个字段名也可以是函数接收行数据后返回一个唯一值。el-table :datatableData row-keyid selection-changehandleSelectionChangeel-table :datatableData :row-keyrow row.id selection-changehandleSelectionChange这里的隐性要求很关键row-key 指向的字段必须在全部数据范围内唯一且稳定。比如说用户表用 id订单表用 order_no库存表用 sku_id这种业务主键是最理想的选择。我在项目里见过一个典型错误拿后端返回的序号列当 row-key。这种序号往往每页都从 1 开始重新编号第一页有 id1第二页也有 id1组件根本分不清这俩是不是同一行。结果就是reserve-selection 开了row-key 也设了翻页后选中状态依然全乱。这时候排查方向不能盯着代码流程而要先检查 row-key 的取值是否真的全局唯一。还有一个小细节容易被忽略row-key 的值类型要保持一致。如果同一份数据里 id 有时候是数字1001有时候是字符串1001在部分版本里组件会把它们当成两个不同的行来处理回显时就会出现勾选状态对不上号的问题。3.3 reserve-selection跨页保留选中的真正开关有了 row-key 还不够。当你把第一页数据替换成第二页数据时表格默认还是会清空所有勾选状态。想让组件把能认出来的那些行的选中记录保留住还需要在选择列上额外加上reserve-selection。el-table-column typeselection reserve-selection width55 /reserve-selection的作用机制是这样的当某一行不在当前数据源里时组件不会删除它对应的选中记录而是把它暂时存在内部的记忆层里等这一行再次出现在数据源中时自动恢复它的勾选状态。注意这个属性必须和 row-key 同时使用单独开任何一个都不生效。我看过太多帖子只提 reserve-selection 不提 row-key 的前提条件结果读者照着配了一通发现没用。拆开来说row-key 是地基作用是让组件能认出每一行reserve-selection 是在地基上盖的楼作用是保留选中记录。地基不牢楼自然盖不起来。3.4 toggleRowSelection 手动设置默认选中除了翻页保留另一个高频需求是回显。比如弹窗形式的多选器第二次打开时需要把之前选中的行自动勾上。这时需要拿到表格实例调用toggleRowSelection方法逐行设置选中状态// Vue2 写法 this.$nextTick(() { this.editingRows.forEach(row { this.$refs.multipleTable.toggleRowSelection(row, true) }) }) // Vue3 Element Plus 写法 const multipleTableRef ref(null) const editingRows [...] nextTick(() { editingRows.forEach(row { multipleTableRef.value.toggleRowSelection(row, true) }) })toggleRowSelection接收两个参数第一个是行对象第二个是可选参数——传入true表示选中该行传入false表示取消选中不传则自动切换当前状态。用这个方法时有几个坑我一个个说。第一个坑传入的行对象必须和表格数据里的行对象保持同一个引用或者至少有完全一致的 row-key。如果你从接口拿回来的回显数据是重新组装的新对象字段值一样但引用不同组件可能认不出来调用后没有任何效果。我的建议是回显前先基于 row-key 从当前表格数据里find出对应的行对象再把找到的对象传给 toggleRowSelection。const rowsToToggle editingIds .map(id tableData.value.find(item item.id id)) .filter(Boolean) rowsToToggle.forEach(row { multipleTableRef.value.toggleRowSelection(row, true) })第二个坑如果表格数据还没渲染完成就调用toggleRowSelection方法会静默失败或者直接报错。我一般会放在nextTick里执行更稳妥的方式是 watch 表格的 data等数据真正到位后再触发回显操作。尤其是异步请求场景接口返回数据 → 赋值给 tableData → 表格渲染行 → 此时调用 toggleRowSelection这整条链路里的每一步都有时序问题不能想当然。4. 服务端分页下多选的正确保存策略选中的行不能只放在 val 里4.1 selection-change 在服务端分页下的丢行现象回到我开头说的服务端分页场景。假设你按基础姿势写了代码只依赖 selection-change 的 val 来维护选中行很快就会遇到一个诡异的现象用户在第 1 页勾了 2 行翻到第 2 页又勾了 1 行。此时你在回调里打印 val发现里面只有第 2 页勾的 1 行或者干脆是空数组。之前在第 1 页勾的那 2 行仿佛人间蒸发了。原因在第 2 节已经埋下伏笔val 是当前表格数据源里处于选中状态的行。第 1 页的数据已经被移出数据源就算 reserve-selection 保留了它们的选中记录这些行也不会出现在 val 里——因为 val 只反映当前页上的选中状态它不是一个跨页累计的容器。很多人的第一反应是加 row-key reserve-selection以为这样 val 就能跨页累加。试完之后发现还是不行又回来搜文章。我想在这里把结论说死跨页汇总选中行这个需求靠 val 一个变量是永远解决不了的必须自己另外维护一份全局选中集合。4.2 维护一份独立的选中 Map每次回调做增量合并我在项目里长期使用的方案是维护一个 Map或者普通对象用 row-key 作为键行对象作为值专门用来存放用户在本会话里选中的所有行。const selectedMap ref(new Map()) const handleSelectionChange (rows) { // 当前页表格数据里所有行的 key 集合 const currentPageKeys new Set(tableData.value.map(row row.id)) // 本次事件传进来即当前页最新选中状态的所有行 key 集合 const latestSelectedKeys new Set(rows.map(row row.id)) // 第一步从全局 Map 里删除在当前页中已被取消选中的行 selectedMap.value.forEach((value, key) { if (currentPageKeys.has(key) !latestSelectedKeys.has(key)) { selectedMap.value.delete(key) } }) // 第二步把本次选中状态里的行全部合并进全局 Map天然按 key 去重 rows.forEach(row { selectedMap.value.set(row.id, row) }) }这段逻辑的核心思想是每次 selection-change 触发时只需要增量同步当前这一页的最新勾选状态其他页的选中记录原封不动地保留在 Map 里。分解一下两个步骤第一步解决取消选中的同步。如果某一行在当前页数据里存在但它这次没有出现在 rows 里说明用户在这一页把它的勾选取消了所以要从全局 Map 里删掉它。加了currentPageKeys.has(key)这个判断是为了避免误删其他页的选中记录——其他页的行根本不在当前页数据里不可能被用户在当前页取消。第二步解决新增选中的同步。当前页勾选的行直接set进 Map如果之前已经存在相同 key则覆盖更新天然完成去重。等用户点提交时直接把 Map 的 value 数组交给后端const submitRows [...selectedMap.value.values()]这里有一个取舍可以提一下如果后端只关心主键 ID你完全可以只存一个 ID 数组省内存也省响应式开销。但如果你需要在页面上展示已选 X 项或详细的已选列表那就必须存整个行对象因为后续展示要用到行里的其他字段。4.3 给后端提交数据的格式与选型建议提交给后端时常见的格式有三种我列出来供你根据业务选型格式示例适用场景只提交主键 ID 数组[1001, 1002, 1003]批量删除、批量启用/停用后端只关心删哪些/改哪些提交完整行对象数组[{id, name, age}, ...]后端需要看到行的完整信息做二次校验或落库提交 ID 若干关键字段[{id, code, name}, ...]折中方案后端不用再回查数据前端也不用传全量字段不管用哪种格式去重是底线。如果你用的是 Map 方案key 天然唯一不需要额外处理但如果你图省事用了数组 push 的方式每次新事件 push 之前务必检查 includes。去重逻辑如果想写成一行可以这样const uniqueRows [...new Map(rows.map(row [row.id, row])).values()]5. 这几个坑我实测踩过从发现到修复的完整链路5.1 坑一界面明明勾选了selection-change 里打印 val 却是空数组这是我以前排查时间最长的一个问题。当时的现象很诡异界面上勾选了好几行事件也触发了回调里的日志确实打印了但 val 是空数组。排查过程我完整复盘一遍这条路对你们应该也有参考价值先在 selection-change 回调里打印日志确认真实触发频率和参数。结果触发了参数为空。紧接着打印 tableData 内容和界面对比。结果发现表格实际渲染的数据并不是我以为的那个数组。进一步追查发现我把 tableData 绑定成了一个 computed 属性而这个 computed 每次都会生成一个新数组。表格内部维护的选中状态指向的是旧数组里的行对象界面换了一批新对象后组件内部缓存的对不上号勾选状态自然无法正确上报。搞清楚根因后修复方案是把表格的 data 改绑为一个引用稳定的数组不再用每次计算都产生新引用的 computed。如果业务上确实需要派生数据就保证 computed 返回的数组在依赖不变时不重新生成。这里再补一条建议如果你使用的是 Element UI 老版本遇到 val 与勾选状态不一致的怪问题时直接改用getSelectionRows()主动拉取能绕开不少内部状态错乱的问题。5.2 坑二树形表格下父子联动选中带来一堆多余行树形表格的多选有一个默认行为选中父行子行自动全部选中取消父行子行全部取消。这个联动逻辑本身没什么问题但真实的业务场景往往不想要这种连带关系。比如我做过一个权限分配的功能表格按部门树展示用户需要勾选的是部门下某个具体的人而不是整个部门。但默认联动导致一勾部门下面所有人全被选中selection-change 返回的 val 里混入了一堆被动选中的行提交给后端的数据量完全失控。解决方案有两个方向取决于产品需求方向一过滤掉联动产生的行。只在回调里保留叶子节点没有子节点的行const handleSelectionChange (rows) { const leafRows rows.filter(row !(row.children row.children.length 0)) // 后续逻辑只用 leafRows }方向二从源头禁用父节点的勾选。在 selection 列上加selectable函数让父行的勾选框变成禁用状态el-table-column typeselection :selectablerow !(row.children row.children.length 0) width55 /这样设置后用户只能手动勾选叶子节点。但有一个需要和产品确认的细节点击表头全选框时组件会选中所有满足 selectable 的行——也就是所有叶子节点父行依然不会被勾上。不同业务对全选是否包含父行的预期可能不同上线前一定要确认清楚否则测试阶段就会被打回。5.3 坑三弹窗二次打开上次勾选状态还残留在界面上弹窗里放多选表格关闭弹窗后再次打开之前勾选的行还保持着勾选状态。这在交互上通常是不符合预期的——用户第二次打开应该面对一个干干净净的全新选择。背后的原因很简单弹窗关了但表格组件实例和数据都还在勾选状态自然还在。最稳妥的做法是关闭弹窗时清空const handleDialogClose () { multipleTableRef.value multipleTableRef.value.clearSelection() }clearSelection()是表格实例提供的清空选中方法调用后不仅能清掉界面上的勾选还会触发一次 selection-change 事件让你维护的独立选中集合同步清空。这里有一个容易被忽略的连带问题如果你的全局选中集合没有跟着事件清空就会出现界面已经清空了但提交时数据还在的 bug。所以切记clearSelection 之后你自定义的选中 Map 也要同步 reset不要只清界面不清数据。5.4 坑四几万行数据下 selection-change 疯狂触发页面卡成 PPT大数据量表格还有一个性能隐患每次勾选都会触发 selection-change如果你在回调里做了重操作——比如深拷贝选中数组、遍历全量数据做匹配、或者同步触发大列表的重新渲染——页面的卡顿会非常明显。我的优化经验是按这三个步骤来的回调里只做轻量同步。给业务数据源的赋值、去重这些操作可以放在回调里但重逻辑比如重新统计、重新渲染必须延后。用防抖包裹重逻辑。用 lodash 的 debounce 或者自己手写一个把提交统计类的操作合并到最后一次触发时执行。// 简单防抖 let timer null const debouncedUpdate (fn, delay 100) { clearTimeout(timer) timer setTimeout(fn, delay) } const handleSelectionChange (rows) { // 轻量同步 selectedMap.value.set(/* ... */) // 重量操作延后 debouncedUpdate(() updateSelectedSummary()) }优先用 Set/Map 存选中标识别在数组上用 includes/splice。几千条数据时数组方法还能忍几万条时includes一次就是遍历整个数组O(n) 的操作叠加高频触发性能直接崩。Map 的查找、删除都是 O(1)差距非常大。5.5 坑五Element UI 迁移到 Element Plus 的 API 差异与兼容问题如果你的项目正在从 Vue2 Element UI 迁移到 Vue3 Element Plus多选这块的 API 基本是平迁的但有几个差异点值得提前知道功能点Element UI (Vue2)Element Plus (Vue3)表格组件名el-tableel-table保持一致选择列类型typeselectiontypeselection保持一致selection-change 事件有老版本有内部引用问题有相对稳定getSelectionRows有有clearSelection有有toggleRowSelection有有TypeScript 支持较弱完整方法名和参数都有类型提示两边的方法名和属性名几乎一模一样迁移成本集中在 Vue3 的响应式 api 和 ref 获取实例的方式上。如果老项目里曾经出现过 val 和界面不同步的问题升级到 Element Plus 之后大概率会自愈因为新版内部的对行对象引用的缓存逻辑更健壮了。6. 从实际项目里抽出的两个可直接复制模板6.1 模板 A纯前端分页 多选提交 ID 数组这个模板适合数据量不大、纯前端分页的简单业务。template div el-table refmultipleTableRef :datatableData row-keyid selection-changehandleSelectionChange el-table-column typeselection reserve-selection width55 / el-table-column propname label姓名 / el-table-column propage label年龄 / /el-table el-pagination :current-pagepage :page-sizepageSize :totalallData.length layoutprev, pager, next current-changehandlePageChange / el-button typeprimary clickhandleSubmit提交/el-button /div /template script setup import { ref, computed } from vue const allData ref([ { id: 1, name: 张三, age: 28 }, { id: 2, name: 李四, age: 30 }, { id: 3, name: 王五, age: 22 }, { id: 4, name: 赵六, age: 26 }, { id: 5, name: 孙七, age: 35 } ]) const page ref(1) const pageSize 2 const tableData computed(() { const start (page.value - 1) * pageSize return allData.value.slice(start, start pageSize) }) const multipleTableRef ref(null) const selectedRows ref([]) const handlePageChange (current) { page.value current } const handleSelectionChange (rows) { // 纯前端分页 row-key reserve-selection 场景下 // val 里就是所有分页中选中的行直接赋值即可 selectedRows.value rows } const handleSubmit () { const ids selectedRows.value.map(row row.id) console.log(提交的 ID 列表, ids) } /script这个模板的关键在于row-keyid加reserve-selection必须同时存在缺一个翻页后 selectedRows 都会丢数据。纯前端分页的场景下组件内部已经帮我们把所有页选中的行汇总好了拿 val 直接用是可行的。6.2 模板 B服务端分页 关键词搜索 跨页保持选中再上一个复杂场景的模板适合真实后台管理系统。template div el-input v-modelpage.keyword placeholder搜索姓名 stylewidth: 200px changehandleSearch / el-table v-loadingloading refmultipleTableRef :datatableData row-keyid selection-changehandleSelectionChange el-table-column typeselection reserve-selection width55 / el-table-column propname label姓名 / el-table-column propdept label部门 / /el-table el-pagination :current-pagepage.current :page-sizepage.size :totalpage.total layoutprev, pager, next, total current-changehandlePageChange / el-button typeprimary clickhandleSubmit提交已选 {{ selectedMap.size }} 项/el-button /div /template script setup import { ref, reactive, nextTick } from vue const loading ref(false) const tableData ref([]) const page reactive({ current: 1, size: 10, total: 0, keyword: }) const multipleTableRef ref(null) // 核心全局选中集合用 Map 维护key 是行主键 id const selectedMap ref(new Map()) // 模拟接口请求 const fetchTableData ({ page, pageSize, keyword }) { return new Promise(resolve { setTimeout(() { const mockData [ { id: 1, name: 张三, dept: 研发部 }, { id: 2, name: 李四, dept: 产品部 }, { id: 3, name: 王五, dept: 设计部 }, { id: 4, name: 赵六, dept: 测试部 }, { id: 5, name: 孙七, dept: 运维部 } ] const filtered keyword ? mockData.filter(item item.name.includes(keyword)) : mockData const start (page - 1) * pageSize resolve({ list: filtered.slice(start, start pageSize), total: filtered.length }) }, 200) }) } const loadData async () { loading.value true try { const { list, total } await fetchTableData({ page: page.current, pageSize: page.size, keyword: page.keyword }) tableData.value list page.total total restoreSelectionAfterSearch() } finally { loading.value false } } const handleSearch () { page.current 1 loadData() } const handlePageChange (current) { page.current current loadData() } const handleSelectionChange (rows) { const currentPageKeys new Set(tableData.value.map(row row.id)) const latestSelectedKeys new Set(rows.map(row row.id)) // 从全局 Map 中删除在当前页已被取消选中的行 selectedMap.value.forEach((value, key) { if (currentPageKeys.has(key) !latestSelectedKeys.has(key)) { selectedMap.value.delete(key) } }) // 合并当前页最新选中的行进全局 Map rows.forEach(row { selectedMap.value.set(row.id, row) }) } // 搜索 / 翻页重新加载数据后把之前选中且命中的行恢复勾选状态 const restoreSelectionAfterSearch () { nextTick(() { tableData.value.forEach(row { if (selectedMap.value.has(row.id)) { multipleTableRef.value.toggleRowSelection(row, true) } }) }) } const handleSubmit () { const selectedRows [...selectedMap.value.values()] const ids selectedRows.map(row row.id) console.log(跨页汇总选中的行, selectedRows) console.log(跨页汇总选中的 ID, ids) } loadData() /script模板 B 的核心思想我在第 4 节已经拆解过用 Map 维护全局选中集合每次 selection-change 只负责增量同步当前页的状态变化。这样无论用户翻多少页、搜索多少次只要 row-key 稳定选中的结果就不会丢。这里有一个需要单独拎出来说的场景用户在第 1 页勾了几行后输入关键词搜索结果列表里没有之前勾选的行。此时这些行不会出现在表格里但它们依然在 selectedMap 中。如果你希望搜索后已经选中的行继续保留那就什么都不用做如果你希望搜索后只保留结果里仍然存在的选中行就需要在 loadData 成功后按当前页数据过滤一下 selectedMap具体逻辑按业务需求调整。restoreSelectionAfterSearch这个方法的作用是搜索或翻页后新加载的数据如果命中之前已选中的行则自动把勾选状态恢复出来。这里有个时序细节必须先赋新数据给 tableData等下一轮渲染结束后再调用 toggleRowSelection所以我在方法外层包了nextTick。如果你拿到新数据后立刻调用表格还没渲染完成方法会静默失败。关于 el-table 多选选中行的获取做项目这几年我最大的体会是一定要先把选中状态由谁维护这个问题想清楚。简单场景可以把选中状态全权交给组件内部但一旦涉及分页、搜索、弹窗复用、树形数据你就得自己扛起一份独立的选中集合并且用稳定唯一的 row-key 把每一行认清楚。最后再分享一个调试小技巧拿不准组件内部选中状态的行为时可以在 selection-change 回调里打印一行val.map(r r.id)然后界面上勾一行看数组怎么变翻一页再看一眼搜索一次再观察。花不了十分钟你就能直观理解这个机制到底是怎么运作的比翻文档死记硬背高效得多。
返回列表