(Explication détaillée des conditions d'erreur et des solutions) Les données Vue sont mises à jour mais la page n'est pas mise à jour

Cliquez pour suivre, comme Shuan Q

arrière-plan

Dans le projet vue, certains d'entre nous rencontreront la situation où les données sont modifiées, mais la vue n'est pas mise à jour. Les scénarios spécifiques sont différents et les méthodes pour résoudre le problème sont également différentes. J'ai lu beaucoup d'articles sur Internet, et je vais les résumer ici. Compte tenu du fait que la vue de mise à jour des données n'est pas mise à jour, il est recommandé d'avoir une compréhension plus approfondie du principe réactif de Vue.

Si vous avez besoin de faire une mise à jour forcée dans vue, 99,9% du temps, vous faites quelque chose de mal quelque part.

 1. Résumé de 7 situations où les données de vue sont mises à jour mais la page n'est pas mise à jour

1. Vue ne peut pas détecter la propriété qui n'existe pas dans les données lors de la création de l'instance

Raison : étant donné que vue effectuera une transformation getter/setter sur la propriété lors de l'initialisation de l'instance, la propriété doit exister sur l'objet de données pour que vue le convertisse en un objet réactif.

 ①Scène :

var vm = new Vue({
  data:{},
  // 页面不会变化
  template: '<div>{
   
   {message}}</div>'
})
vm.message = 'Hello!' // `vm.message` 不是响应式的

②Solution :

var vm = new Vue({
  data: {
    // 声明 a、b 为一个空值字符串
    message: '',
  },
  template: '<div>{
   
   { message }}</div>'
})
vm.message = 'Hello!'

2. Vue ne peut pas détecter l'ajout ou la suppression de propriétés d'objet

Raison : Officiel – En raison des limitations de JavaScript (ES5), Vue.js ne peut pas détecter l'ajout ou la suppression de propriétés d'objet. Étant donné que vue.js convertit les propriétés en getters/setters lors de l'initialisation d'une instance, les propriétés doivent se trouver sur l'objet de données pour que vue.js le convertisse et le rende réactif.

 ①Scène :

var vm = new Vue({
  data:{
    obj: {
      id: 001
    }
  },
  // 页面不会变化
  template: '<div>{
   
   { obj.message }}</div>'
})

vm.obj.message = 'hello' // 不是响应式的
delete vm.obj.id       // 不是响应式的

②Solution :

//动态添加 -- vue.set
Vue.set(vm.obj,propertyName,newValue);

//动态添加 -- vm.$set
vm.$set(vm.obj,propertyName,newValue);

//动态添加多个
// 代替Object.assign(this.obj,{a:1,b:2})
this.obj = Object.assign({},this.obj,{a:1,b:2});

//动态移除--vm.$delect
Vue.delete(vm.obj,propertyName);

//动态移除 --vm.$delete
vm.$delete(vm.obj,propertyName);

3. Vue ne peut pas détecter à travers la valeur d'index du tableau et modifier directement un élément du tableau

Raison : Officiel - en raison des limitations de JavaScript, Vue ne peut pas détecter les changements dans les tableaux et les objets ; le coût des performances n'est pas directement proportionnel à l'expérience utilisateur.

 ①Scène :

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的

②Solution :

//Vue.set
Vue.set(vm.items,indexOfItem,newValue);

//vm.$set
vm.$set(vm.items,indexOfItem,newValue);

//Array.prototype.splice
vm.items.splice(indexOfItem,1,newValue);

Extension : Object.defineProperty() peut surveiller les changements de tableau

Object.defineProperty() peut surveiller les modifications du tableau. Cependant, l'ajout d'un attribut (index) à un tableau ne détectera pas les modifications de données, car l'indice (index) du tableau nouvellement ajouté ne peut pas être surveillé, et il en va de même pour la suppression d'un attribut (index).

①Scène :

var arr = [1, 2, 3, 4]
arr.forEach(function(item, index) {     Object.defineProperty(arr, index, {     set: function(value) {       console.log('trigger setter')       item = value     },     get: function() {       console.log('trigger getter')       return item     }   }) }) arr[1] = '123' // trigger setter arr[1] // trigger getter la valeur de retour est "123 " arr[5] = 5 // ne déclenchera pas le setter et le getter













 4. Vue ne peut pas surveiller le changement de modification directe de la longueur du tableau

Raison : Officiel - en raison des limitations de JavaScript, vue ne peut pas détecter les changements dans les tableaux et les objets ; (le coût des performances n'est pas directement proportionnel à l'expérience utilisateur).

 ①Scène :

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items.length = 2 // 不是响应性的

