VueJS反应式绑定到模块导出 [英] VueJS reactive binding to module export

查看:50
本文介绍了VueJS反应式绑定到模块导出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm new to Vue and I'm trying to bind a component value to a property of an exported object. The initial value is set correctly but it's not reactive. I'm not sure I'm using the right terminology, but the relevant sections are

// 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可能无法将其挂入其中.问题是,我希望此设置由此设置"服务拥有",该服务负责在页面加载之间持久保存值,但我不认为该服务应该知道该组件要监视一个页面.值并注意在值更改时手动触发组件上的更新-我可能只是误解了我在这种情况下应该使用的模式.

Now, I know that the exported object (settings) is a read-only view onto the object, because that's how modules work, so probably Vue can't put its hooks into it. The thing is, I want the setting to be "owned" by this Settings service, which is responsible for persisting the values between page loads, but I don't feel like the service should have to be aware that the component wants to watch a value and take care of manually triggering updates on the component when the value changes -- I probably just misunderstand the pattern I'm supposed to use for cases like this.

这是由Webpack/babel构建的,如果有什么不同的话.

This is being built with Webpack / babel, if that makes any difference.

推荐答案

此刻,我感到有些sheep脚.根据您在问题中看到的某些语法,我掉了一个小兔子洞,这使一大堆不必要的回转成为可能.语法是这样的:

I'm feeling a little bit sheepish at the moment. I went down a little rabbit hole based on some syntax I saw in your question and that let to a whole bunch of unnecessary gyrations. The syntax was this:

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

出于某种原因,我将其解释为很确定,只要启用 的更改, settings.showOverlay 就会更改,因为Vue具有响应性 ".

Which, for some reason, I interpreted as "well sure, whenever enabled changes, settings.showOverlay is going to change because Vue is reactive".

是的,不.

在该代码中, settings.showOverlay 只是 enabled 属性的初始值. enabled 属性将是 reactive ,但绝不会将值传递给settings对象.基本上,数据函数返回一个具有启用属性的对象,该属性的初始值为 settings.showOverlay 是什么,然后将该对象 变成反应性对象.

In that code, settings.showOverlay is just the initial value for the enabled property. The enabled property will be reactive, but in no way is it going to pass values to the settings object. Basically the data function returns an object with an enabled property that has an initial value of whatever settings.showOverlay is and then that object is turned into a reactive object.

如果您希望将在Vue中所做的更改传递到您的设置对象,那么您要做的就是将设置对象暴露在Vue的数据对象上.

If you want the changes made in Vue to be passed along to your settings object then all you need to do is expose the settings object on Vue's data object.

data() {
  return {
    settings,
  };
}

现在,如果您有类似的代码

Now if you have code like

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

settings.showOverlay 不仅会在Vue中起作用,而且还会在 settings 对象中起作用.不需要我从下面跳过的任何箍(/facepalm).

settings.showOverlay will not only be reactive in the Vue, but in the settings object. No need for any of the hoops I jumped through below (/facepalm).

FWIW我相信我在注释中提到的某些链接是指数据对象本身.数据对象必须是普通的javascript对象,不一定是它的所有属性.

FWIW I believe some of the links I mentioned in the comments are referring to the data object itself. The data object needs to be a plain javascript object, not necessarily all the properties on it.

换句话说,在

data() {
  return something
}

某物必须是普通的javascript对象.

something must be a plain javascript object.

原始答案

我已经在Vue应用程序中通过多种方式完成了此操作.在我的第一个应用程序中,我想做同样的事情,将设置存储在一个外部模块中,该模块可以管理设置的持久性并将这些设置显示在我的Vue上.我最终写了一个看起来像这样的课程.

I've done this in a couple ways in my Vue apps. In my first app I wanted to do the same thing, store the settings in an external module that could manage persisting the settings and expose those settings on my Vue. I ended up writing a class that looks like this.

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中使用它.

And then used that in my Vue like this.

import Settings from "./settings"

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

然后稍后,触发set()和save().对我来说,这是每当触发路由更改时,我只需将所有设置重新设置回设置"对象,然后保存.

And then some point later, trigger set() and save(). That point for me was whenever a route change was triggered, I'd just set all the settings back to the Settings object and then save.

听起来好像您要导出的对象具有getter/setter属性,可能是这样的.

It sounds like what you have is you're exporting an object that has getter/setter properties possibly something like this.

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

在触发 set 时可能触发保存的位置.我比上面描述的解决方案更喜欢这个想法.但是让它正常工作还需要更多工作.首先,我尝试使用计算机.

Where you maybe trigger a save when set is triggered. I like that idea better than the solution I described above. But getting it to work is a little more work. First I tried using a computed.

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

但这并不是很有效,因为它不能反映出对Vue所做的更改.这是有道理的,因为Vue并不真正知道值的变化.我希望将 $ forceUpdate 添加到setter也不起作用,因为计算值的缓存性质,我希望这样.但是,结合使用计算和数据属性,确实有效.

But that doesn't quite work because it doesn't reflect changes to the Vue. That makes sense because Vue doesn't really know the value changed. Adding a $forceUpdate to the setter doesn't work either, I expect because of the caching nature of computed values. Using a computed in combination with a data property, however, does work.

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

这会同时更改Vue的状态并触发设置对象中的更改(进而可以触发持久化).

That changes both the state of the Vue and triggers the change in the settings object (which in turn can trigger persisting it).

但是,该死,这是很多工作.

But, damn, that's a lot of work.

尽管有时要记住重要的一点,我们用来实例化Vue的对象只是普通的旧javascript对象,我们可以对其进行操作.我想知道是否可以编写一些代码来为我们创建data属性和计算值.从Vuex那里得到提示,是的,我们可以.

It's important to remember sometimes, though, that the objects we use to instantiate Vue are just plain old javascript objects and we can manipulate them. I wondered if I could write some code that creates the data property and the computed value for us. Taking a cue from Vuex, yes we can.

我最终想到的是这个.

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

const definition = {
  name:"app"
}

mapSetting(definition, "showOverlay"

export default definition

mapSetting 完成了我们上面为我们所做的所有工作. showOverlay 现在是一个计算属性,可对Vue中的更改做出反应并更新我们的设置对象.目前唯一的缺点是它公开了一个 showOverlay_internal 数据属性.我不确定这有多重要.可以一次映射多个属性.

mapSetting does all the work we did above for us. showOverlay is now a computed property that reacts to changes in Vue and updates our settings object. The only drawback at the moment is that it exposes a showOverlay_internal data property. I'm not sure how much that matters. It could be improved to map multiple properties at a time.

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

Here is the complete code I wrote that uses localStorage as a persistence medium.

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

这篇关于VueJS反应式绑定到模块导出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