Frontend Development 14 min read

How Adding a Row Key Fixed a Mysterious SortableJS Drag‑Drop Bug in Vue

The article walks through a puzzling drag‑and‑drop sorting bug in a Vue admin table, explains why the virtual DOM diverged from the real DOM, and shows that adding a unique row‑key to the el‑table element resolves the issue while highlighting best coding practices.

WeDoctor Frontend Technology
WeDoctor Frontend Technology
WeDoctor Frontend Technology
How Adding a Row Key Fixed a Mysterious SortableJS Drag‑Drop Bug in Vue

1. Preface

Learning through bugs can turn frustration into deep knowledge; the author shares a recent mysterious drag‑and‑drop bug encountered while working on a Vue admin list.

bug 越多,能力越大。

After many late‑night debugging sessions, the author decided to document the problem and its solution.

2. What is the problem

Business scenario: a management backend list needs drag‑and‑drop sorting. The requirement looks simple but the difficulty lies in handling the list data during drag operations.

Instead of using a heavyweight library like vue‑draggable, the lightweight SortableJS was chosen.

1. Using SortableJS

SortableJS is lightweight, but its documentation is sparse, making configuration sometimes tricky.

<code>&lt;template&gt;
	&lt;div&gt;
    &lt;!-- 表单 table --&gt;
      &lt;el-table v-loading="loading" :data="currentLessonList" class="p-course-classes-wrapper--class-table"&gt;
        &lt;el-table-column prop="lessonName" label="课时名称"&gt;&lt;/el-table-column&gt;
        &lt;el-table-column prop="lessonCode" label="课时 ID"&gt;&lt;/el-table-column&gt;
        &lt;el-table-column prop="gmtCreated" label="添加时间"&gt;
          &lt;template slot-scope="scope"&gt;
            &lt;span&gt;{{ formatTime(scope.row.gmtCreated )}}&lt;/span&gt;
          &lt;/template&gt;
        &lt;/el-table-column&gt;
        &lt;el-table-column prop="surveyName" label="随堂测试"&gt;
          &lt;template slot-scope="scope"&gt;
            &lt;span&gt;{{ scope.row.surveyName || '--'}}&lt;/span&gt;
          &lt;/template&gt;
        &lt;/el-table-column&gt;
      &lt;/el-table&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
  import Sortable from 'sortablejs'

	export default {
    ...
    activated () {
		// 初始化排序列表
    	this.$nextTick(() => {
        const that = this
      	const tbody = document.querySelector('.el-table__body-wrapper tbody')
        
        this.sortObj = new Sortable(tbody, {
        animation: 150,
        sort: true,
        disabled: !that.isCanDrag,
        onEnd: async function (evt) {
          // SortableJS 不改变数据的实际顺序,但是传递新旧索引值,需要开发者手动根据索引值改变数据顺序
          that.currentLessonList.splice(evt.newIndex, 0, that.currentLessonList.splice(evt.oldIndex, 1)[0])
          that.currentLessonList = that.currentLessonList.map((item, index) => {
            return {
              ...item,
              sort: index + 1
            }
          })

          await that.updateLessonsOrder()
        }
      })
      })
   	}
  }
&lt;/script&gt;</code>

The code shows that after initializing Sortable on the table body, the

onEnd

handler manually reorders the underlying data array to match the new DOM order.

2. Mysterious issue

After implementing the above, a strange bug appeared: swapping the first and third rows in the UI did not reflect the same order in the data array.

The visual DOM order and the model data became inconsistent.

3. Analysis

The view layer had changed, but the model layer had not been updated correctly. The only data‑reordering code resides in the

onEnd

function, yet debugging showed the array remained unchanged.

4. Solution

The fix was to add a unique

row-key

attribute to the

el-table

component, allowing Vue to track each row properly.

<code>&lt;template&gt;
	&lt;el-table :data="currentLessonList" :row-key="row => row.pkId"&gt;
   	....
  &lt;/el-table&gt;
&lt;/template&gt;</code>

One line of code resolved the afternoon‑long mystery.

3. Exploring the root cause

The bug stemmed from a mismatch between the Virtual DOM and the real DOM after Sortable moved elements.

1. Virtual DOM vs Real DOM

Before modern frameworks, developers manipulated real DOM directly (e.g., jQuery). Frameworks like Vue and React introduced a Virtual DOM, allowing developers to work with a lightweight representation that Vue diffs and patches to the real DOM.

2. Concrete example

Assume the list array is:

<code>let tableData = ['A', 'B', 'C', 'D']</code>

Rendered real DOM:

<code>let tableData_dom = ['$A', '$B', '$C', '$D']</code>

Corresponding Virtual DOM structure:

<code>let tableData_vm = [
  {el: '$A', data: 'A'},
  {el: '$B', data: 'B'},
  {el: '$C', data: 'C'},
  {el: '$D', data: 'D'}
];</code>

After dragging, the real DOM becomes:

<code>['$B', '$A', '$C', '$D']</code>

Sortable only changes the real DOM; the Virtual DOM remains unchanged, still reflecting the original order. When the

onEnd

handler updates the data array to match the new DOM order, Vue’s diff algorithm sees a mismatch between the Virtual DOM and the updated data, causing it to re‑patch the real DOM and produce the observed bug.

Drag real DOM → modify data array → patch algorithm updates real DOM

3. Further investigation

Understanding the diff algorithm and the importance of a unique key explains why a single

row-key

line fixes the issue.

4. Self‑reflection

1. Coding standards

Using indexes as keys is discouraged; a proper unique key prevents subtle bugs like this and promotes better long‑term code health.

2. Knowing why

Simply fixing bugs without grasping the underlying mechanisms leads to shallow knowledge; deeper insight into frameworks’ internals yields more robust solutions.

5. Summary

The article presented a real‑world drag‑and‑drop bug, described the business context, detailed the solution (adding a row‑key), explored the root cause (Virtual DOM vs real DOM inconsistency), and highlighted the importance of coding standards and deeper framework understanding.

6. References

深入浅出 Vue 中的 key 值: https://juejin.cn/post/6844903865930743815

Vue 中使用 SortableJS: https://www.jianshu.com/p/d92b9efe3e6a

virtual‑dom (Vue 实现)简析: https://segmentfault.com/a/1190000010090659

Vuevirtual DOMdrag and dropfrontend debuggingSortableJS
WeDoctor Frontend Technology
Written by

WeDoctor Frontend Technology

Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.