Vue: [fonction] Possibilité de désactiver l'observation Vue

Créé le 7 avr. 2016  ·  50Commentaires  ·  Source: vuejs/vue

Mettre à jour:
Si quelqu'un a besoin de cette fonctionnalité, je l'ai publiée en tant que vue non réactive avec les avertissements appropriés et tout.


Nous avons des modèles non simples pour lesquels nous devons désactiver l'observation et la marche de Vue. Un exemple est un modèle de ressource qui a accès à un cache afin qu'il puisse rechercher des ressources associées. Cela provoque la surveillance de tous les objets du cache (probablement inefficace), ainsi que certaines interactions supplémentaires avec d'autres codes. Actuellement, nous contournons ce problème en définissant un observateur factice sur le cache. Quelque chose de semblable à...

import get from 'http';
import Resource from 'resource';


new Vue({
    data: { instance: {}, },
    ready() { this.fetch(); },

    methods: {
        fetch() {
            const Observer = Object.getPrototypeOf(this.instance.__ob__).constructor;

            get('/api/frobs')
            .then(function(data) {
                // initialize Resource w/ JSON document
                const resource = new Resource(data);

                // Protect cache with dummy observer
                resource.cache.__ob__ = new Observer({});

                this.instance = resource;
            });
        },
    },
});

Cela fonctionne, mais

  • repose sur les internes de vue
  • nécessite un objet déjà observé car nous ne pouvons pas importer la classe Observer directement.

Proposition:
Ajoutez une méthode officielle pour désactiver explicitement l'observation/la marche de Vue. par exemple, quelque chose comme...

const someThing = {
  nestedThing: {},
};

// make entire object non-reactive
Vue.nonreactive(someThing);

// make nested object non-reactive
Vue.nonreactive(someThing.nestedThing);
vm.$set('key.path', someThing);

Considérations :

  • Que se passe-t-il si un utilisateur définit un chemin de clé réactif vers un objet non réactif ? Est-ce que vue doit avertir l'utilisateur ? par exemple,

``` js
vm.$set('a', Vue.nonreactive({});

// différent de..
vm.$set('a', {
someKey : Vue.nonreactive({}),
});
```

  • Un objet déjà réactif doit-il avertir l'utilisateur s'il tente de le rendre non réactif ? par exemple,

``` js
// Erreur
Vue.non réactive(vm.$data.a)

// valide
Vue.nonreactive(_.clone(vm.$data.a));
```

Commentaire le plus utile

  1. Si vous devez ignorer l'observation d'un objet/tableau dans data , utilisez Object.freeze() dessus ;
  2. Vous n'avez pas besoin de mettre un objet dans data pour y accéder sur this . Si vous l'attachez simplement à this dans le crochet created() , il n'est pas du tout observé.

Tous les 50 commentaires

Object.freeze() ne fonctionnera-t-il pas dans votre cas ? Il est supporté depuis la v1.0.18

  1. Si vous devez ignorer l'observation d'un objet/tableau dans data , utilisez Object.freeze() dessus ;
  2. Vous n'avez pas besoin de mettre un objet dans data pour y accéder sur this . Si vous l'attachez simplement à this dans le crochet created() , il n'est pas du tout observé.
  • Object.freeze ne fonctionne pas ici. Le cache est mis à jour au fil du temps.
  • La ressource principale _est_ réactive. Je suis surtout intéressé à rendre l'objet de cache imbriqué non réactif.

Alors peut-être est-il temps de repenser la conception de votre modèle. Pourquoi imbriquer ces choses sous quelque chose à observer ?

Parce que le cache est utilisé pour rechercher dynamiquement des ressources associées.

par exemple, nous pourrions avoir des modèles Author et Post . Le modèle d'auteur définit une relation à plusieurs appelés posts avec le modèle de publication. Ce cache contient les données de relation, ainsi que la collection associée.

appeler author.posts récupère les messages du cache.

De par sa conception, Vue décourage de placer des objets complexes avec leur propre mécanisme de mutation d'état dans l'instance data Vue. Vous ne devriez mettre que l'état pur en tant que données observées dans les instances de Vue. Vous pouvez manipuler ces états comme vous le souhaitez, mais les objets responsables de telles manipulations ne doivent pas faire partie de l'état de l'instance Vue.

Tout d'abord, question de clarification - qu'entendez-vous exactement par état pur ? Nous avons deux types d'état :

  • état du modèle (permanent, données synchronisées avec un magasin. Par exemple, une tâche)
  • état de la vue (temporaire, données qui contrôlent le comportement de la vue. Par exemple, réduire/afficher la liste des tâches)

Mais peu importe:
C'est juste. Le modèle est définitivement « complexe », donc cette demande va à l'encontre des meilleures pratiques actuelles. De plus, mon exemple initial n'est pas très bon - c'est juste ce qui a fonctionné pour désactiver l'observation. Ceci est plus représentatif de notre configuration actuelle avec une utilisation possible :

<!-- layout -->
<post :post="post"></post>
<author :author="author" ><author>
<comments :comments="comments"></comments>
import post from 'components/post';
import author from 'components/author';
import comments from 'components/comments';
/* post = {
 *     template: '...'
 *     props: ['post'],
 *     data: () => {collapsed: false},
 *     ...
 * };  */

new Vue({
    el: 'body',
    data() { 
        instance = postStore.fetch({include: ['author', 'comments.author']})
        Vue.nonreactive(instance.cache)

        return {post: instance, },
    },
    components: {
        post,
        author,
        comments,
    },
    ...
});

Fondamentalement, nous avons une vue parent chargée de placer les composants réutilisables dans une mise en page et de récupérer/lier les données aux composants associés. Les composants enfants ne récupèrent pas leurs propres données car les données sont différentes selon les contextes. par exemple, une liste de commentaires d'un _utilisateur contre une liste de commentaires d'un _post.

Le modèle est assez " stupide " à l'exception du fait que les objets associés ne sont pas imbriqués ( {post: {author: {}, comments: []}} ), mais sont plutôt recherchés dans le cache. par exemple, post.comments[2].author peut être exactement le même objet que post.author . Ainsi, au lieu d'avoir plusieurs copies de l'objet auteur, nous n'en avons qu'une qui est recherchée dans le cache. Il n'y a aucune mutation dans ce qui précède - toutes les données sont générées lors de l'extraction initiale.

De plus, la demande n'est plus pertinente, mais une alternative pourrait être de ne pas observer les membres d'objet « privés ». Il peut s'agir de membres avec un trait de soulignement simple ou double. L'inconvénient de cette approche est que ce serait un changement radical.

Si quelqu'un a besoin de cette fonctionnalité, je l'ai publiée en tant que vue non réactive avec les avertissements appropriés et tout.

@rpkilby merci pour le partage !

@rpkilby Une façon de copier un objet et de supprimer l'observable/la réactivité

var newObj = JSON.parse(JSON.stringify(obj))

Vraiment utile car je souhaite conserver un tableau d'"états" et implémenter un objet d'historique d'état dans vuex.

Edit : Cette solution était spécifique à mon cas. J'avais un objet pour lequel je n'avais besoin que d'une copie des valeurs de propriété à un moment donné. Je ne me souciais pas des références, des mises à jour dynamiques, etc.

À l'heure actuelle, le gel de l'objet n'est pas une solution à long terme. [Vue-nonreactive] a Vue comme une dépendance qui est exagérée lorsqu'il s'agit de faire quelque chose d'aussi simple. Une simple vérification du code telle que instance.__ob__ !== false ne suffirait-elle pas ? Cela permettrait aux bibliothèques de s'assurer que des choses comme les caches ne seront pas observées.

class Unobservable {
  construtor() {
    Object.defineProperty(this, '__ob__', {  
      enumerable: false,  configurable: false,
      writable: false, value: false,
    });
  }
}

C'est principalement un problème pour les bibliothèques utilisées dans les applications Vue (du moins pour moi).

Comment dire à Vue de ne regarder (définir la propriété sur) que la profondeur de données à 1 niveau ?

Mon cas est que je veux que Vue soit averti lorsque data.curObj change,
mais pas avec curObj.position , curObj.rotation , etc.

J'avais l'habitude d'utiliser Object.freeze , mais dans ce cas, cela provoquerait une erreur lorsque three.js essaierait d'attribuer des valeurs à l'objet.

