Vue: [Feature] Möglichkeit, die Vue-Beobachtung zu deaktivieren

Erstellt am 7. Apr. 2016  ·  50Kommentare  ·  Quelle: vuejs/vue

Aktualisieren:
Wenn jemand diese Funktionalität benötigt, habe ich sie als vue-nonreactive mit den entsprechenden Ermahnungen und allem veröffentlicht.


Wir haben einige nicht-einfache Modelle, bei denen wir Vues Beobachtung und Gehen deaktivieren müssen. Ein Beispiel ist ein Ressourcenmodell, das Zugriff auf einen Cache hat, damit es verwandte Ressourcen nachschlagen kann. Dadurch werden alle Objekte im Cache überwacht (wahrscheinlich ineffizient) sowie einige zusätzliche Interaktionen mit anderem Code. Derzeit umgehen wir dies, indem wir einen Dummy-Observer auf den Cache setzen. Etwas Ähnliches wie...

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

Das funktioniert, aber

  • verlässt sich auf die Interna von vue
  • erfordert ein bereits beobachtetes Objekt, da wir die Klasse Observer direkt importieren können.

Vorschlag:
Fügen Sie eine offizielle Methode hinzu, um Vues Beobachtung/Gehen explizit zu deaktivieren. zB sowas wie...

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

Überlegungen:

  • Was sollte passieren, wenn ein Benutzer einen reaktiven Schlüsselpfad zu einem nicht reaktiven Objekt festlegt? Sollte vue den Benutzer warnen? z.B,

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

// anders als..
vm.$set('a', {
someKey: Vue.nonreactive({}),
});
```

  • Sollte ein bereits reaktives Objekt den Benutzer warnen, wenn versucht wird, nicht-reaktiv zu machen? z.B,

``` js
// Error
Vue.nonreactive(vm.$data.a)

