Vue+ソータブルを使用する

php中世界最好的语言
php中世界最好的语言オリジナル
2018-06-11 10:54:242230ブラウズ

今回は、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 依存関係の挿入と追跡によって実現されていました。 v-for 配列命令では、一意の Key が指定されている場合、配列内の要素の差分が効率的な Diff アルゴリズムによって計算され、最小限の移動または削除操作が実行されます。 Vue 2.0 以降の Virtual Dom の導入後、Children 要素の Dom Diff アルゴリズムは実際には前者と似ています。唯一の違いは、2.0 より前では、Diff は v-for 命令の配列オブジェクトを直接ターゲットとしていたのに対し、2.0 以降では、それはVirtual Domをターゲットにしていました。 DOM Diff アルゴリズムについては、ここでは説明しません。virtual-dom diff アルゴリズムについては、ここでより明確に説明します

リスト要素の配列が

['A','B','C','D']

であると仮定します。

レンダリングされた DOM ノードは

[$A,$B,$C,$D]

すると、対応する Virtual Dom の構造は

[{elm:$A,data:'A'} ,
{elm:$B,data:'B'},
{elm:$C,data:'C'},
{elm:$D,data:'D'}]

ドラッグ アンド ドロップを想定しますソート後、実DOMは

[$B,$A,$C,$D]

この時は実DOMを操作して位置を調整しただけですが、仮想Domの構造はまだ変更されていません

[{elm:$A,data:'A'},
{elm:$B,data:'B'},
{elm:$C,data:'C'},
{elm :$D,data:'D'}]

このとき、実際のDOMに従ってリスト要素もソートし、

['B','A','C',' となります。 D']

このとき、Diff アルゴリズムによれば、計算された Patch は、VNode の最初の 2 つの項目は同じタイプのノードであるため、直接更新されます。つまり、$A ノードは次のように更新されます。 $B、$B ノードは $A に更新され、実際の DOM は再び

[$A,$B,$C,$D]

に変更されるため、更新されるという問題があります。ドラッグ後のパッチ アルゴリズム: 操作パスは、

実際の DOM をドラッグして移動 -> データ配列を操作 -> パッチ アルゴリズムで実際の DOM を更新します

根本原因仮想 DOM と実際の DOM の間の不一致です。

つまり、Vue2.0 以前では、Virtual DOM が導入されていなかったため、この問題は存在しませんでした。

Vue フレームワークを使用する場合は、DOM を直接操作しないようにしてください

ソリューション

1. キーを設定して各 VNode を一意にマークします。これは、Vue が v-for 命令を使用することを推奨する方法でもあります。 2つのVNodeが同じ型かどうかを判定する際にはsameVnodeメソッドが呼び出されるため、キーが同じかどうかの判定が優先される

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)
 )
}

2 根本的な理由は実DOMとVNodeが一致していないため、操作は行われません。実際の DOM のドラッグと移動を復元できます。つまり、コールバック関数で [$B,$A,$C,$D] を [$A,$B,$C,$D] に復元し、 Vue への DOM 操作

実際の DOM をドラッグして移動します - > 移動操作を復元します - > データ配列を操作します - > パッチ アルゴリズムにより実際の DOM が更新されます

コードは次のとおりです

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))
      }
    }
  })

3。解決!パッチ更新なしで、v-if 設定を通じて直接再レンダリングします。もちろん、これを行うことはお勧めしません。私はこのアイデアを提供しているだけです~

    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
      })
    },

この記事の事例を読んだ後は、この方法を習得したと思います。さらに興味深い情報については、PHP に関する他の関連記事に注目してください。中国語のサイトです!

推奨読書:

Vue+Nuxt.jsでサーバーサイドレンダリングを行う

AngularはHMRコード分析を使用する

vueを使用してモバイルドロップダウンリストを作成する

以上がVue+ソータブルを使用するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。