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
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:
``` js
vm.$set('a', Vue.nonreactive({});
// anders als..
vm.$set('a', {
someKey: Vue.nonreactive({}),
});
```
``` js
// Error
Vue.nonreactive(vm.$data.a)
// gültig
Vue.nonreactive(_.clone(vm.$data.a));
```
Wird Object.freeze()
in Ihrem Fall nicht funktionieren? Es wird seit v1.0.18 unterstützt
data
überspringen müssen, verwenden Sie dafür Object.freeze()
;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.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:
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
Object.isExtensible
( Object.freeze
)?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:
Als Antwort auf einige Vorschläge:
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.
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
setval._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
setval._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
Hilfreichster Kommentar
data
überspringen müssen, verwenden Sie dafürObject.freeze()
;data
ablegen, um aufthis
darauf zuzugreifen. Wenn Sie es einfach anthis
imcreated()
Hook anhängen, wird es überhaupt nicht beobachtet.