// gültig
Vue.nonreactive(_.clone(vm.$data.a));
```

Hilfreichster Kommentar

  1. Wenn Sie die Beobachtung für ein Objekt/Array in data überspringen müssen, verwenden Sie dafür Object.freeze() ;
  2. Sie müssen kein Objekt in data ablegen, um auf this darauf zuzugreifen. Wenn Sie es einfach an this im created() Hook anhängen, wird es überhaupt nicht beobachtet.

Alle 50 Kommentare

Wird Object.freeze() in Ihrem Fall nicht funktionieren? Es wird seit v1.0.18 unterstützt

  1. Wenn Sie die Beobachtung für ein Objekt/Array in data überspringen müssen, verwenden Sie dafür Object.freeze() ;
  2. Sie müssen kein Objekt in data ablegen, um auf this darauf zuzugreifen. Wenn Sie es einfach an this im created() Hook anhängen, wird es überhaupt nicht beobachtet.
  • Object.freeze funktioniert hier nicht. Der Cache wird im Laufe der Zeit aktualisiert.
  • Die Hauptressource _ist_ reaktiv. Ich bin hauptsächlich daran interessiert, das verschachtelte Cache-Objekt nicht reaktiv zu machen.

Dann ist es vielleicht an der Zeit, Ihr Modelldesign zu überdenken. Warum diese Dinge unter etwas verschachteln, das beobachtet werden muss?

Weil der Cache verwendet wird, um verwandte Ressourcen dynamisch zu suchen.

zB könnten wir Author und Post Modelle haben. Das Autorenmodell definiert eine zu-viele-Beziehung namens posts zum Post-Modell. Dieser Cache enthält die Beziehungsdaten sowie die zugehörige Sammlung.

Der Aufruf von author.posts holt die Beiträge aus dem Cache.

Vue rät absichtlich davon ab, komplexe Objekte mit ihrem eigenen Mechanismus zur Zustandsänderung in data Vue-Instanz zu platzieren. Sie sollten nur reine Zustandsdaten als beobachtete Daten in Vue-Instanzen einfügen. Sie können diesen Status beliebig manipulieren, aber die Objekte, die für solche Manipulationen verantwortlich sind, sollten nicht Teil des Status der Vue-Instanz sein.

Zuerst eine klärende Frage - was genau meinst du mit reinem Zustand ? Wir haben zwei Arten von Staaten:

  • Modellstatus (permanent, Daten mit einem Speicher synchronisiert, z. B. ein Todo)
  • vue-Status (temporär, Daten, die das Ansichtsverhalten steuern, z. B. die Aufgabenliste minimieren/anzeigen)

Aber wie auch immer:
Das ist fair. Das Modell ist definitiv „komplex“, daher widerspricht diese Anfrage den aktuellen Best Practices. Außerdem ist mein erstes Beispiel nicht sehr gut - es hat nur funktioniert, um die Beobachtung zu deaktivieren. Dies ist repräsentativer für unser aktuelles Setup mit möglicher Verwendung:

<!-- 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,
    },
    ...
});

Grundsätzlich haben wir einen übergeordneten Vue, der dafür verantwortlich ist, wiederverwendbare Komponenten in einem Layout zu platzieren und Daten an die zugehörigen Komponenten abzurufen/zu binden. Die untergeordneten Komponenten rufen ihre eigenen Daten nicht ab, da die Daten in verschiedenen Kontexten unterschiedlich sind. zB eine Liste der Kommentare eines _Benutzers_ vs. eine Liste der Kommentare eines _Beitrags_.

Das Modell ist ziemlich "dumm", mit der Ausnahme, dass verwandte Objekte nicht verschachtelt sind ( {post: {author: {}, comments: []}} ), sondern stattdessen aus dem Cache nachgeschlagen werden. zB post.comments[2].author kann genau dasselbe Objekt wie post.author . Anstatt also mehrere Kopien des Autorenobjekts zu haben, haben wir nur eine, die aus dem Cache nachgeschlagen wird. Es gibt keine Mutation im oben genannten - alle Daten werden beim ersten Abruf erstellt.

Außerdem ist die Anfrage nicht mehr relevant, aber eine Alternative könnte darin bestehen, 'private' Objektmember nicht zu beobachten. Dies können Mitglieder mit einem führenden einfachen oder vielleicht doppelten Unterstrich sein. Die Kehrseite dieses Ansatzes ist, dass er den Wandel brechen würde.

Wenn jemand diese Funktionalität benötigt, habe ich sie als vue-nonreactive mit den entsprechenden Ermahnungen und allem veröffentlicht.

@rpkilby danke fürs teilen!

@rpkilby Auf eine Weise kopiere ich ein Objekt und entferne das Observable/Reaktivität

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

Wirklich nützlich, da ich ein Array von "Zuständen" behalten und ein Zustandsverlaufsobjekt in vuex implementieren möchte.

Bearbeiten : Diese Lösung war spezifisch für meinen Fall. Ich hatte ein Objekt, bei dem ich zu einem bestimmten Zeitpunkt nur eine Kopie der Eigenschaftswerte benötigte. Referenzen, dynamische Updates etc. waren mir egal.

Im Moment ist das Einfrieren des Objekts keine langfristige Lösung. [Vue-nonreactive] hat Vue als Abhängigkeit, die übertrieben ist, wenn es darum geht, etwas so unkompliziert zu tun. Wäre ein einfaches Einchecken des Codes wie instance.__ob__ !== false nicht ausreichend? Dies würde es Bibliotheken ermöglichen, sicherzustellen, dass Dinge wie Caches nicht beobachtet werden.

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

Dies ist hauptsächlich ein Problem für Bibliotheken, die in Vue-Anwendungen verwendet werden (zumindest für mich).

Wie kann man Vue anweisen, nur die einstufige Datentiefe zu beobachten (defineProperty to)?

Mein Fall ist, ich möchte, dass Vue benachrichtigt wird, wenn sich data.curObj geändert hat,
aber nicht mit curObj.position , curObj.rotation usw.

Früher habe ich Object.freeze , aber in diesem Fall würde ein Fehler auftreten, wenn Three.js versucht, dem Objekt Werte zuzuweisen.

Muss ich folgendes tun?
(eigentlich habe ich es an einem anderen ähnlichen Ort gemacht)

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. Können wir eine andere Bedingung für den Benutzer hinzufügen, um die Beobachtung zu deaktivieren, als nur Object.isExtensible ( Object.freeze )?
  2. Können wir die Vue.isPlainObject-Erkennung verbessern?

Sie können Destrukturierung verwenden

var newObj = { ...obj };

Dies sollte es beheben. Dadurch wird die isPlainObject-Methode false zurückgeben.

/**
 * 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]"

Hallo zusammen, ein Punkt, der in den Antworten verloren gegangen ist, ist, dass die Daten im ursprünglichen Kommentar kein reiner Zustand sind . In meinem Fall habe ich eine Modellinstanz mit einem privaten Verweis auf einen Beziehungs-Lookup-Cache. Ein article kann beispielsweise seine author oder comments . Wenn ich article.author aufrufe, ist dies eine dynamische Eigenschaftssuche in diesem Beziehungscache und kein einfacher Attributzugriff. Ein paar Dinge zu beachten:

  • Der Cache ist kein reiner Zustand, daher möchte ich nicht, dass er von Vue beobachtet wird, da er Ressourcenverschwendung ist.
  • Der Cache kann nicht verworfen werden, da ich noch eine Referenz benötige, um diese dynamischen Lookups/Updates durchzuführen.
  • Der Cache ist effektiv ein Singleton und kann extern aktualisiert werden. Die App wird entsprechend aktualisiert.

Als Antwort auf einige Vorschläge:

  • Die Verwendung von JSON-Stringify/Parse oder Objektdestrukturierung ist nicht ausreichend, da dies sowohl das Objekt und den Cache dupliziert als auch die Referenz auf den ursprünglichen Cache bricht. Durch das Aktualisieren der ursprünglichen Cache-Referenz werden die Instanzen der App nicht mehr aktualisiert. Ohne diese dynamischen Lookups und Updates ist der Cache im Grunde sinnlos, und es wäre besser, die zugehörigen Objekte zu einfachen Attributen auf dem Originalmodell zu machen. Es ist auch erwähnenswert, dass diese Vorschläge die Instanzmethoden dieser Objekte unterbrechen.
  • Die Vorschläge von @Mechazawa sind sinnvoll, wenn Sie die

Mein einziger Kritikpunkt:

Vue-nonreactive hat Vue als Abhängigkeit, die übertrieben ist, wenn es darum geht, etwas so Direktes zu tun.

Ich bin mir nicht sicher, warum das schlecht sein sollte? Sie verwenden Vue bereits in Ihrer Anwendung und das Plugin ist spezifisch für Vue. Wenn ich mich nicht irre, sind die meisten Build-Tools intelligent genug, um keine Bundles mit doppelten Abhängigkeiten zu erstellen. Unabhängig davon ist dies nicht richtig . Es gibt eine Dev-Abhängigkeit, aber keine Laufzeit-Abhängigkeit.


Wie auch immer, ich freue mich zu sehen, dass dieser Beitrag einiges Interesse geweckt hat, und ich bin sicher, dass einige dieser anderen Lösungen in einer Vielzahl anderer Fälle funktionieren werden. Ich möchte nur die Anforderungen aus meinem ursprünglichen Kommentar hervorheben und warum die Vorschläge für diesen Fall keine geeigneten Alternativen sind.

Ich bin kürzlich darauf gestoßen und habe festgestellt, dass es eine viel einfachere Möglichkeit gibt, die Beobachtungslogik von Vue kurzzuschließen: _Definieren Sie eine Eigenschaft als nicht konfigurierbar._

Hintergrund

In meiner Anwendung muss ich mit einer Drittanbieterbibliothek (OpenLayers) arbeiten, die Klasseninstanzen erstellt, die Daten enthalten, und kein Reaktivitätssystem unterstützt. Der Versuch, einen zu schnüren, hat so viele Kopfschmerzen verursacht, lass es mich dir sagen. Die einzige praktikable Lösung für eine groß angelegte Anwendung, die diese Bibliothek verwendet, besteht also darin, OpenLayers die Dinge so zu überlassen, wie sie es wollen, und für mich, Vue mit diesen schrecklich verschachtelten, übertriebenen Doom-Objekten besser spielen zu lassen. Bevor dieses Problem gefunden wurde, verbrauchte meine Anwendung ungefähr 3 GB RAM (in unserem größten Datensatz), was alles dadurch verursacht wurde, dass Vue diese Objekte reaktiv machte. Außerdem war es beim Laden sehr langsam. Ich habe Vue-nonreactive ausprobiert und es hat geholfen, aber nur, um uns auf etwa 1 Gig zu reduzieren. Vor der Verwendung von Vue lag die Anwendung bei etwa 350 MB.

Lösung

Alles, was Sie nicht reagieren möchten, markieren Sie einfach als configurable: false . Es ist so einfach wie:

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

(Dies verhindert, dass die Eigenschaft nested und alle ihre Eigenschaften beobachtet werden.)

Das ist es! Keine Vue-Abhängigkeit und wohl nicht einmal falsch. Damit ist meine Anwendung mit unserem größten Datensatz auf 200 MB reduziert. Es ist einfach und leicht und erfordert nur eine Änderung der Dokumentation von Vues Seite, um es zu einer "offiziellen" Methode zu machen, etwas nicht reaktiv zu machen.

Interessant - scheint definitiv eine praktikable Alternative zu sein.

Gibt es eine Möglichkeit, die Beobachtung vorübergehend zu unterbrechen und später wieder aufzuheben?

Ich habe einen Prop-Watcher, in dem ich ein riesiges Objekt aktualisiere, bei dem ich das DOM-Update nicht erst auslösen möchte, wenn die gesamte Datenaufbereitung abgeschlossen ist.

@intijk Nicht ganz. Sehen Sie, es hängt davon ab, was Sie zu tun versuchen; Vue muss schließlich Ihren Status anwenden, also hilft es nicht viel, einfach zu pausieren, während er berechnet wird. Wenn Sie stattdessen versuchen, Zwischenzustände zu überspringen und nur den Endzustand anzuwenden, beginnen Sie einfach mit einem neuen Objekt und weisen Sie dieses Objekt am Ende zu.

Beispiel (Psuedocode):

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

  // Do intermediate state updates

  this.myState = state;
}

(Es gelten die normalen Vue-Vorbehalte bezüglich der Objektreaktivität.)

Meine Empfehlung wäre, den obigen configurable Trick zu verwenden, um die Abschnitte Ihres großen Objekts zu überspringen, die nicht reaktiv sein müssen. Wenn alles reaktiv sein muss, empfehle ich etwas wie vuex .

@Morgul Ich habe diesen Trick schon lange benutzt, aber Tatsache ist, dass ich diesen Trick nicht mehr verwenden möchte.
In meinem Fall ist das Datenobjekt jetzt ziemlich groß, reicht von 2M bis 100M, das Ausführen einer tiefen Kopie für ein solches Objekt ist ziemlich schmerzhaft.

@intijk Das klingt unglaublich komplex für etwas, an das man Vue binden kann. Was ist hier der Anwendungsfall?

@Morgul
Ich glaube nicht, dass der Fall komplex ist, der Fall selbst ist einfach, nur die Daten sind ziemlich groß. Jedes Mal lädt das Netzwerk eine indizierte Visualisierungsprotokolldatei, und ich habe eine Visualisierungskomponente, um sie anzuzeigen.

Hat jemand Gedanken zum Definieren eines nicht-reaktiven Felds in einer berechneten Eigenschaft? Meine erste Idee hängt von der Nichtreaktivität der Zuweisung zum Array ab ...

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;

Aber das ist nicht gerade selbstdokumentierend :-)

Hallo Leute. Ich habe gerade dieses Problem gefunden und festgestellt, dass ich vor einem Problem stehe, das dem von rpkilby ziemlich ähnlich ist: Mein Projekt erstellt eine Reihe von Vue Virtual DOMs (oder Vnode genannt) aus einem JSON-Objekt. Ich werde dieses JSON-Objekt verwenden, um eine Android-App zu erstellen. Wie auch immer, dieses JSON-Objekt kann eine große Größe haben, und wenn ich dieses JSON in Vue verwende, wird es von Vue beobachtet. Ich habe es auf die Art von rpkilby und Morgul versucht, aber es funktioniert nicht. (Übrigens bin ich in einem Team, während einige Leute mit Vue vielleicht nicht recht vertraut sind, und sie werden wahrscheinlich den JSON beobachten, und meine Vue-Version ist 2.5.16 ). Ich frage mich, ob wir dies in Vue-Traverse tun können:
Funktion _traverse (val, gesehen) {
var i, Schlüssel;
var isA = Array.isArray(val);
if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode
|| (val && val['__vueNonReactive__'])) {
Rückkehr
}
...
Wie Sie sehen können, füge ich das "val && val['__vueNonReactive__']" hinzu. Dann ändere ich mein JSON-Objekt so, dass es "__vueNonReactive__ = true" mit dem Stammknoten von JSON hat, und dies löst mein Problem.
Ich frage mich, ob dies zu Problemen führen kann? Und wird dies als eine neue Funktion in Vue betrachtet, die es dem Entwickler ermöglicht, ein Objekt so zu konfigurieren, dass es von Vue beobachtet wird oder nicht, indem eine Eigenschaft des Objekts konfiguriert wird? (Object.freeze kann das Objekt in ein unveränderliches Objekt ändern, so dass es kann nicht in jede Situation passen)

Betrachten Sie dies https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true kann Vue-Beobachtungsprozeduren entkommen.

Heute traf ich auf einen Fall, bei dem Vue eine Karteninstanz von mapbox-gl beobachtete, dann passierten seltsame Dinge, die Karte wurde heller. Die Karteninstanz muss jedoch zwischen vue-Instanzen übergeben werden. Nachdem ich map._isVue = true hinzugefügt habe, ist das Problem behoben.

+1, um das offiziell zu unterstützen. Ich verwende ein großes Objekt, das keine Reaktivität in einer Komponente benötigt, und das Deaktivieren nicht verwendeter Reaktivität reduzierte die Speicherobjektgröße von 800 MB auf 43 MB
Ich verwende die @magicdawn- Lösung für Kompatibilitätsprobleme, aber @Mechazawa ist hier meiner Meinung nach die beste Lösung.
Die Lösung des Setzens von __ob__ configurable auf false funktioniert, lässt aber Vue abstürzen, wenn es versucht, das echte __ob__ .

Ich habe ein Vue-Plugin erstellt, das es ermöglicht, Vue-Variablen nicht reaktiv zu machen (es verwendet den beforeCreate-Hook).

Dies ist sauberer als vue-nonreactive - @rpkilby , bitte sehen Sie sich diesen Kommentar an - Ihre Lösung wird für die nächste Version von Vue nicht funktionieren.


Bitte sehen Sie sich Vue-Static an, um eine Möglichkeit zu finden, Variablen nicht reaktiv zu machen.

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

Hallo @samuelantonioli - es sieht so aus, als ob vue-static etwas etwas anderes macht und die Reaktivität für ein gesamtes Objekt deaktiviert. Im Gegensatz dazu ist vue-nonreactive in der Lage, die Beobachtung für eine einzelne Eigenschaft zu deaktivieren, während der Rest des Objekts reaktiv bleibt.

Das heißt, es sieht so aus, als ob die Absichten etwas anders sind. Die statischen Eigenschaften beobachten keine Änderungen, sondern sollen in eine Vorlage gerendert werden. Nicht-reaktive Eigenschaften sollen weder beobachtet noch gerendert werden.

zB hat meine Modellinstanz einen Verweis auf einen Objektcache, der verwandte Objektsuchvorgänge ermöglicht. Ich möchte das instance und das zugehörige instance.author beobachten/rendern, aber nicht das instance._cache .

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

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

So oder so, danke für die Hinweise. Ich muss mir ansehen, wie die nächste Version Proxys verwendet, und sehen, ob es eine Möglichkeit gibt, die Beobachter-/Proxy-Erstellung zu täuschen.

@LinusBorg - Ich sehe keinen experimentellen Zweig. Wo wird für die nächste Version entwickelt?

Wir sind noch in der Konzept-/Erfahrungsphase und haben noch keinen Zweig veröffentlicht. Die ernsthafte Arbeit daran wird nicht beginnen, bevor wir das Update 2.6 vor der Tür haben, was selbst einige Arbeit erfordern wird, nachdem wir hoffentlich sehr bald vue-cli 3.0 veröffentlicht haben

Danke für das Update.

Ich bin mir also nicht sicher, ob das Problem nach der Einführung der ES6-Proxys im gleichen Umfang besteht. In meiner Anwendung nutze ich sie intensiv, und ihr Overhead im Vergleich zum Overhead der aktuellen Beobachtung von vue scheint viel geringer zu sein. Der Teufel steckt im Detail, vermute ich.

Das Problem, das ich mit Vue-Static ist, dass es sich überflüssig anfühlt. Ich kann mein Objekt in einem JS-Modul erstellen, es importieren und dann seinen Wert von einer berechneten Funktion zurückgeben; Da er nicht beachtet wird, spielt es keine Rolle, dass der Wert der berechneten Funktion nie neu berechnet wird. Und das ist eine viel bessere Trennung von Bedenken, da ich in meinen Vue-Komponenten keine Geschäftslogik-Typen mache.

Auf jeden Fall ist mein Trick, die Eigenschaften als nicht konfigurierbar festzulegen, immer noch die am wenigsten invasive und am wenigsten Vue-abhängige Möglichkeit, das Problem zu lösen. Es gibt auch keinen Grund anzunehmen, dass es mit den ES Proxies brechen würde; Sie möchten wahrscheinlich immer noch keine nicht konfigurierbaren Eigenschaften beobachten. Ich könnte völlig falsch liegen, aber wir _wissen_, dass __ob__ weggeht... wir wissen nicht, ob die Prüfung auf eine Eigenschaft konfigurierbar ist.

Außerdem funktioniert es seit mehr als 8 Monaten wie ein Champion in unserem Produktionscode. ;) (Wir haben einen ähnlichen Problemraum wie @samuelantonioli; wir haben eine OpenLayers-Karte, mit der wir in Vue arbeiten müssen, ohne dass Vue unseren Speicher auf 2,4 Gigs erhöht...)

Ich stimme zu, wenn Sie ein anderes Muster verwenden, z. B. Module importieren und berechnete Eigenschaften verwenden, benötigen Sie Vue-Static nicht . Ich brauchte nur ein Muster, das ich meinen Mitarbeitern beibringen konnte, das für sie leicht zu verstehen und zu verwenden ist. Das Muster für das Importieren von Modulen und die Verwendung von berechneten Eigenschaften ist IMO nicht so klar.


Ein bisschen OT: Ich bin mir ziemlich sicher, dass ES6-Proxies ein guter Weg sind, aber ich habe einige Bedenken hinsichtlich der Browserkompatibilität (IE11 und darunter werden nicht unterstützt). Ich bin daran interessiert, ob es eine Kompatibilitätsebene / eine Art Polyfill geben wird, damit wir Vue für Projekte mit strengeren Browseranforderungen verwenden können.

Wie kann man Vue anweisen, nur die einstufige Datentiefe zu beobachten (defineProperty to)?

+1 für diese Idee wird es einfacher sein, die Daten für die Verwendung von Vue mit einer externen Grafikbibliothek zu strukturieren (die normalerweise über mehrere Ebenen verschachtelte große Objekte verfügt).

Wie wäre es, wenn Sie nur einige Eigenschaften als reaktiv angeben?

Vielleicht übersehe ich hier etwas Offensichtliches, aber using this.propertyName = { /* Etwas Großes hier */ };
im mount()-Hook ist keine Lösung für nicht beobachtete Eigenschaften?

Hallo @vlahanas. Siehe https://github.com/vuejs/vue/issues/2637#issuecomment -403630456.

set _isVue führt zum Absturz von vue-devtool, benutze stattdessen diese Funktion

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
}

Es hat sich im Rauschen verloren, aber das Markieren einer Eigenschaft als nicht konfigurierbar scheint wirklich die Lösung zu sein.

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

Wirklich schön, wenn ich ein THREE.Scene herumgeben muss, aber nicht möchte, dass der gesamte Szenengraph buchstäblich ein Durcheinander von Observablen wird. Und dann kann ich immer noch das Originalobjekt herumreichen und es kann darauf basierend reagieren. Perfekt!

Immer noch ein Problem.
Ich habe Objekte, die viele verschachtelte Ebenen von Eigenschaften enthalten, die ich nicht reaktiv halten möchte.
1

Auch wenn ich benutze

Es hat sich im Rauschen verloren, aber das Markieren einer Eigenschaft als nicht konfigurierbar scheint wirklich die Lösung zu sein.

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

Wirklich schön, wenn ich ein THREE.Scene herumgeben muss, aber nicht möchte, dass der gesamte Szenengraph buchstäblich ein Durcheinander von Observablen wird. Und dann kann ich immer noch das Originalobjekt herumreichen und es kann darauf basierend reagieren. Perfekt!

oder

Betrachten Sie dies https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true kann Vue-Beobachtungsprozeduren entkommen.

Heute traf ich auf einen Fall, bei dem Vue eine Karteninstanz von mapbox-gl beobachtete, dann passierten seltsame Dinge, die Karte wurde heller. Die Karteninstanz muss jedoch zwischen vue-Instanzen übergeben werden. Nachdem ich map._isVue = true hinzugefügt habe, ist das Problem behoben.

Eigenschaften in verschachtelten Objekten werden reaktiv.

Ich habe es rekursiv versucht, aber Maximum call stack size exceeded , und es verursacht mehr Verzögerungen.

@Mitroright Sie müssen dies rekursiv tun, aber ich vermute, dass Ihr Ansatz möglicherweise nicht ganz richtig war.

Das Problem hierbei ist, wie Vue mit Arrays umgeht; Das einfache Markieren der geoJsonData Eigenschaft als nicht konfigurierbar wird wahrscheinlich nicht funktionieren (ich hatte damit Probleme, habe mich aber nie mit dem "Warum" befasst.)

Versuchen Sie so etwas:

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

Wir gehen nur eine Ebene tiefer, denn das ist alles, was wir tun müssen; Vue sucht nicht in verschachtelten Objekteigenschaften. Es scheint jedoch Objekte innerhalb von Arrays auf Eigenschaften zu untersuchen, die als nicht konfigurierbar markiert sind, daher das Problem, auf das Sie stoßen.

Nun, ich werde Ihnen sagen, dass dies bei 10.000 zu durchlaufenden Objekten beim ersten Durchlaufen dieses Arrays für einige Sekunden oder so durcheinander geht; dies sollte nur als Teil der Kosten für das Abrufen dieser Daten angesehen werden. Ehrlich gesagt empfehle ich, diese Objekte mit einer Klasse zu konsumieren (ich verwende eine Klasse, die einen Proxy von ihrem Konstruktor zurückgibt) und diese Objekte dann nach einer eindeutigen ID zwischenzuspeichern, wenn Sie sie während der Lebensdauer der Anwendung mehr als einmal laden. Spanne. Aber das ist wirklich ein Designdetail und nicht genau mit Ihrem Problem verbunden.

Lösung habe ich hier gefunden:
https://medium.com/@deadbeef404/tell -vue-js-to-stop-wasting-time-and-render-faster-7c3f7d2acaab

Kurz gesagt, machen Sie die Utility-Funktion:

import Vue from 'vue';

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

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

Hallo @Mitoright. Nur als Referenz beschreibt der Artikel die Eingeweide von vue-nonreactive . Der Unterschied besteht darin, ob Sie den Code als Plugin (über vue-nonreactive ) oder als Hilfsfunktion verwenden möchten. Außerdem wird vue-nonreactive im Update ganz oben in der Beschreibung dieses Problems erwähnt.

Da vue-devtool wieder aktualisiert wurde, wird https://github.com/vuejs/vue/issues/2637#issuecomment -434154442 vue-devtool erneut zum Absturz bringen

Ich schlage vue-nonreactive wie Lösungen vor 😆

um __ob__ nicht aufzählbar zu machen, verwende 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

Ich würde meine 2 Cent und Lösung dafür geben.

Ich hatte auch ähnliche Probleme bei der Implementierung des Freeze-Konzepts und des gefälschten Observer. Meine Daten kommen vom Server und sind ein rekursives TreeNode-Szenario. Mein Projekt verwendet in diesem Fall auch vuex, was den gesehenen Problemen eine Ebene hinzugefügt hat. Ich habe ständig Maximum call stack size exceeded wegen der Schleife vues object.keys bekommen. Ich hatte das Einfrieren und Einstellen von Daten in einem gefälschten VNode versucht, aber keines schien die Rekursionsprobleme zu stoppen.

Ich trat schließlich zurück und verpackte meine "nicht reaktiven" Eigenschaften mit dem klassischen Revealing Module Pattern

dies ist die Klasse (ES6/typescript), aber das gleiche kann auch im Klartext angewendet werden

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

Berechnet hat in meinem Fall nicht funktioniert, weil ich immer noch das vollständige Objekt benötigte und keinen Handler, der mir die Änderung auf dem berechneten schickt. Zumindest, wenn ich verstehe, wie ein Deep Watcher gegen ein berechnetes Teilergebnis arbeiten würde.

Ich denke, dies auf eine nächste Stufe zu heben, wäre, einen injizierten nichtrekursiven Helfer oder Dekorator zu erstellen, um den Prozess zu beschleunigen. Jedes nützliche Feedback wäre toll.

scheint das Problem schon gelöst zu haben,

Ich hoffe, Sie überprüfen alle Details unter https://github.com/vuejs/vue/issues/4384

Hallo, ich habe #10265 erstellt, insbesondere weil mir dieses frühere Problem nicht bewusst war.
Ich frage mich nur, was die empfohlene Lösung wäre, um zukunftssicher zu sein und mit Vue kompatibel zu bleiben, wenn es Proxy verwendet.
Die Verwendung von Object.defineProperty mit configurable: false funktioniert gut (aber es verhindert nicht, dass eine Eigenschaft mit einem vorhandenen Setter/Getter reaktiv wird).
Wird diese Technik mit Vue 3 noch verwendbar sein?
Danke

@colin-guyon configurable: false würde wahrscheinlich _nicht_ funktionieren, zumal es https://github.com/vuejs/vue-next/blob/d9c6ff372c10dde8b496ee32f2b9a246edf66a35/packages/reactivity/src/reactive ..ts configurable: false L159 zu geben scheint . Wenn es in Vue 3.x landet, gäbe es eine offizielle API, um ein Objekt als nicht reaktiv zu markieren.

Beachten Sie, dass genau wie beim neuen vorgeschlagenen Vue.observable , wenn eine Eigenschaft für ein reaktives Objekt festgelegt wird, dieser _neue_ Wert nicht verfälscht und einfach unverändert belassen würde. Stattdessen würde der _getter_ einen reaktiven Proxy dafür zurückgeben und einen erstellen, wenn er noch nicht im Cache vorhanden ist.

Eine glückliche Nachricht ist, dass es Ihnen wahrscheinlich gut gehen sollte, solange Sie nicht viel mit _diesem_ reaktivem Proxy tun. Wenn Speicherfresser oder Interoperabilität ein Problem darstellen, müssen Sie sich sicherlich keine Sorgen machen, denn was auch immer das Objekt ist – eine riesige Sammlung von Daten oder ein fremdes Objekt aus einer Bibliothek, dessen Verhalten unvorhersehbar ist, wenn Reaktivität darauf angewendet wird, sagen Sie — _nichts_ daran wird schließlich angerührt. In diesem Sinne löst Vue 3.x tatsächlich viele Eckfälle, in denen diese Funktion ansonsten nützlich wäre.

Im Moment scheint der vue-next Code Symbolschlüssel von der Reaktion auszuschließen, genau wie dies bei der aktuellen Version von Vue der Fall ist.

Betrachten Sie dies https://github.com/vuejs/vue/blob/v2.5.16/src/core/observer/index.js#L121
set val._isVue = true kann Vue-Beobachtungsprozeduren entkommen.

Heute traf ich auf einen Fall, bei dem Vue eine Karteninstanz von mapbox-gl beobachtete, dann passierten seltsame Dinge, die Karte wurde heller. Die Karteninstanz muss jedoch zwischen vue-Instanzen übergeben werden. Nachdem ich map._isVue = true hinzugefügt habe, ist das Problem behoben.

Ich habe diese Methode verwendet, bis ich von den Vue-Entwicklertools gebissen wurde, die abgehört haben, weil sie sehen, dass _isVue wahr ist und das Objekt denkt, dass es sich um eine Vue-Komponenteninstanz handelt, dies jedoch nicht der Fall ist.

Der einzige Hack, den ich ohne ernsthafte Nebenwirkungen gesehen habe, scheint der Ansatz von OP mit der vue-nonreactive Bibliothek zu sein.

Gibt es dafür alternative Lösungen in V3?

@HunderlineK shallowRef, shallowReactive, markRaw

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen