2017-06-19 3 views
2

Ich bin neu in Vue und versuche, einen Komponentenwert an eine Eigenschaft eines exportierten Objekts zu binden. Der Anfangswert ist richtig eingestellt, aber nicht reaktiv. Ich bin mir nicht sicher, ob ich die richtige Terminologie bin mit, aber die entsprechenden Abschnitte sindVueJS reaktive Bindung an Modulexport

// Settings.js 
export const settings = { showOverlay: true } 

// Overlay.vue 
<template> 
    <div v-show="enabled"> Some stuff </div> 
</template> 

<script> 
import { settings } from "../js/Settings.js"; 
export default { 
    data() { 
    return { 
     enabled: settings.showOverlay 
    }; 
    } 
}; 
</script> 

Nun, ich weiß, dass das exportierte Objekt (settings) ist eine Nur-Lese-Sicht auf die Objekt, weil Module so funktionieren, also kann Vue wahrscheinlich nicht darauf zugreifen. Die Sache ist, ich möchte, dass die Einstellung diesem Einstellungsdienst "gehört", der dafür verantwortlich ist, die Werte zwischen den Seitenladungen beizubehalten, aber ich habe nicht das Gefühl, dass der Dienst wissen sollte, dass die Komponente ein Video ansehen möchte Wert und kümmern sich darum, manuell Updates auf der Komponente auszulösen, wenn sich der Wert ändert - ich missverstand wahrscheinlich nur das Muster, das ich für solche Fälle verwenden sollte.

Dies wird mit Webpack/babel erstellt, wenn das einen Unterschied macht.

+1

