今回は、Vue で Sortable を使用するための手順について詳しく説明します。Vue で Sortable を使用する際の 注意事項 は何ですか?実際の事例を見てみましょう。
私は以前、コンポーネント ライブラリ Vue と Element-UI を使用したバックエンド管理システムを開発しました。非常に興味深い問題に遭遇したので、それを共有したいと思います。 シーンは次のようになります。リスト表示ページでは、Element-UIのテーブルコンポーネントを使用します。新しい要件は、元のテーブルに基づいたドラッグアンドドロップの並べ替えをサポートすることです。しかし、元のコンポーネント自体はドラッグ&ドロップによるソートに対応しておらず、Element-UIから直接導入しているためソースコードの修正が不便で、DOMを直接操作するしか方法がありません。 具体的な方法は、マウントされたライフサイクル関数内でthis.$elに対して実際のDOM操作を実行し、ドラッグの一連のイベントを監視し、イベントコールバックでDOMを移動し、データを更新することです。
Touch イベントに似た HTML5 の Drag イベントが多数あり、手動で実装することもできますが、ここでは、オープンソースの Sortable ライブラリを直接渡して、カプセル化されたイベントを監視します。 Vue の開発モデルに従って、モバイル DOM のコールバックで実際のデータ データを更新して、データと DOM の間の一貫性を維持します。 これで終わりだと思ったら、それは完全に間違いです。遅かれ早かれ、盗んだ怠惰を返さなければなりません。 。 。この解決策は素晴らしいと思いましたが、デバッグしようと思った瞬間、奇妙な現象が発生しました。A と B をドラッグして交換した後、B と A が魔法のように再び交換されてしまいました。これはどうなっているでしょうか?実際の DOM を移動した後、データ配列の順序とレンダリングされた DOM の順序も一致するはずです。 何が問題ですか? Vue の実装原理を思い出してみましょう。Vue2.0 より前では、双方向バインディングは、definePropertyリスト要素の配列が['A','B','C','D']レンダリングされたであると仮定します。
DOM ノード は
[$A, $B, $C, $D] ですそして、対応する Virtual Dom の構造は
[{elm:$A,data:'A '},ドラッグを想定しますドラッグしてソートすると、実DOMは{elm:$B,data:'B'},
{elm:$C,data:'C'},
{elm:$D,data:'D'}]
[$B,$A,$C,$D]この時は実DOMを操作して位置を変更しただけですが、仮想Domの構造は変更されませんでしたが、依然として
[{elm:$A,data:'A'},このとき、リスト要素も実際の DOM に従ってソートされ、{elm:$B,data:'B'},
{elm:$C,data:'C'},
{elm:$D,data:'D'}]
['B','A','C', 'D']このとき、Diff アルゴリズムによれば、計算された Patch は、VNode の最初の 2 つの項目が同じタイプのノードであるため、直接更新されます。つまり、$A ノードが更新されます。 $B に変更され、$B ノードが実際の DOM である $A に更新されます。
[$A,$B,$C,$D]に戻っているため、更新されるという問題があります。ドラッグ後のパッチ アルゴリズムによる操作パスは、ドラッグで実際の DOM を移動 -> データ配列を操作 -> パッチ アルゴリズムで実際の DOM を更新する
根本的な原因は、仮想 DOM と実際の DOM の間の不一致です。 Vue2.0 以前では、Virtual DOM が導入されていなかったため、この問題は存在しませんでした。 Vue フレームワークを使用する場合は、DOM Solution 1 を直接操作しないようにしてください。これは、Vue が v-for 命令を使用することを推奨する方法でもあります。 2つのVNodeが同じ型かどうかを判定する際にはsameVnodeメソッドが呼び出されるため、キーが同じかどうかの判定が優先される 2 根本的な理由は実DOMとVNodeが一致していないため、操作は行われません。実際の DOM のドラッグと移動は復元できます。つまり、コールバック関数で、[$B,$A,$C,$D] を [$A,$B,$C,$D] に復元します。 DOM 操作を Vue に戻します 実際の DOM をドラッグして移動します -> データ配列を操作します -> パッチ アルゴリズムを使用して実際の DOM を更新します コードは次のとおりです 3。 !パッチ更新なしで、v-if 設定を通じて直接再レンダリングします。もちろん、これを行うことはお勧めしません。私はこのアイデアを提供しているだけです~ この記事の事例を読んだ後は、この方法を習得したと思います。さらに興味深い情報については、PHP に関する他の関連記事に注目してください。中国語のサイトです! 推奨読書: function sameVnode (a, b) {
return (
a.key === b.key &&
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
)
}
var app = new Vue({
el: '#app',
mounted:function(){
var $ul = this.$el.querySelector('#ul')
var that = this
new Sortable($ul, {
onUpdate:function(event){
var newIndex = event.newIndex,
oldIndex = event.oldIndex
$li = $ul.children[newIndex],
$oldLi = $ul.children[oldIndex]
// 先删除移动的节点
$ul.removeChild($li)
// 再插入移动的节点到原有节点,还原了移动的操作
if(newIndex > oldIndex) {
$ul.insertBefore($li,$oldLi)
} else {
$ul.insertBefore($li,$oldLi.nextSibling)
}
// 更新items数组
var item = that.items.splice(oldIndex,1)
that.items.splice(newIndex,0,item[0])
// 下一个tick就会走patch更新
}
})
},
data:function() {
return {
message: 'Hello Vue!',
items:[{
key:'1',
name:'1'
},{
key:'2',
name:'2'
},{
key:'3',
name:'3'
},{
key:'4',
name:'4'
}]
}
},
watch:{
items:function(){
console.log(this.items.map(item => item.name))
}
}
})
mounted:function(){
var $ul = this.$el.querySelector('#ul')
var that = this
var updateFunc = function(event){
var newIndex = event.newIndex,
oldIndex = event.oldIndex
var item = that.items.splice(oldIndex,1)
that.items.splice(newIndex,0,item[0])
// 暴力重新渲染!
that.reRender = false
// 借助nextTick和v-if重新渲染
that.$nextTick(function(){
that.reRender = true
that.$nextTick(function(){
// 重新渲染之后,重新进行Sortable绑定
new Sortable(that.$el.querySelector('#ul'), {
onUpdate:updateFunc
})
})
})
}
new Sortable($ul, {
onUpdate:updateFunc
})
},
以上がVue で Sortable を使用する手順の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。