②Solution :

vm.items.splice(newLength)

5. Les données DOM de l'opération ne changeront pas avant l'exécution de la mise à jour asynchrone

Raison : Vue est exécutée de manière asynchrone lors de la mise à jour du DOM. Tant que des modifications de données sont détectées, Vue ouvre une file d'attente et met en mémoire tampon toutes les modifications de données qui se produisent dans la même boucle d'événements. Si le même observateur est déclenché plusieurs fois, il ne sera poussé qu'une seule fois dans la file d'attente. Cette déduplication lors de la mise en mémoire tampon est très importante pour éviter les calculs inutiles et les manipulations du DOM. Ensuite, lors de la prochaine boucle temporelle "tic", Vue vide la file d'attente et effectue le travail réel (dédupliqué). Vue essaie en interne d'utiliser Promise.then natif, MutationObserver et setImmediate pour les files d'attente asynchrones. Si l'environnement d'exécution ne le prend pas en charge, il utilisera à la place setTimeout(fn,0).

 ①Scène :

<div id="example">{
   
   {message}}</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
vm.$el.style.color = 'red' // 页面没有变化

 ②Solution :

var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
//使用Vue.nextTick(callback) callback将在DOM更新完成后被调用
Vue.nextTick(function(){
	vm.$el.textContent === 'new message'  //true
	vm.$el.style.color = 'red' //文字颜色变成红色
})

Extension : incompréhension de la réponse des données causée par une mise à jour asynchrone

<!-- 页面显示:我更新啦! -->
<div id="example">{
   
   {message.text}}</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: {},
  }
})
vm.$nextTick(function () {
  this.message = {}
  this.message.text = '我更新啦!'
})

Dans le code ci-dessus, nous avons déclaré un objet de message vide dans l'objet de données, puis avons exécuté les deux morceaux de code suivants dans le rappel asynchrone déclenché après la fin du prochain cycle de mise à jour DOM :

this.message = {};
this.message.text = '我更新啦!'

À ce stade, le modèle a été mis à jour et la page montrera enfin que je l'ai mis à jour ! .
Le modèle a été mis à jour et devrait être réactif, si vous pensez que c'est le cas, vous avez entré le mauvais endroit.
Au début, nous avons simplement déclaré un objet de message vide dans l'objet de données, qui n'a pas d'attribut de texte, donc l'attribut de texte n'est pas réactif.
Mais le modèle a en fait été mis à jour, alors que se passe-t-il ?
En effet, la mise à jour DOM de Vue.js est asynchrone, c'est-à-dire que lorsque l'opération de définition se produit, l'instruction ne sera pas mise à jour immédiatement et il y aura un retard dans l'opération de mise à jour de l'instruction.Lorsque la mise à jour de l'instruction est exécutée, l'attribut de texte a été affecté à ce moment. Ainsi, lorsque la directive met à jour le modèle, elle obtient la nouvelle valeur.

Chaque directive/liaison de données dans le modèle a un objet observateur correspondant, qui enregistre les propriétés en tant que dépendances lors de l'évaluation. Plus tard, lorsque le setter dépendant est appelé, il déclenchera le recalcul de l'observateur, ce qui entraînera également la mise à jour du DOM par ses instructions associées.

 

 Le processus spécifique est le suivant :

  • Lors de l'exécution de this.message = { };, le setter est appelé ;
  • Une fois que Vue.js a détecté que le setter dont dépend le message est appelé, il déclenchera le recalcul de l'observateur ;
  • this.message.text = 'Je l\'ai mis à jour ! '; Attribuer une valeur à l'attribut text ;
  • Une fois l'exécution de la logique de rappel asynchrone terminée, ses instructions associées mettront à jour le DOM et la mise à jour de l'instruction commencera à s'exécuter.
  • Par conséquent, la véritable opération qui déclenche la mise à jour du modèle est this.message = { } ; causé par cette phrase, car le setter est déclenché, donc en regardant simplement l'exemple ci-dessus, les seules données avec des caractéristiques réactives sont la couche de message, qui est l'attribut ajouté dynamiquement n'est pas disponible.

Correspondant au deuxième point ci-dessus - Vue ne peut pas détecter l'ajout ou la suppression de propriétés d'objet

 6. Le niveau d'imbrication des boucles est trop profond, la vue n'est pas mise à jour ?

