当对象属性依赖于外部对象时,如何使它们具有反应性?

时间:2019-07-22 22:05:45

标签: javascript vue.js javascript-objects vue-router dynamic-arrays

tldr:

User是全局对象。

如果我更改show的值,该组件将立即更新,没关系。 我想要获得的结果就像“ User.isLoggedIn()变为false时,Log out元素必须隐藏。当它变为true时,该元素必须显示并且Login/Signup必须隐藏。”在我的应用中,该目标将转换为另一个目标:“当我从loginsignupsignout页面重定向时,必须更新这些属性(和按钮的状态) ”。

Toolbar.Vue

脚本:

<script>
export default {
    data() {
        return {
            items: [
                {title: 'Questions', to: '/questions', show: true},
                {title: 'Ask question', to: '/askQuestion', show: true},
                {title: 'Categories', to: '/categories', show: true},
                //  only F5.
                {title: 'Login/Signup', to: '/login', show: !User.isLoggedIn()},
                {title: 'Log out', to: '/logout', show: User.isLoggedIn()},
            ]
        }
    },

}

标记部分:

<router-link
          v-for="item in items"
          :key="item.title"
          :to="item.to"
          v-if="item.show"
  >

您知道,我正在尝试使用vue进行“注销”。我有一个Toolbar组件和一个router-linkLogout组件。

注意 :我没有直接在组件中导入User类,但我在app.js中做到了。像这样:

import User from './helpers/AppUser';
window.User = User;

因此,我认为每个人都有权使用正确的User。此外,此类仅是几种方法的组合。主要方法是retrieve()

在我的Logout组件中是代码:

beforeMount() {
    User.logout();
    router.push('questions')
  // window.location = '/questions'
    }

因此,当我注销时,一切都很好(我将返回问题页面),但是我的Log out按钮仍然在这里。

User.isLoggedIn()正常工作(当我F5页面时,一切都很好)。

我还提到如果更改show值,该组件将立即更新,没关系。

这种尝试也不起作用:

    {title: 'Login/Signup', to: '/login', show: ()=> !this.isLoggedIn},
            {title: 'Log out', to: '/logout', show: ()=> this.isLoggedIn},
        ],
    }
},

computed:{
    isLoggedIn: function() {
        return User.isLoggedIn();
  },

我的临时解决方案是使用window.location = '/questions'而不是vue-router。 也许我需要一些观察者,或者将User添加到我的全局Vue中。我不知道。

更新:用户类别。

/**
 * Class-helper for managing user login/signup part. Also represents the user.
 */
class AppUser {

    constructor() {
        this.storageKey = 'appUser';
    }


    /**
     * retrieves user data from localStorage. if none stored, returns null
     * @return {object}|{null}
     */
    retrieve() {
        let data = {};

        try {
            data = JSON.parse(localStorage.getItem(this.storageKey));
        } catch (e) {
            console.log(e);
        } finally {
            // console.log(data)
        }

        return data;
    }

    /**
     * clears localStorageEntry
     */
    clear() {
        localStorage.removeItem(this.storageKey);
    }

    /**
     * @return boolean
     */
    hasId() {
        let data = this.retrieve();
        return data === null ? false : data.hasOwnProperty('id');
// if id then return true
    }

    /**
     * @return boolean
     */
    hasToken() {
        let data = this.retrieve();
        return data === null ? false : data.hasOwnProperty('jwt');
// if token then return true
    }


    isLoggedIn() {
        // console.log('in user.isLoggedIn')
        // try {
            return this.hasToken() && this.hasId();
        // }
        // catch (e) {
        //  console.log(e);
        // }
    }
}


export default AppUser = new AppUser();

2 个答案:

答案 0 :(得分:0)

基本上,您对User对象的设计不是被动的。主动调用方法以获取当前状态更像是拉样式。

使用getter/ setter使其具有反应性或纯属性。

然后将您的User对象添加到options.data时,vue将使其具有反应性。

因此简而言之

export default {
    data(){return {User: User}}, // reactive now if methods were transormed into getters
    computed: {
            items: [
                {title: 'Questions', to: '/questions', show: true},
                {title: 'Ask question', to: '/askQuestion', show: true},
                {title: 'Categories', to: '/categories', show: true},
                //  only F5.
                {title: 'Login/Signup', to: '/login', show: !this.User.isLoggedIn},
                {title: 'Log out', to: '/logout', show: this.User.isLoggedIn},
            ]
        }
    },

}

