VueJS反应绑定到模块导出

时间:2017-06-19 09:01:15

标签: vuejs2 vue-component es6-modules

我是Vue的新手,我试图将组件值绑定到导出对象的属性。初始值设置正确,但不是反应性的。我不确定我是否使用了正确的术语,但相关部分是

// 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可能无法将其挂钩它。问题是,我希望设置为&#34;拥有&#34;通过此设置服务,它负责在页面加载之间保持值,但我不认为服务应该知道组件想要观察值并负责手动触发组件上的更新当价值发生变化时 - 我可能只是误解了我应该用于这种情况的模式。

这是使用Webpack / babel构建的,如果这有任何区别的话。

1 个答案:

答案 0 :(得分:3)

此刻我感觉有点羞怯。基于我在你的问题中看到的一些语法,我走了一个小兔子洞,让一大堆不必要的旋转。语法是这样的:

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

出于某种原因,我将其解释为&#34;确定无论何时enabled发生变化,settings.showOverlay都会发生变化,因为Vue 被动&#34 ;

是的,没有。

在该代码中,settings.showOverlay只是enabled属性的初始值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()。对我而言,无论何时触发路由更改,我都只需将所有设置设置回Settings对象,然后保存。

听起来你拥有的是你正在导出一个具有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)
}