Ich glaube, Sie für einige Statusbehaftung suchen, ich einen Blick auf [vuex] nehmen würde (http://vuex.vuejs.org/en/intro.html) – dops

+0

Einer der Gründe, begann ich mit Vue ist, weil es "leicht" sein soll und du es in der Lage sein solltest, es ein wenig zu adoptieren.Ich wollte meinen gesamten Anwendungszustand noch nicht auf einen Vue-spezifischen Speicher reduzieren (und ich denke, ich würde mehr Zeit darauf verwenden, zu einem globalen Nachrichtenbusmodell zu wechseln, als ich mit Vue sparen würde). Gibt es nicht einen Weg, "nur ein bisschen Vue" zu benutzen? – Coderer

+0

Ich denke, das umfassendere Problem könnte sein, dass ich Objekte beobachten möchte, die Eigenschaften haben, die bereits einen Getter/Setter verwenden - ich glaube, dass exportierte Module unter der Haube eine schreibgeschützte Ansicht bieten, indem sie die tatsächlichen Eigenschaften verstecken und aussetzen nur ein generierter Getter. Also, wenn ich ein Objekt habe und einen Getter beobachten möchte, gibt es dann ein gutes Muster dafür? – Coderer

Antwort

2

Ich fühle mich im Moment ein bisschen verlegen. Ich bin ein kleines Kaninchenloch hinuntergegangen, basierend auf einer Syntax, die ich in deiner Frage gesehen habe und die zu einer ganzen Reihe unnötiger Wendungen geführt hat. Die Syntax war:

data() { 
    return { 
    enabled: settings.showOverlay 
    }; 
} 

Welche, aus irgendeinem Grund, ich interpretiert als „gut sicher, wann immer enabled Änderungen, settings.showOverlay wird sich ändern, weil Vue reaktive ist“.

Ja, nein.

In diesem Code ist settings.showOverlay nur der Anfangswert für die enabled Eigenschaft. Die Eigenschaft enabled wird reaktiv sein, aber in keiner Weise werden Werte an das Einstellungsobjekt übergeben. Grundsätzlich gibt die Datenfunktion ein Objekt mit einer aktivierten Eigenschaft zurück, die einen Anfangswert von settings.showOverlay hat und dann das Objekt in ein reaktives Objekt verwandelt.

Wenn Sie möchten, dass die in Vue vorgenommenen Änderungen an Ihr Einstellungsobjekt übergeben werden, müssen Sie nur das Einstellungsobjekt auf dem Datenobjekt von Vue verfügbar machen.

data() { 
    return { 
    settings, 
    }; 
} 

Jetzt wenn Sie Code wie

<div v-show="settings.showOverlay"> Some stuff </div> 
<button @click="settings.showOverlay= !settings.showOverlay"></button> 

settings.showOverlay wird nicht nur in der Vue reaktiv sein, aber in dem settings Objekt. Keine Notwendigkeit für einen der Reifen, die ich unten durchsprang (/ Facepalm).

FWIW Ich glaube, einige der Links, die ich in den Kommentaren erwähnte, beziehen sich auf das Datenobjekt selbst. Das Datenobjekt muss ein einfaches JavaScript-Objekt sein, nicht unbedingt alle Eigenschaften.

Mit anderen Worten, in

data() { 
    return something 
} 

something muss eine einfache JavaScript-Objekt sein.

Original-Antwort

Ich habe in meinem Vue apps dies in ein paar Arten. In meiner ersten App wollte ich das gleiche tun, die Einstellungen in einem externen Modul speichern, das die Einstellungen beibehalten und diese Einstellungen auf meinem Vue verfügbar machen konnte. Ich habe am Ende eine Klasse geschrieben, die so aussieht.

class Settings { 
    constructor(){ 
    // read settings from persisted solution 
    } 
    get(key){ 
    // return "key" from settings 
    } 
    set(key){ 
    // set "key" in settings 
    } 
    save(){ 
    // save settings to persisted solution 
    } 
} 

export default Settings 

Und dann verwendet, dass in meinem Vue so.

import Settings from "./settings" 

new Vue({ 
    data:{ 
    someSetting: Settings.get("someSetting") 
    } 
}) 

Und dann einen Punkt später, trigger set() und save(). Dieser Punkt war für mich immer dann, wenn eine Routenänderung ausgelöst wurde. Ich würde alle Einstellungen auf das Objekt "Einstellungen" zurücksetzen und dann speichern.

Es klingt wie das, was Sie haben, ist, dass Sie ein Objekt exportieren, das Getter/Setter-Eigenschaften möglicherweise so etwas hat.

export const settings = { 
    overlay: stored.showOverlay, 
    get showOverlay(){ 
    return this.overlay 
    }, 
    set showOverlay(v){ 
    this.overlay = v 
    } 
} 

Wo Sie vielleicht auslösen ein speichern, wenn set ausgelöst wird. Ich mag diese Idee besser als die oben beschriebene Lösung. Aber es zur Arbeit zu bringen ist ein bisschen mehr Arbeit. Zuerst habe ich versucht, eine berechnete zu verwenden.

new Vue({ 
    computed:{ 
    showOverlay: { 
     get(){ return settings.showOverlay } 
     set(v) { settings.showOverlay = v } 
    } 
    } 
}) 

Aber das funktioniert nicht ganz, weil es keine Änderungen an der Vue widerspiegelt. Das macht Sinn, weil Vue den Wert nicht wirklich kennt. Das Hinzufügen einer $forceUpdate zum Setter funktioniert auch nicht, ich erwarte aufgrund der Caching-Natur der berechneten Werte. Mit einer in Kombination mit einer Dateneigenschaft berechneten Funktion funktioniert jedoch.

new Vue({ 
    data(){ 
    return { 
     showOverlay_internal: settings.showOverlay 
    } 
    }, 
    computed:{ 
    showOverlay: { 
     get(){ return this.showOverlay_internal } 
     set(v) { 
     settings.showOverlay = v 
     this.showOverlayInternal = v 
     } 
    } 
    } 
}) 

dass Änderungen sowohl den Zustand des Vue und löst die Änderung der Einstellungen Objekt (das wiederum kann es auslösen persistierenden).

Aber, verdammt, das ist eine Menge Arbeit.

Es ist jedoch wichtig, sich manchmal daran zu erinnern, dass die Objekte, die wir zur Instanziierung von Vue verwenden, nur einfache alte JavaScript-Objekte sind und wir sie manipulieren können. Ich fragte mich, ob ich etwas Code schreiben könnte, der die Dateneigenschaft und den berechneten Wert für uns erstellt. Ein Stichwort von Vuex, ja, wir können.

Was ich damit endete war dies.

import {settings, mapSetting} from "./settings" 

const definition = { 
    name:"app" 
} 

mapSetting(definition, "showOverlay" 

export default definition 

mapSetting erledigt die ganze Arbeit, die wir oben für uns getan haben. showOverlay ist jetzt eine berechnete Eigenschaft, die auf Änderungen in Vue reagiert und unser Einstellungsobjekt aktualisiert. Der einzige Nachteil im Moment ist, dass es eine showOverlay_internal Dateneigenschaft freilegt. Ich bin mir nicht sicher, wie wichtig das ist. Es könnte verbessert werden, um mehrere Eigenschaften gleichzeitig abzubilden.

Hier ist der vollständige Code, den ich geschrieben habe, der localStorage als Persistenzmedium verwendet.

function saveData(s){ 
    localStorage.setItem("settings", JSON.stringify(s)) 
} 

let stored = JSON.parse(localStorage.getItem("settings")) 
if (null == stored) { 
    stored = {} 
} 

export const settings = { 
    overlay: stored.showOverlay, 
    get showOverlay(){ 
    return this.overlay 
    }, 
    set showOverlay(v){ 
    this.overlay = v 
    saveData(this) 
    } 
} 

function generateDataFn(definition, setting, internalName){ 
    let originalDataFn = definition.data 
    return function(){ 
    let data = originalDataFn ? originalDataFn() : {} 
    data[internalName] = settings[setting] 
    return data 
    } 
} 

function generateComputed(internalName, setting){ 
    return { 
     get(){ 
     return this[internalName] 
     }, 
     set(v){ 
     settings[setting] = v 
     this[internalName] = v 
     } 
    } 
} 

export function mapSetting(definition, setting){ 
    let internalName = `${setting}_internal` 
    definition.data = generateDataFn(definition, setting, internalName) 

    if (!definition.computed) 
    definition.computed = {} 

    definition.computed[setting] = generateComputed(internalName, setting) 
} 
+0

Das ist eine wirklich erschöpfende Antwort - danke für deine harte Arbeit! Ich denke, ich werde jetzt meinen Vue-Port pausieren. Ich denke, Sie können zustimmen, dass die obige Lösung viel herumtanzt, um die Bindung zu unterstützen, aber ich habe andere (nicht-Vue-zentrische) Logik, die ich hätte, nur um es überhaupt funktionieren zu lassen, und ich habe gerade keine Zeit. Vielleicht werde ich später noch mehr über Vuex lesen, wenn es Zeit für eine vollständige Neuschreibung ist. – Coderer

+0

@Coderer Das ist eine faire Aussage. Ich habe etwas darüber gesucht, nachdem ich eine Lösung gefunden habe, weil es ein wenig zu kompliziert erscheint. Vue möchte wirklich in Daten mit einfachen Objekten arbeiten (https://vuejs.org/v2/api/#data). Das heißt, Vue ist in der Regel das Framework, das ich am einfachsten in andere Projekte integrieren kann. Ich bin ein Befürworter von Vuex, bis Sie es brauchen. Ich denke gerade dieser Fall, wo deine Eigenschaften auf deinem externen Objekt bereits Getter/Setter sind, ist hier die Hauptquelle von Schmerzen. Ich benutze Objekte mit Methoden und einfachen Eigenschaften die ganze Zeit. – Bert

+0

@Coderer Evan Du sprichst auch kurz [hier] (https://github.com/vuejs/vue/issues/2718), "Alle Prototyp-Methoden werden ignoriert. Die Verwendung von nicht-einfachen Objekten als Zustand ist ein Kaninchenloch zu Komplexität und es wird nicht vom Design unterstützt ". – Bert