我的尝试将是这样的:

    // class AppUser as vue mixin, component, global, vuex module or wherever
export default {
    // vuex 
    // state: 
    data()
    {
        return { 
            retrivals = null,
            storageKey = 'appUser'
        }
    },
    //getters :
    computed: 
    {
        // hasId(state)
        hasId() {
            // if id then return true
            // return stete.retrivals && state.retrivals.hasOwnProperty('id') || false
            return this.retrivals && this.retrivals.hasOwnProperty('id') || false
        },
        // hasToken(state)
        hasToken() 
        {

            // if token then return true
            // return state.retrivals && state.retrivals.hasOwnProperty('jwt') || false
            return this.retrivals && this.retrivals.hasOwnProperty('jwt') || false
        },
        // isLoggedIn(state, getters)
        isLoggedIn()
        {
            // return getters.hasToken && getters.hasId
            return this.hasToken && this.hasId
        }

    },
    //actions:
    methods: {
        // retrieve({commit})
        retrieve()
        {
            let data = {};

            try {
                data = JSON.parse(localStorage.getItem(this.storageKey));
            } catch (e) {
                console.log(e);
            } finally {
            }
            // commit('setRetrievals', data)
            this.retrivals = data
        },
        // clear({commit})
        clear()
        {
            localStorage.removeItem(this.storageKey);
            commit('setRetrivals', null)
            this.retrivals = null
        }
    },
    // mutations: {
    //     setRetrievals(state, payload){

    //         state.retrivals = payload
    //     }
}

,然后在路由器中显示以下内容:

const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  window.AppUser.retrieve()
  // this.$store.dispatch(`${AppUserModulePath}/retrieve`)
  // this.$AppUser.retrieve()
})

和组件:

export default {

   computed: {
      show(){return window.AppUser.isLoggedIn}
      // show(){return this.$AppUser.isLoggedIn}
      // show(){return this.$store.state/*.{modulename}*/.isLoggedIn}
   }
}

在工具栏中

export default {    
      computed: {
            items(){ return [
                {title: 'Questions', to: '/questions', show: true},
                {title: 'Ask question', to: '/askQuestion', show: true},
                {title: 'Categories', to: '/categories', show: true},
                //  only F5.
                {title: 'Login/Signup', to: '/login', show: !this.$AppUser.isLoggedIn},
                {title: 'Log out', to: '/logout', show: this.$AppUser.isLoggedIn},
            ]}
        }
    },

}

要深入了解vuex

  function applyMixin (Vue) {
    var version = Number(Vue.version.split('.')[0]);

    if (version >= 2) {
      Vue.mixin({ beforeCreate: vuexInit });
    } else {
      // override init and inject vuex init procedure
      // for 1.x backwards compatibility.
      var _init = Vue.prototype._init;
      Vue.prototype._init = function (options) {
        if ( options === void 0 ) options = {};

        options.init = options.init
          ? [vuexInit].concat(options.init)
          : vuexInit;
        _init.call(this, options);
      };
    }

所以Vue.use(vuex)基本上是在root上添加一个mixin,而vuexInit则是从$store添加了root.$options,最后是store contains a vue instance state的反应性成分(gettersvuex

这可以应用于您的登录名/用户类。

i made a untested sketch of a vue-jwt plugin

答案 1 :(得分:0)

您可以在“创建”阶段替换用户的原始方法。

return {
    data () {
        return {
            items: [
                { title: 'Questions', to: '/questions', show: true },
                { title: 'Ask question', to: '/askQuestion', show: true },
                { title: 'Categories', to: '/categories', show: true },
                { title: 'Login/Signup', to: '/login', show: !User.isLoggedIn() },
                { title: 'Log out', to: '/logout', show: User.isLoggedIn() },
            ]
        }
    },
    created () {
        let app = this;
        let items = app.items;
        let loginUser = User.login.bind(User);
        let logoutUser = User.logout.bind(User);

        User.login = () => {
            // modify the data
            items[3].show = false;
            items[4].show = true;
            // do login
            loginUser();
        };

        User.logout = () => {
            // modify the data
            items[3].show = true;
            items[4].show = false;
            // do logout
            logoutUser();
        };
    }
};