Maison >interface Web >js tutoriel >Explication détaillée des étapes d'utilisation de Sortable dans Vue
Cette fois, je vais vous donner une explication détaillée des étapes d'utilisation de Sortable avec Vue. Quelles sont les précautions pour utiliser Sortable avec Vue. Voici des cas pratiques, jetons un coup d'œil.
J'ai précédemment développé un système de gestion backend qui utilisait la bibliothèque de composants Vue et Element-UI. J'ai rencontré un problème très intéressant et j'aimerais le partager avec vous.
La scène est comme ceci. Sur une page d'affichage de liste, j'ai utilisé le composant table d'Element-UI. La nouvelle exigence est de prendre en charge le tri par glisser-déposer basé sur la table d'origine. Cependant, le composant d'origine lui-même ne prend pas en charge le tri par glisser-déposer, et comme il est directement introduit depuis Element-UI, il n'est pas pratique de modifier son code source, la seule méthode réalisable est donc d'exploiter directement le DOM.
La méthode spécifique consiste à effectuer de vraies opérations DOM sur this.$el dans la fonction montéecycle de vie, à surveiller une série d'événements de glisser et à rappeler en cas de déplacement du DOM à l'intérieur et de mise à jour des données.
Il existe de nombreux événements HTML5 Drag, qui sont similaires aux événements Touch. Vous pouvez également les implémenter manuellement, mais ici, je suis paresseux et j'utilise une bibliothèque open source Sortable et je la transmets directement.$el. Écoutez le rappel encapsulé et, selon le modèle de développement de Vue, mettez à jour les données de données réelles dans le rappel du DOM mobile pour maintenir la cohérence entre les données et le DOM.
Si vous pensez que c'est fini ici, vous vous trompez totalement. Tôt ou tard, vous devrez rembourser la paresse que vous avez volée. . . Je pensais que cette solution était très bonne, mais dès que j'ai voulu la déboguer, un phénomène étrange s'est produit : après que A et B aient été glissés et échangés, B et A ont été à nouveau échangés comme par magie ! Que se passe-t-il? Il semble qu'il n'y ait aucun problème avec notre fonctionnement. Une fois le vrai DOM déplacé, nous déplaçons également les données correspondantes. L'ordre du tableau de données et l'ordre du DOM rendu doivent être cohérents.
Quel est le problème ? Rappelons le principe d'implémentation de Vue. Avant Vue2.0, la liaison bidirectionnelle était réalisée via DefinePropertyinjection de dépendances et de suivi. Pour l'instruction de tableau v-for, si une clé unique est spécifiée, la différence entre les éléments du tableau sera calculée via un algorithme Diff efficace et des opérations de mouvement ou de suppression minimales seront effectuées. Après l'introduction de Virtual Dom après Vue2.0, l'algorithme Dom Diff de l'élément Children est en fait similaire au premier. La seule différence est qu'avant 2.0, Diff ciblait directement l'objet tableau de l'instruction v-for, alors qu'après 2.0. , il ciblait Virtual Dom. L'algorithme DOM Diff ne sera pas décrit en détail ici. L'algorithme de comparaison dom virtuel est expliqué plus clairement ici
Supposons que notre tableau d'éléments de liste soit
['A',' B', 'C', 'D']
Le nœud DOM rendu est
[$A,$B,$C, $ D]
Alors la structure correspondante de Virtual Dom est
[{elm:$A,data:'A'},
{elm:$ B, data :'B'},
{elm:$C,data:'C'},
{elm:$D,data:'D'}]
Supposons que le glisser-déposer Après le tri par glisser-déposer, le vrai DOM devient
[$B,$A,$C,$D]
À l'heure actuelle, nous n'utilisons que le vrai DOM, adapté Sa position a été modifiée, mais la structure de Virtual Dom n'a pas changé Il est toujours
[{elm:$A,data:'A'},
{. orme:$B,data: 'B'},
{elm:$C,data:'C'},
{elm:$D,data:'D'}]
À ce moment-là, nous Les éléments de la liste sont également triés selon le vrai DOM et deviennent
['B','A','C','D']
À l'heure actuelle, selon l'algorithme Diff, le Patch calculé est que les deux premiers éléments de VNode sont des nœuds du même type, ils sont donc mis à jour directement, c'est-à-dire que le nœud $A est mis à jour en $ B, le nœud $B est mis à jour en $A et le vrai DOM revient à
[$A,$B,$C,$D]
Il y a donc un problème car il est mis à jour par l'algorithme Patch après le glissement. Le chemin de l'opération peut être simplement compris comme
Faites glisser et déplacez le vrai DOM -> Manipulez le tableau de données -> le vrai DOM
La cause profonde
La cause profonde est virtuelle Il y a une incohérence entre le DOM et le vrai DOM.
Donc, avant Vue2.0, comme Virtual DOM n'a pas été introduit, ce problème n'existait pas.
Lorsque vous utilisez le framework Vue, essayez d'éviter d'utiliser directement le DOM
Solution
1 Marquez de manière unique chaque VNode en définissant la clé This. C'est également la manière recommandée d'utiliser la directive v-for dans Vue. Parce que la méthode sameVnode sera appelée pour juger si deux VNodes sont du même type, la priorité est de juger si la clé est la même
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. Parce que la raison fondamentale est que le vrai DOM et Les VNode sont incohérents, vous pouvez donc déplacer le vrai DOM en faisant glisser Le fonctionnement du DOM est restauré, c'est-à-dire dans la fonction de rappel , restaurez [$B, $A, $C, $D] vers [ $A, $B, $C, $D], pour que le DOM L'opération soit renvoyée à Vue
Faites glisser et déplacez le vrai DOM ->Restaurer l'opération de déplacement-> > Corrigez l'algorithme puis mettez à jour le vrai DOM
Le code est le suivant
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)) } } })
Solution violente ! Sans mise à jour du correctif, effectuez un nouveau rendu directement via les paramètres v-if. Bien sûr, ce n'est pas recommandé de faire cela, mais je donne juste cette idée ~
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 }) },
Je pense que vous maîtrisez la méthode après avoir lu le cas dans cet article. Pour des informations plus intéressantes, veuillez prêter attention. vers d'autres articles connexes sur le site Web PHP chinois !
Lecture recommandée :
Comment vue-cli effectue des requêtes inter-domaines
Angular5 étapes pour ajouter une classe de style au composant tags Expliquer
Comment implémenter la surcharge des opérateurs dans JS
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!