J'ai vu certaines personnes sur Internet dire que le niveau de mise à jour des données est trop profond, ce qui fait que les données ne sont pas mises à jour ou que la mise à jour est lente, ce qui fait que la vue n'est pas mise à jour ?
PS : C'est ce que j'ai rencontré, et je n'ai trouvé aucun problème (embarrassing.jpg) pour le moment, j'ai donc utilisé la méthode de mise à jour forcée.
En réponse à la situation ci-dessus, quelqu'un a donné une solution pour utiliser la mise à jour forcée :

Si vous avez besoin de faire une mise à jour forcée dans vue, 99,99% du temps, vous faites quelque chose de mal quelque part.

vm.$forceUpdate()

7. Extension : lorsque les paramètres de routage changent, la page ne sera pas mise à jour (les données ne seront pas mises à jour)

Développez un problème selon lequel la page n'est pas mise à jour en raison de modifications des paramètres de routage. Le fait que la page ne soit pas mise à jour signifie essentiellement que les données ne sont pas mises à jour.

Raison : lorsque le composant de la vue de routage fait référence au même composant, lorsque les paramètres de routage changent, le composant ne peut pas être mis à jour, ce que nous disons souvent que la page ne peut pas être mise à jour.

①Scène :

<div id="app">
  <ul>
    <li><router-link to="/home/foo">To Foo</router-link></li>    
    <li><router-link to="/home/baz">To Baz</router-link></li>    
    <li><router-link to="/home/bar">To Bar</router-link></li>    
  </ul>    
  <router-view></router-view>
</div>
const Home = {
	template:`<div>{
   
   {message}}</div>`,
	data(){
		return{
			message:this.$route.params.name
		}
	}
}
const router = new VueRouter({
	mode:'history',
	  routes:[
	  {path:'/home',component:Home},
	  {path:'/home/:name',component:Home}
	  ]
})
new Vue({
	el:'#app',
	router
})

Dans le code ci-dessus, nous avons configuré une route dynamique '/home/:name' dans l'option de construction de route routes.Ils partagent un composant de route Home, ce qui signifie qu'ils réutilisent RouterView.
Lorsque la route est commutée, la page affichera uniquement les paramètres correspondant à la première route, et le message ne changera pas lorsque la route sera commutée ultérieurement.

②Scène :

<div id="app">
  <ul>
    <li><router-link to="/home/foo">To Foo</router-link></li>    
    <li><router-link to="/home/baz">To Baz</router-link></li>    
    <li><router-link to="/home/bar">To Bar</router-link></li>    
  </ul>    
  <router-view></router-view>
</div>
const Home = {
  template: `<div>{
   
   {message}}</div>`,
  data() {
    return {
      message: this.$route.params.name
    }
  }
}

const router = new VueRouter({
  mode:'history',
    routes: [
    {path: '/home', component: Home },
    {path: '/home/:name', component: Home }
  ]
})

new Vue({
  el: '#app',
  router
})

Dans le code ci-dessus, nous avons configuré une route dynamique '/home:/name' dans les routes d'option de construction de routage, et elles partagent un composant de routage Home, ce qui signifie qu'elles réutilisent RouterView.
Lorsque la route est commutée, la page affichera uniquement les paramètres correspondant à la première route, et le message ne changera pas lorsque la route sera commutée ultérieurement.

③Solution :

Il existe de nombreuses solutions, et voici quelques-unes des méthodes que j'utilise couramment.

1. Surveillez les changements de $route via watch :

const Home = {
  template: `<div>{
   
   {message}}</div>`,
  data() {
    return {
      message: this.$route.params.name
    }
  },
  watch:{
	 '$route':function(){
		this.message = this.$route.params.name
	 }
  }
}
...
new Vue({
  el: '#app',
  router
})

2. Liez l'attribut key, afin que vue pense qu'il est différent.

Inconvénients : si vous passez de /home à /user et à d'autres routes, nous n'avons pas à nous soucier des mises à jour des composants, l'attribut key est donc redondant pour le moment.

<div id="app">
  ...
  <router-view :key="key"></router-view>
</div>

Deuxièmement, la méthode que j'utilise souvent :

  1. Liez la valeur de clé à dom, puis mettez à jour les données, puis modifiez la valeur de clé, ce qui peut résoudre le problème
  2. forcer l'actualisation
  3. set, delete, Object.assign() et autres méthodes
  4. this.$nextTick(() => {

                    })

Je suppose que tu aimes

Origine blog.csdn.net/qq_59747594/article/details/128216340
conseillé
Classement