【问题标题】:VueJS: Best practice for working with global object between components?VueJS:在组件之间使用全局对象的最佳实践?
【发布时间】:2019-07-09 03:25:31
【问题描述】:

User.js 类和用户对象(user = new User();)。

user 对象正在所有嵌套组件中使用。在User 类中有很多重要的方法。

如何在任何组件中简单地使用/访问this.userthis.$user 及其方法?

1-solution(临时工作解决方案):在 vuex 的 store 中设置 user 并在所有组件的 data 中定义:

data(){
  return {
    user:this.$store.state.user
  }
}

缺点:在每个组件中,都应该添加它。注意:组件太多了。

2-solution:将用户添加到 Vue 的 prototype 之类的插件:

Vue.prototype.$user = user

缺点:当user的数据发生变化时,它不会影响在DOM元素(UI)中。

3-solution:放入组件的props

缺点:在每个组件中,都应该添加它。注意:还有这么多组件。

我发现的所有解决方案都有问题,尤其是随着项目越来越大。

任何建议和解决方案将不胜感激!

【问题讨论】:

  • github.com/vuejs/vue/issues/4029 这会很有帮助。阅读它
  • @CodeManiac 谢谢,我不知道 react 的类似上下文的功能。但我没有找到好的解决方案。
  • 对于这种情况dependency injection 是最好的解决方案。 vuejs.org/v2/guide/…
  • 您的代码this.user or this.$user 也需要写入每个组件!没有不同 。但是您无需更改 user 数据,只需从商店直接访问
  • @imudin07 是的,这是因为注入的值是非反应性的。你需要改用这个 npm 包github.com/LinusBorg/vue-reactive-provide

标签: javascript vue.js vuex


【解决方案1】:

注意:适用于 Vue 2x

建议一:使用 vuex 的 getter

  • 您可以使用 Vuex 中的 gettersmapGetters 将用户包含在每个组件的计算属性中。

Vuex

getters: {
  // ...
  getUser: (state, getters) => {
    return getters.user
  }
}

组件

import { mapGetters } from 'vuex'
computed: {
  ...mapGetters([getUser])
}

方案二:通过插件添加观察者

Vue

// When using CommonJS via Browserify or Webpack
const Vue = require('vue')
const UserPlug = require('./user-watcher-plugin')

// Don't forget to call this
Vue.use(UserPlug)

user-watcher-plugin.js

const UserPlug = {
  install(Vue, options) {
    // We call Vue.mixin() here to inject functionality into all components.

    Vue.watch: 'user'
  }
};

export default UserPlug;

方案3:通过mixin添加一个计算属性user作为插件

Vue

// When using CommonJS via Browserify or Webpack
const Vue = require('vue')
const UserPlug = require('./user-watcher-plugin')

// Don't forget to call this
Vue.use(UserPlug)

user-watcher-plugin.js

const UserPlug = {
  install(Vue, options) {
    // We call Vue.mixin() here to inject functionality into all components.

    Vue.mixin({
      computed: {
        user: function() {
          return this.$store.state.user
        }
      }
    })
  }
};

export default UserPlug;

【讨论】:

  • 感谢您的时间和目的。 2号3号好看!让我尝试实现它
  • 我使用了提案 3,效果很好。只有我只担心性能
【解决方案2】:

基于@Denis 的回答,特别是提案3,这里是UserPlugin.js

import store from '@/store/store';
import User from './User';

const UserPlugin = {
  install(Vue) {
    const $user = new User();
    window.$user = $user;
    store.commit('setUser', $user);
    Vue.mixin({
      computed: {
        $user() {
          return store.state.user;
        }
      }
    });
  }
};
export default UserPlugin;

main.js:

import UserPlugin from './common/UserPlugin';

Vue.use(UserPlugin);

new Vue({
  render: h => h(App)
}).$mount('#app');

为了进一步使用,我发布了解决这些问题的小型库:

https://www.npmjs.com/package/vue-global-var