Dois-je faire ce qui suit ?
(en fait je l'ai fait dans un autre endroit similaire)

data () {
  return {
    wrapper: Object.freeze({
      actual: [bigData]
    })
  }
},
methods: {
  operation () {
    this.wrapper = Object.freeze({
      actual: [newBigData]
    })
  }
}

// core/observer/watch.js
function _traverse (val: any, seen: ISet) {
  let i, keys
  const isA = Array.isArray(val)
  if ((!isA && !isObject(val)) || !Object.isExtensible(val)) {
    return
  }
  // ...
// core/observer/index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    observerState.shouldConvert &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  // ...
> curObj PerspectiveCamera {uuid: "BD3C14DF-8C2B-4B96-9900-B3DD0EAC1163", name: "PerspectiveCamera", type: "PerspectiveCamera", parent: null, children: Array(0), …}

> Lodash.isPlainObject(curObj) false
> Vue.isPlainObject(curObj) true
  1. Pouvons-nous ajouter une autre condition pour que l'utilisateur désactive l'observation que juste par Object.isExtensible ( Object.freeze ) ?
  2. Pouvons-nous améliorer la détection Vue.isPlainObject ?

Vous pouvez utiliser la déstructuration

var newObj = { ...obj };

Cela devrait le réparer. Cela rendra la méthode isPlainObject false.

/**
 * Makes an object and it's children unobservable by frameworks like Vuejs
 */
class Unobservable {
  /**
   * Overrides the `Object.prototype.toString.call(obj)` result
   * <strong i="6">@returns</strong> {string} - type name
   * <strong i="7">@see</strong> {<strong i="8">@link</strong> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag}
   */
  get [Symbol.toStringTag]() {
    // Anything can go here really as long as it's not 'Object'
    return 'ObjectNoObserve';
  }
}
>> Object.prototype.toString.call(new Unobservable());
   "[object ObjectNoObserve]"

Salut à tous, un point qui a été perdu dans les réponses est que les données dans le commentaire d'origine ne sont pas à l'état pur . Dans mon cas, j'ai une instance de modèle avec une référence privée à un cache de recherche de relation. Par exemple, un article peut rechercher son author ou comments . Lorsque j'appelle article.author , il s'agit d'une recherche de propriété dynamique dans ce cache de relation et non d'un simple accès à un attribut. Quelques éléments à prendre en compte :

  • Le cache n'est pas un état pur, donc je ne veux pas qu'il soit observé par Vue, car c'est un gaspillage de ressources.
  • Le cache ne peut pas être supprimé, car j'ai toujours besoin d'une référence pour effectuer ces recherches/mises à jour dynamiques.
  • Le cache est effectivement un singleton et peut être mis à jour en externe. L'application se met à jour en conséquence.

En réponse à quelques suggestions :

  • L'utilisation de JSON stringify/parse ou la déstructuration d'objets n'est pas suffisante, car cela duplique à la fois l'objet et le cache, ainsi que la rupture de la référence au cache d'origine. La mise à jour de la référence de cache d'origine ne mettra plus à jour les instances de l'application. Sans ces recherches et mises à jour dynamiques, le cache est fondamentalement inutile, et il serait préférable de faire des objets associés des attributs simples sur le modèle d'origine. Il convient également de noter que ces suggestions cassent les méthodes d'instance de ces objets.
  • Les suggestions de @Mechazawa ont du sens si vous contrôlez la création de types et que les types sont conçus pour être utilisés avec Vue. Dans mon cas, il s'agit d'une bibliothèque externe qui n'est pas liée à Vue, et je ne veux pas me donner la peine de modifier les types dans l'application. Il est beaucoup plus simple pour la couche vue de simplement marquer certaines propriétés connues comme non observables.

Ma seule critique :

Vue-nonreactive a Vue comme une dépendance qui est exagérée lorsqu'il s'agit de faire quelque chose d'aussi simple.

Je ne sais pas pourquoi ce serait une mauvaise chose? Vous utilisez déjà Vue dans votre application et le plugin est spécifique à Vue. Si je ne me trompe pas, la plupart des outils de build sont suffisamment intelligents pour ne pas créer de bundles avec des dépendances en double. Quoi qu'il en soit, ce n'est pas correct . Il existe une dépendance de développement, mais pas une dépendance d'exécution.


Quoi qu'il en soit, je suis heureux de voir que ce message a suscité un certain intérêt, et je suis sûr que certaines de ces autres solutions fonctionneront dans une variété d'autres cas. Je veux simplement souligner les exigences de mon commentaire original et pourquoi les suggestions ne sont pas des alternatives appropriées pour ce cas.

Ainsi, je suis récemment tombé sur cela et j'ai découvert qu'il existe un moyen beaucoup plus simple de court-circuiter la logique d'observation de Vue : _Définir une propriété comme non configurable._

Fond

Dans mon application, je dois travailler avec une bibliothèque tierce (OpenLayers), qui crée des instances de classe contenant des données et ne prenant en charge aucun système de réactivité. Essayer d'en mettre un a causé tellement de maux de tête, laissez-moi vous le dire. Ainsi, la seule solution viable pour une application à grande échelle utilisant cette bibliothèque est de laisser OpenLayers avoir les choses comme il le souhaite, et pour moi de rendre Vue plus agréable avec ces objets horriblement imbriqués et uber de malheur. Avant de trouver ce problème, mon application utilisait environ 3 Go de RAM (sur notre plus grand ensemble de données), tout cela à cause du fait que Vue rendait ces objets réactifs. De plus, le chargement était très lent. J'ai essayé Vue-non réactif, et cela m'a aidé, mais seulement pour nous ramener à environ 1 Go. Avant d'utiliser Vue, l'application fonctionnait à environ 350 Mo.

Solution

Tout ce que vous ne voulez pas être réactif, marquez simplement comme configurable: false . C'est aussi simple que :

Object.defineProperty(target, 'nested', { configurable: false });

(Cela empêche la propriété nested et toutes ses propriétés d'être observées.)

C'est ça! Aucune dépendance de Vue, et sans doute même pas incorrecte. Avec cela, mon application est réduite à 200 Mo avec notre plus grand ensemble de données. C'est simple et facile, et ne nécessite qu'une modification de la documentation du côté de Vue pour en faire un moyen "officiel" de rendre quelque chose non réactif.

Intéressant - semble définitivement être une alternative viable.

Existe-t-il un moyen de suspendre temporairement l'observation réactive et de la réactiver plus tard ?

J'ai un observateur d'accessoires, dans lequel je mets à jour un objet énorme où je ne veux pas déclencher la mise à jour du DOM uniquement après la fin de toute la préparation des données.

@intijk Pas exactement. Vous voyez, cela dépend de ce que vous essayez de faire ; Vue doit finalement appliquer votre état, donc simplement faire une pause pendant qu'il est calculé n'aide pas beaucoup. Si, à la place, vous essayez d'ignorer les états intermédiaires et d'appliquer uniquement l'état final, commencez simplement par un nouvel objet, puis affectez cet objet à la fin.

Par exemple (psuedocode) :

doUpdate()
{
   const state = _.cloneDeep(this.myState);

  // Do intermediate state updates

  this.myState = state;
}

(Les mises en garde de Normal Vue concernant la réactivité des objets s'appliquent.)

Ma recommandation serait d'utiliser l'astuce configurable ci-dessus pour ignorer les sections de votre grand objet qui n'ont pas besoin d'être réactifs. Si tout _doit_ être réactif, je recommande d'utiliser quelque chose comme vuex .

@Morgul J'ai déjà utilisé cette astuce pendant longtemps, mais le fait est que je ne veux plus utiliser cette astuce.
Dans mon cas, l'objet de données est maintenant assez gros, allant de 2M à 100M, effectuer une copie en profondeur sur un tel objet est assez pénible.

@intijk Cela semble incroyablement complexe pour quelque chose

@Morgul
Je ne pense pas que l'affaire soit complexe, l'affaire elle-même est simple, les données sont un peu grosses. Chaque fois que le réseau chargera un fichier journal de visualisation indexé, j'ai un composant de visualisation pour l'afficher.

Quelqu'un a-t-il des idées sur la définition d'un champ non réactif dans une propriété calculée ? Ma première idée dépend de la non-réactivité de l'affectation au tableau...

template: '<div v-html="markdown.render(input, env)"></div>',
props: ['id', 'input'],
computed: {
  env:      function() { return { reactive:this.id, non_reactive:[] } },
  markdown: function() { return Markdown },
},

// within markdown.render():
  env.non_reactive[0] = internal_data;

Mais ce n'est pas exactement de l'auto-documentation :-)

Salut les gars. Je viens de trouver ce problème et j'ai découvert que je faisais face à un problème qui ressemble assez au problème de rpkilby : mon projet construit une série de DOM virtuels Vue (ou appelés vnode) à partir d'un objet JSON. Je vais utiliser cet objet JSON pour créer une application Android. Quoi qu'il en soit, cet objet JSON peut être de grande taille, et lorsque j'utiliserai ce JSON dans Vue, il sera observé par Vue. J'ai essayé la méthode de rpkilby et de Morgul, mais cela ne fonctionne pas. ). Je me demande si nous pouvons le faire dans Vue traverse :
fonction _traverse (val, vu) {
var i, touches ;
var isA = Array.isArray(val);
if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode
|| (val && val['__vueNonReactive__'])) {
retourner
}
...
Comme vous pouvez le voir, j'ajoute le "val && val['__vueNonReactive__']". Ensuite, je modifie mon objet JSON pour avoir le "__vueNonReactive__ = true" avec le nœud racine de JSON, et cela résout mon problème.
Je me demande si cela peut causer un problème? Et cela sera-t-il considéré comme une nouvelle fonctionnalité dans Vue qui permet au développeur de configurer un objet à observer par Vue ou non en configurant une propriété de l'objet ? (Object.freeze peut changer l'objet en un objet immuable, donc il ne peut pas s'adapter à toutes les situations)

considérez ceci https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true peut échapper aux procédures d'observation de vue.

Aujourd'hui, j'ai rencontré un cas dans lequel Vue observe une instance de carte de mapbox-gl, puis des choses étranges se sont produites, la carte s'éclaircit. Mais l'instance de carte doit être transmise entre les instances de vue. Après avoir ajouté map._isVue = true , le problème est résolu.

+1 pour soutenir cela officiellement. J'utilise un objet volumineux qui n'a pas besoin de réactivité dans un composant, et la désactivation de la réactivité inutilisée a réduit la taille de l'objet mémoire de 800 Mo à 43 Mo
J'utilise la solution @Mechazawa est la meilleure solution ici, je pense.
Pour que la solution consistant __ob__ définir configurable sur false fonctionne mais fait planter Vue lorsqu'il essaie de définir le vrai __ob__ .

J'ai construit un plugin Vue qui permet de rendre les variables Vue non réactives (il utilise le hook beforeCreate).

C'est plus propre que vue- nonreactive - ce commentaire - votre solution ne fonctionnera pas pour la prochaine version de Vue.


Veuillez consulter Vue-Static pour

<script>
export default {
    static() {
        return {
            map: null,
        };
    },
    mounted() {
        this.map = new mapboxgl.Map({...}); /* something heavy */
    },
};
</script>

Salut @samuelantonioli - il semble que vue-static fasse quelque chose de légèrement différent, désactivant la réactivité pour un objet entier. En revanche, vue-nonreactive est capable de désactiver l'observation pour une seule propriété , tout en gardant le reste de l'objet réactif.

Cela dit, il semble que les intentions soient légèrement différentes. Les propriétés statiques n'observent pas les changements, mais sont destinées à être rendues dans un modèle. Les propriétés non réactives ne sont pas destinées à être observées, ni à être rendues.

par exemple, mon instance de modèle a une référence à un cache d'objets qui permet les recherches d'objets associés. Je veux observer/rendre le instance et le instance.author , mais pas le instance._cache .

new Vue({
    data() {
        const instance = postStore.fetch({include: ['author', 'comments.author']})
        Vue.nonreactive(instance._cache)

        return {post: instance, },
    },
    ...
});

Quoi qu'il en soit, merci pour l'avertissement. Je vais devoir examiner comment la prochaine version utilise les proxy et voir s'il existe un moyen de duper la création de l'observateur/proxy.

@LinusBorg - Je ne vois pas de branche expérimentale. Où se déroule le développement de la prochaine version ?

Nous sommes encore au stade de concept/d'expérimentation et n'avons pas encore publié de branche. Le travail sérieux à ce sujet ne commencera pas avant que nous ayons sorti la mise à jour 2.6, qui elle-même demandera du travail après que nous ayons, espérons-le, la sortie de vue-cli 3.0 très bientôt

Merci pour la mise à jour.

Je ne suis donc pas sûr que le problème existe dans le même périmètre une fois les Proxy ES6 introduits. Dans mon application, je les utilise beaucoup, et leur surcharge par rapport à la surcharge de l'observation actuelle de vue semble beaucoup plus petite. Le diable sera dans les détails, je suppose.

Le problème que j'ai avec Vue-Static est qu'il me semble redondant. Je peux construire mon objet dans un module JS, l'importer, puis renvoyer sa valeur à partir d'une fonction calculée ; puisqu'il ne sera pas observé, peu importe que la valeur de la fonction calculée ne soit jamais recalculée. Et c'est une bien meilleure séparation des préoccupations, puisque je ne fais pas de choses de type logique métier dans mes composants vue.

Dans tous les cas, mon astuce consistant à définir les propriétés comme non configurables est toujours le moyen le moins invasif et le moins dépendant de Vue pour gérer le problème. Il n'y a également aucune raison de supposer que cela romprait avec les proxys ES ; vous ne voulez probablement toujours pas observer les propriétés non configurables. Je peux me tromper complètement, mais nous savons que __ob__ va disparaître... nous ne savons pas si la vérification d'une propriété est configurable.

De plus, cela fonctionne comme un champion dans notre code de production depuis plus de 8 mois. ;) (Nous avons un espace de problème similaire à @samuelantonioli ; nous avons une carte OpenLayers avec laquelle nous devons travailler dans Vue, sans que Vue gonfle notre mémoire à 2,4 Go...)

Je suis d'accord, si vous utilisez un autre modèle, par exemple importer des modules et utilisez des propriétés calculées, vous n'avez pas besoin de Vue-Static . J'avais juste besoin d'un modèle que je pourrais enseigner à mes employés, qui soit facile à comprendre et à utiliser pour eux. Le modèle import-module-and-use-computed-properties n'est pas si clair selon l'OMI.


Un peu OT : je suis à peu près sûr que les proxy ES6 sont une bonne solution, mais j'ai quelques inquiétudes concernant la compatibilité des navigateurs (IE11 et les versions antérieures ne le prennent pas en charge). Je suis intéressé s'il y aura une couche de compatibilité / un certain type de polyfill afin que nous puissions utiliser Vue pour des projets avec des exigences de navigateur plus strictes.

Comment dire à Vue de ne regarder (définir la propriété sur) que la profondeur de données à 1 niveau ?

+1 pour cette idée, il sera plus facile de structurer les données pour utiliser Vue avec une bibliothèque graphique externe (qui a normalement de gros objets imbriqués à plusieurs niveaux).

Qu'en est-il de ne spécifier que certaines propriétés pour qu'elles soient réactives ?

Il me manque peut-être quelque chose d'évident ici, mais en utilisant this.propertyName = { /* quelque chose de gros ici */ };
dans le hook monté () n'est-ce pas une solution pour avoir des propriétés non observées?

set _isVue fera planter vue-devtool, utilisez cette fonction à la place

export default function setIsVue(val) {
    if (!val) return

    Object.defineProperty(val, '_isVue', {
        value: true,
        enumerable: false,
        configurable: true,
    })

    // vue-devtool
    // https://github.com/vuejs/vue-devtools/blob/c309065c57f6579b778341ea37042fdf51a9fc6c/src/backend/index.js#L616
    // 因为有 _isVue 属性
    if (process.env.NODE_ENV !== 'production') {
        if (!val.$options) {
            Object.defineProperty(val, '$options', {
                value: {},
                enumerable: false,
                configurable: true,
            })
        }

        if (!val._data) {
            Object.defineProperty(val, '_data', {
                value: {},
                enumerable: false,
                configurable: true,
            })
        }
    }

    return val
}

Il s'est perdu dans le bruit, mais marquer une propriété comme non configurable semble vraiment être la solution.

Object.keys(scene).forEach((key)=>{
  Object.defineProperty(target, 'nested', { configurable: false });
});

Vraiment sympa quand j'ai besoin de faire circuler un THREE.Scene mais que je ne veux pas littéralement que le graphe de scène entier devienne un gâchis d'observables. Et puis je peux toujours faire circuler l'objet d'origine et il peut être réactif en fonction de cela. Parfait!

Encore un problème.
J'ai des objets contenant de nombreux niveaux imbriqués de propriétés que je souhaite maintenir non réactifs.
1

Même si j'utilise

Il s'est perdu dans le bruit, mais marquer une propriété comme non configurable semble vraiment être la solution.

Object.keys(scene).forEach((key)=>{
  Object.defineProperty(target, 'nested', { configurable: false });
});

Vraiment sympa quand j'ai besoin de faire circuler un THREE.Scene mais que je ne veux pas littéralement que le graphe de scène entier devienne un gâchis d'observables. Et puis je peux toujours faire circuler l'objet d'origine et il peut être réactif en fonction de cela. Parfait!

ou

considérez ceci https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true peut échapper aux procédures d'observation de vue.

Aujourd'hui, j'ai rencontré un cas dans lequel Vue observe une instance de carte de mapbox-gl, puis des choses étranges se sont produites, la carte s'éclaircit. Mais l'instance de carte doit être transmise entre les instances de vue. Après avoir ajouté map._isVue = true , problème résolu.

Les propriétés des objets imbriqués deviennent réactives.

J'ai essayé de le faire de manière récursive, mais Maximum call stack size exceeded , et cela provoque plus de retards.

@Mitroright Vous devrez le faire de manière récursive, mais je soupçonne que votre approche n'était peut-être pas tout à fait correcte.

Le problème ici est de savoir comment Vue gère les tableaux ; simplement marquer la propriété geoJsonData configurable ne fonctionnera probablement pas (j'ai eu des problèmes avec ça, mais je n'ai jamais creusé le "pourquoi".)

Essayez quelque chose comme ceci :

function makeArrayNonConfigurable(objects)
{
    objects.forEach((obj) =>
    {
        Object.keys(obj).forEach((key) =>
        {
            Object.defineProperty(obj, key, { configurable: false });
        });
    });
}

Nous n'allons qu'à un niveau de profondeur, car c'est tout ce que nous devons faire ; Vue ne cherchera pas dans les propriétés des objets imbriqués. Cependant, il semble examiner les objets à l'intérieur des tableaux sur les propriétés marquées comme non configurables, d'où le problème que vous rencontrez.

Maintenant, je vais vous dire qu'avec 10 000 objets à parcourir, cela tournera pendant quelques secondes environ la première fois qu'il traversera ce tableau ; cela doit être considéré comme une partie du coût de récupération de ces données. Franchement, je recommande de consommer ces objets avec une classe (j'utilise une classe qui renvoie un proxy à partir de son constructeur), puis de mettre ces objets en cache par un identifiant unique, si vous les chargez plus d'une fois au cours de la vie de l'application. envergure. Mais c'est vraiment un détail de conception, et pas exactement lié à votre problème.

J'ai trouvé la solution ici :
https://medium.com/@deadbeef404/tell -vue-js-to-stop-wasting-time-and-render-faster-7c3f7d2acaab

En bref, faites fonctionner l'utilitaire :

import Vue from 'vue';

const Observer = (new Vue()).$data.__ob__.constructor;

export function makeNonreactive(obj) {
    obj.__ob__ = new Observer({});
}

Salut @Mitoright. Juste pour référence, l'article décrit les entrailles de vue-nonreactive . La différence est de savoir si vous souhaitez utiliser le code en tant que plugin (via vue-nonreactive ) ou en tant que fonction d'assistance. De plus, vue-nonreactive est mentionné dans la mise à jour juste en haut de la description de ce problème.

Comme vue-devtool a de nouveau été mis à jour, https://github.com/vuejs/vue/issues/2637#issuecomment -434154442 fera à nouveau planter vue-devtool

Je suggère vue-nonreactive comme des solutions 😆

pour rendre __ob__ non énumérable, utilisez defineProperty

vue-free.js

import Vue from 'vue'

const Observer = new Vue().$data.__ob__.constructor

function prevent(val) {
    if (val) {
        // Set dummy observer on value
        Object.defineProperty(val, '__ob__', {
            value: new Observer({}),
            enumerable: false,
            configurable: true,
        })
    }

    return val
}

// vue global
Vue.VUE_FREE = prevent

// window
global.VUE_FREE = prevent

// default export
export default prevent

Figure je donnerais mes 2 cents et solution à ce sujet.

J'ai également eu des problèmes similaires en implémentant à la fois le concept Freeze et le faux Observateur. Mes données proviennent du serveur et constituent un scénario TreeNode récursif. Mon projet utilise également vuex dans ce cas, ce qui a ajouté une couche aux problèmes rencontrés. J'ai constamment des Maximum call stack size exceeded cause de la boucle vues object.keys. J'avais essayé de geler et de définir des données dans un faux VNode, mais aucun des deux ne semblait arrêter les problèmes de récursivité.

J'ai finalement pris du recul et j'ai enveloppé mes propriétés "non réactives" à l'aide du modèle de module révélateur classique

c'est la classe (ES6/typescript) mais la même chose peut également être appliquée en clair

import {  first, forEach } from 'lodash';

export class TreeNode {
    internalRefsInstance: () => { getParent: () => TreeNode; setParent: (parent: TreeNode) => void; getChildNodes: () => TreeNode[]; setChildNode: (childNode: TreeNode) => number; };

    get visitedDate(): string | undefined {
        return this._visitedDates.get(this.id) || undefined;
    }

    isSelectedTreeNode: boolean = false;
    showSubheader: boolean = false;
    showHelp: boolean = false;
    treeNodeIconName: string = 'empty';
    childTreeNodeCount: number = 0;

    constructor(public id: string,
        public componentName: string,
        private _visitedDates: Map<string, string>,
        public isActive: boolean = true,
        public nextFlow?: string,
        public prevFlow?: string,
        parent: TreeNode | undefined = undefined) {

        //invoke the internal refs module to create our static instance func to get the values from
        this.internalRefsInstance = this.nonReactiveModule();
        this.internalRefsInstance().setParent(parent);
    }
    nonReactiveModule = () => {
        let _parent: TreeNode | undefined = undefined;
        let _childNodes: TreeNode[] = [];
        const _getParent = (): TreeNode | undefined => {
            return _parent;
        };
        const _setParent = (parent: TreeNode | undefined): void => {
            _parent = parent;
        };
        const _getChildNodes = (): TreeNode[] => {
            return _childNodes || [];
        };
        const _setChildNode = (childNode: TreeNode): number => {
            if (!_childNodes) {
                _childNodes = [];
            }
            _childNodes.push(childNode);
            return _childNodes.length;
        };
        const returnObj = {
            getParent: _getParent,
            setParent: _setParent,
            getChildNodes: _getChildNodes,
            setChildNode: _setChildNode,
        };
        return () => { return returnObj; };
    }

    getParent(): TreeNode | undefined {
        return this.internalRefsInstance().getParent();
    }

    getChildNodes(): TreeNode[] {
        return this.internalRefsInstance().getChildNodes();
    }

    setChildNode(childFlow: TreeNode): void {
        this.childTreeNodeCount = this.internalRefsInstance().setChildNode(childFlow);
    }

    clone(parent: TreeNode | undefined = undefined): TreeNode {
        const newInstance = new TreeNode(this.id, this.componentName, this._visitedDates, this.isActive, this.nextFlow, this.prevFlow, parent);
        newInstance.showHelp = this.showHelp;
        newInstance.showSubheader = this.showSubheader;
        newInstance.isSelectedTreeNode = this.isSelectedTreeNode;
        forEach(this.getChildNodes(), (flow: TreeNode) => {
            newInstance.childTreeNodeCount = newInstance.internalRefsInstance().setChildNode(flow.clone(newInstance));
        });
        return newInstance;
    }

    setVisitedDates(visitedDates: Map<string, string>): void {
        this._visitedDates = visitedDates;
        forEach(this.getChildNodes(), (flow: TreeNode) => {
            flow.setVisitedDates(visitedDates);
        });
    }

    setAsSelected(setParent: boolean = true, setAllFirstChildren: boolean = true): void {
        this.isSelectedTreeNode = true;
        if (setAllFirstChildren) {
            const firstChildFlow = first(this.getChildNodes());
            if (firstChildFlow) {
                firstChildFlow.setAsSelected(false, true);
            }
        }

        if (setParent && this.getParent()) {
            this.getParent()!.setAsSelected(setParent);
        }
    }
    resetSelected(resetChildren: boolean = true): void {
        this.isSelectedTreeNode = false;
        if (resetChildren) {
            forEach(this.getChildNodes(), (flow: TreeNode) => {
                flow.resetSelected(resetChildren);
            });
        }
    }
}

Calculé n'a pas fonctionné dans mon cas car j'avais toujours besoin de l'objet complet et non d'un gestionnaire m'envoyant la modification sur le calcul. Au moins si je comprends comment un observateur profond fonctionnerait contre un résultat de sous-ensemble calculé.

Je pense que passer à un niveau supérieur serait de créer un assistant ou un décorateur non récursif injecté pour accélérer le processus. Tout retour d'information utile serait formidable.

on dirait que quelqu'un a déjà résolu ce problème,

J'espère que vous vérifierez tous les détails sur https://github.com/vuejs/vue/issues/4384

Salut, j'ai créé #10265 d'autant plus que je n'étais pas au courant de cet ancien problème.
Je me demande simplement quelle serait la solution conseillée pour être à l'épreuve du temps et rester compatible avec Vue lorsqu'il utilisera Proxy.
Utiliser Object.defineProperty avec configurable: false fonctionne bien (mais cela n'empêche pas une propriété ayant un setter/getter existant de devenir réactive).
Cette technique sera-t-elle toujours utilisable avec Vue 3 ?
Merci

@colin-guyon configurable: false ne fonctionnerait probablement _pas_, d'autant plus qu'il semble y avoir https://github.com/vuejs/vue-next/blob/d9c6ff372c10dde8b496ee32f2b9a246edf66a35/packages/reactivity/src/reactive.ts#L159 . Si cela se retrouve dans Vue 3.x, il y aurait une API officielle pour marquer un objet non réactif.

Notez que, tout comme le nouveau Vue.observable proposé, lorsqu'une propriété est définie sur un objet réactif, cette _nouvelle_ valeur ne serait pas entachée et resterait telle quelle. Au lieu de cela, le _getter_ renverrait un proxy réactif pour lui, en créant un s'il n'existe pas déjà dans le cache.

Une bonne nouvelle est que, tant que vous ne faites pas grand-chose sur _ce_ proxy réactif, tout devrait bien se passer. Si la mémoire ou l'interopérabilité est un problème, vous n'avez certainement pas à vous en soucier car quel que soit l'objet - une collection géante de données ou un objet étranger d'une bibliothèque dont le comportement est imprévisible si une réactivité y est appliquée, dites-vous — _rien_ à ce sujet n'est touché, après tout. En ce sens, Vue 3.x résout en fait de nombreux cas de coin où cette fonctionnalité serait autrement utile.

Pour le moment, le code vue-next semble exclure les touches de symboles d'être réactives, tout comme ce que fait la version actuelle de Vue.

considérez ceci https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true peut échapper aux procédures d'observation de vue.

Aujourd'hui, j'ai rencontré un cas dans lequel Vue observe une instance de carte de mapbox-gl, puis des choses étranges se sont produites, la carte s'éclaircit. Mais l'instance de carte doit être transmise entre les instances de vue. Après avoir ajouté map._isVue = true , le problème est résolu.

J'utilisais cette méthode jusqu'à ce que je sois mordu par les outils de développement de Vue, car il voit que _isVue est vrai et il pense que l'objet est une instance de composant Vue mais ce n'est pas le cas.

Le seul hack que j'ai vu sans effets secondaires sérieux semble être l'approche d'OP avec la bibliothèque vue-nonreactive .

Existe-t-il des solutions alternatives en V3 pour cela ?

@HunderlineK peu profondRef, peu profondReactive, markRaw

Cette page vous a été utile?
0 / 5 - 0 notes