2017-06-19 69 views
2

我是Vue的新手,我尝试将一个组件值绑定到导出对象的属性。初始值设置正确,但没有反应。我不知道我使用的是正确的术语,但相关部分是VueJS反应绑定到模块导出

// 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> 

现在,我知道,出口文物(settings)是一个只读视图到对象,因为这就是模块的工作方式,所以Vue可能无法将其挂钩。问题是,我希望此设置由此设置服务“拥有”,该服务负责在页面加载之间保持值,但我不认为服务人员应该知道该组件想要观看值并在值更改时手动触发组件上的更新 - 我可能只是误解了我应该用于这种情况的模式。

这是用Webpack/babel构建的,如果这有什么区别的话。

+1

我认为你正在寻找一些有状态,我想看看[vuex(http://vuex.vuejs.org/en/intro.html) – dops

+0

的原因之一,我开始使用Vue是因为它应该是“轻量级”的,你应该能够一次采用它。我不想将我的整个应用程序状态切换到Vue特定的存储区(我想我会花更多时间切换到全局消息总线模型,而不是使用Vue首先保存的内容)。有没有什么方法可以使用“只是一点点Vue”? – Coderer

+0

我认为更广泛的问题可能是我正在观察具有已经使用getter/setter的属性的对象 - 我相信在引擎盖下,导出的模块通过隐藏实际属性并提供了只读视图只有一个生成的getter。所以,如果我有一个对象并想要观察吸气剂,那么有没有一个好的方法来做到这一点? – Coderer

回答

2

我现在感觉有点不好意思。根据我在您的问题中看到的一些语法,我走下了一个小兔子洞,并且让一堆不必要的旋转。语法是这样的:

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

其中,出于某种原因,我解释为“好肯定的是,每当enabled变化,settings.showOverlay会有所改变,因为Vue公司是反应”。

是的,没有。

在该代码中,settings.showOverlay只是初始值对于enabled属性。 enabled属性将为反应,但它绝不会将值传递给设置对象。基本上数据函数返回一个启用了属性的对象,该对象的初始值为settings.showOverlay,然后是,即对象变为反应对象。

如果您希望将Vue中所做的更改传递给您的设置对象,那么您只需在Vue的数据对象上公开设置对象。

data() { 
    return { 
    settings, 
    }; 
} 

现在如果你有一个像

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

settings.showOverlay代码不仅将反应在Vue公司,但在settings对象。不需要我在下面跳过的任何箍(/ facepalm)。

FWIW我相信我在评论中提到的一些链接是指数据对象本身。数据对象需要是一个普通的javascript对象,不一定是它的所有属性。

换句话说,在

data() { 
    return something 
} 

something必须是一个普通的javascript对象。

原来的答案

我已经在我的Vue公司的应用程序一对夫妇的方式做到了这一点。在我的第一个应用程序中,我想做同样的事情,将设置存储在一个外部模块中,该模块可以管理持久设置并在Vue上显示这些设置。我最终写了一个看起来像这样的类。

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 

然后在我的Vue中这样使用它。

import Settings from "./settings" 

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

然后稍后再触发set()和save()。对我来说,这一点是每当路由更改被触发时,我只需将所有设置都设置回设置对象,然后保存。

听起来像是你正在输出一个具有getter/setter属性的对象,可能是这样的。

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

当您触发set时,您可能会触发保存。我比上述解决方案更喜欢这个想法。但让它工作是一个更多的工作。首先,我尝试使用计算机。

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

但是这并不起作用,因为它不反映Vue的变化。这是有道理的,因为Vue并不知道改变的价值。添加$forceUpdate到setter也不起作用,我期望因为计算值的缓存性质。然而,使用一个计算结合数据属性,确实工作。

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

这改变了Vue的状态并触发了设置对象的改变(这反过来可以触发持久化)。

但是,该死的,这是很多工作。

尽管有时记住我们用于实例化Vue的对象只是普通的旧javascript对象,我们可以操纵它们,这一点很重要。我想知道是否可以编写一些为我们创建数据属性和计算值的代码。从Vuex那里得到一个提示,是的,我们可以。

我最终得到的是这个。

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

const definition = { 
    name:"app" 
} 

mapSetting(definition, "showOverlay" 

export default definition 

mapSetting做了我们上面为我们做过的所有工作。 showOverlay现在是一个计算属性,对Vue中的更改起反应并更新我们的设置对象。目前唯一的缺点是它暴露了showOverlay_internal数据属性。我不知道有多重要。可以改进它来一次映射多个属性。

这是我写的使用localStorage作为持久性介质的完整代码。

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

这是一个非常详尽的答案 - 感谢您的辛勤工作!我认为我现在将暂时搁置我的Vue端口。我认为你可以同意上面的解决方案做了很多跳舞来支持绑定,但是我还有其他的(非Vue中心的)逻辑,我必须要Vue-ify才能让它工作,而我根本没有时间。也许我稍后会在Vuex上做更多的阅读,当时是全部重写的时候。 – Coderer

+0

@Coderer这是一个公平的陈述。在我提出解决方案之后,我已经对此进行了一些研究,因为它看起来有些复杂。 Vue确实想在数据中使用[纯对象](https://vuejs.org/v2/api/#data)。也就是说,Vue通常是我最容易集成到其他项目中的框架。我是支持Vuex直到你需要它的支持者。我认为这种情况下,特别是你的外部对象的属性已经是getters/setters,是这里疼痛的主要来源。我始终使用具有方法和简单属性的对象。 – Bert

+0

@Coderer Evan你也简单地谈了一下[这里](https://github.com/vuejs/vue/issues/2718),“所有原型方法都被忽略了。使用非简单对象作为状态是一个兔子洞复杂性,并没有得到设计的支持“。 – Bert