【讨论】:

    【解决方案3】:

    我刚刚创建了最小的codesandbox 以明确dependency Injection 在 vue 中的工作原理。

    【讨论】:

    • 感谢您的努力。对此,我真的非常感激。这个解决方案我将用于其他一些不会用于所有组件的对象。但是公认的答案的解决方案非常适合几乎所有组件都在使用的用户对象(计数约为 30-40)
    • User只是带有js扩展的es6类,不是Vue组件
    • @imudin07 他们(30-40)都是父母的孩子吗?
    • 是的,将路由与组件和子组件相乘
    【解决方案4】:

    您可以使用 mixins 将 User.js 添加到您的根组件中,例如

    import userLib from './User';
    //User.js path should correct
    

    然后

    var app = new Vue({
        router,
        mixins: [
            userLib
        ],
      //.....
    });
    

    之后,您可以在任何组件中使用这些User 方法中的任何一个,例如

    this.$parent.userClassMehtod();
    

    或者如果有任何数据访问

    this.$parent.userClassData;
    

    最后别忘了在User.js中加export default{//..}

    注意:仅当您将User.js 的所有方法导出到export default 时才有效

    【讨论】:

    • 感谢您的解决方案,但它不能在这里应用,我无法使用 mixin 直接将User.js 添加到组件中。它是带有用户信息和一些方法的 es6 类,我们根据用户的属性和方法来决定 UI。 user= new User()。这里user.something 应该在任何组件的模板和脚本中都具有反应性和可访问性
    【解决方案5】:

    假设您实际上并未在每个组件中使用 user 的所有方法/属性,而是每次都使用其中的一部分,我看不出解决方案 1 和 2 对您不起作用的任何原因,因为通过不需要对每个组件都使用整个 user 对象。

    假设您的对象User 有一些属性(a1、a2、a3 等)和方法(m1、m2、m3...)。如果一个组件只需要其中的一些(例如 a1、a2、m1、m2、m3),那么使用 Vuex,您可以使用映射函数(mapState、mapGetters、mapMutations 和 mapActions)从 user

    import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
    export default {
        computed: {
            ...mapState('user', [ 'a1' ]),
            ...mapGetters('user', [ 'a2' ])
        },
        methods: {
            ...mapMutations('user', [ 'm1' ]),
            ...mapActions('user', [ 'm2', 'm3' ])
        }
    }
    

    对于方案2(使用原型),要在user数据发生变化时更新组件,可以通过methods将必要的数据映射到组件。

    export default {
        methods: {
            userA1() {
                return this.$user.attributes.a1;
            },
            userM1() {
                this.$user.methods.m1();
            }
            // and so on
        }
    }
    

    更好的是,您可以创建 mixin 以显式映射来自 user 的数据,并重用您的 mixin 以避免组件中的重复代码。 Vuex解决方案和原型解决方案都可以应用。

    // mixin1:
    const mixin1 = {
        computed: {
            ...mapState('user', [ 'a1' ]),
        },
        methods: {
            ...mapMutations('user', [ 'm1' ])
        }
    }
    
    // mixin2:
    const mixin2 = {
        computed: {
            ...mapGetters('user', [ 'a2' ]),
        },
        methods: {
            ...mapActions('user', [ 'm2', 'm3' ])
        }
    }
    
    // component1
    export default {
        mixins: [ mixin1 ]
    }
    
    // component 2
    export default {
        mixins: [ mixin1, mixin2 ]
    }
    

    但如果你真的需要将整个对象 user 传递给每个组件,那么什么也做不了。相反,您应该检查您的实现,看看是否有更好的方法将对象分解为更小的有意义的对象。

    【讨论】:

    • 非常感谢您的回答。正如我提到的 1-solution 运行良好,但需要为所有组件添加它。在解决方案中,我编写的代码最少。假设我在每个组件中都使用了 User 的大部分方法。我们的团队发现,在 1-solution 已经是负担的情况下,为所有组件一个一个添加 Vuex 的映射功能似乎并不好和负担。我拥有的 4 解决方案是全局 mixin,但无法使其工作
    • @imudin07 如果是这样的话,那么我的回答似乎与您的实际问题无关。是的,在这种情况下,全局 mixin 应该可以减少代码重复;如果您仍然坚持使用 global mixin,您可以提出另一个问题。祝你好运。
    【解决方案6】:

    您可以拥有第二个 Vue 实例并声明一个响应式属性。

    See: Reactivity in depth

    【讨论】:

      猜你喜欢
      • 2018-06-16
      • 1970-01-01
      • 2017-12-12
      • 2014-01-21
      • 2021-07-06
      • 1970-01-01
      • 1970-01-01
      • 2018-06-25
      • 2013-09-23
      相关资源
      最近更新 更多