简体   繁体   English

Vue Router无法呈现正确的组件

[英]Vue Router does not render correct component

I have the some nested routes in vue-router , which can be found in router.js . 我在vue-router有一些嵌套的路由,可以在router.js找到router.js However, everytime I logged the user in, the Query and Result link in the Header always render Register and Login components, but I can confirmed that my Vuex has the proper token setup by investigating through vue developer tools . 但是,每次我登录用户时, HeaderQueryResult链接始终呈现RegisterLogin组件,但是我可以通过通过vue开发人员工具进行调查来确认我的Vuex具有正确的令牌设置。


So my expectation for the vue-router to work is as followed: 因此,我对vue-router期望如下:

  1. User attempt to login at /user/login 用户尝试登录/user/login
  2. Login successfully, token set in Vuex state 登录成功,令牌已设置为Vuex状态
  3. User is redirected to / 用户被重定向到/
  4. User click on /query or result links, <router-view> render Query or Result component in App.vue . 用户单击/queryresult链接, <router-view>呈现App.vue QueryResult组件。
  5. User click on Logout button, Vuex state is cleared. 用户单击Logout按钮, Vuex状态被清除。
  6. User is redirected back to /user/login . 用户被重定向回/user/login

But the result I get is: 但是我得到的结果是:

  1. User attempt to login at /user/login 用户尝试登录/user/login
  2. Login successfully, token set in Vuex state 登录成功,令牌已设置为Vuex状态
  3. User is redirected to / 用户被重定向到/
  4. User click on /query link, <router-view> in App.vue rendered Register component. 用户单击App.vue /query链接, <router-view>呈现了Register组件。
  5. User click on /result link, <router-view> in App.vue rendered Login component. 用户单击App.vue /result链接, <router-view>呈现了Login组件。
  6. User refresh the page using F5 . 用户使用F5刷新页面。
  7. User click on /query or result links, <router-view> render Query or Result component in App.vue . 用户单击/queryresult链接, <router-view>呈现App.vue QueryResult组件。
  8. User click on Logout button, Vuex state is cleared. 用户单击Logout按钮, Vuex状态被清除。
  9. User is redirected back to /user/login . 用户被重定向回/user/login
  10. User click on Login button, <router-view> in App.vue rendered Result component. 用户单击App.vue呈现的“ Result组件中的“ Login按钮, <router-view>

What I have tried 我尝试过的

  1. Remove the token protection on route, the <router-view> still does not render the correct components. 删除路由上的令牌保护, <router-view>仍无法呈现正确的组件。
  2. Remove v-if="auth" from all <router-link> with token protection still on, the problem seems to go away. 从所有<router-link>删除v-if="auth" ,同时仍启用令牌保护,问题似乎消失了。 But I wanted to render Query and Result only when the user logged in, and vice versa. 但是我只想在用户登录时才呈现QueryResult ,反之亦然。
  3. Use v-show instead of v-if remove the issue, but I doubt it is a proper way to hide the links from the user. 使用v-show而不是v-if消除问题,但是我怀疑这是隐藏用户链接的正确方法。
  4. Name all routes, but the problem still persists. 命名所有路由,但问题仍然存在。

Some Code 一些代码

This is my router.js 这是我的router.js

import User from './components/user/User'
import Home from './components/Home'
import Login from './components/user/Login'
import Register from './components/user/Register'
import Query from './components/Query'
import Result from './components/result/Result'
import List from './components/result/List'
import Display from './components/result/Display'

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter);

const router = [
    {
        path: '/', name: 'home', component: Home
    },
    {
        path: '/user', component: User, children: [
            {path: 'login', component: Login},
            {path: 'register', component: Register},
        ]
    },
    {
        path: '/query', name: 'query', component: Query
    },
    {
        path: '/result', component: Result, children: [
            {path: '', component: List},
            {path: ':token', component: Display}
        ]
    },
    {path: '*', redirect: '/'},
];

export default new VueRouter({
    routes: router,
    mode: 'history',
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition;
        }
        if (to.hash) {
            return {selector: to.hash};
        }
        return {x: 0, y: 0};
    }
})

This is my Header.vue 这是我的Header.vue

<template>
    <nav class="navbar navbar-expand-lg navbar-light bg-light" id="header">
        <a class="navbar-brand" href="#">CMAP</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav ml-auto">
                <router-link to="/user/register" active-class="active" tag="li" v-if="!auth">
                    <a class="nav-link">Register</a>
                </router-link>
                <router-link to="/user/login" active-class="active" tag="li" v-if="!auth">
                    <a class="nav-link">Login</a>
                </router-link>
                <router-link to="/query" active-class="active" tag="li" v-if="auth">
                    <a class="nav-link">Query</a>
                </router-link>
                <router-link to="/result" active-class="active" tag="li" v-if="auth">
                    <a class="nav-link">Result</a>
                </router-link>
                <li @click="logout" class="nav-item" v-if="auth">
                    <a class="nav-link">Logout</a>
                </li>
            </ul>
        </div>
    </nav>
</template>

<script>
    import * as  types from '../../stores/types'

    export default {
        methods: {
            logout() {
                // clear state.token and redirect user to '/user/login'
                this.$store.dispatch(types.ACTION_USER_LOGOUT);
            }
        },
        computed: {
            auth() {
                // return true if state.token is set in vuex
                return this.$store.getters[types.GETTER_IS_AUTHENTICATED]
            }
        }
    }
</script>

This is my App.vue 这是我的App.vue

<template>
    <div>
        <appHeader></appHeader>
        <div class="container">
            <router-view></router-view>
        </div>
        <appFooter></appFooter>
    </div>
</template>

<script>
    import Header from './components/layouts/Header'
    import Footer from './components/layouts/Footer'

    export default {
        components: {
            'appHeader': Header,
            'appFooter': Footer
        }
    }
</script>

This is my store/store.js 这是我的store/store.js

import Vue from 'vue';
import Vuex from 'vuex';

import actions from './actions';
import getters from './getters';
import mutations from './mutations';

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        jwt: {
          token: null,
          expire: null
        },      
    },
    getters,
    mutations,
    actions,
});

This is my store/actions.js 这是我的store/actions.js

import * as types from './types';
import axios from '../axios';
import router from '../router'

export default {
    [types.ACTION_USER_LOGIN]: ({commit, dispatch}, payload) => {
        axios.post('/api/user/login/', {
            email: payload.email,
            password: payload.password,
        })
            .then(res => {
                if (Object.keys(res.data).length === 0 && res.data.constructor === Object) {
                    return
                }
                let payload = {
                    'token': res.data.token,
                    'expire': new Date(res.data.expire_on * 1000)
                };

                dispatch(types.ACTION_SET_TOKEN, payload);
                commit(types.MUTATE_UPDATE_JWT, payload);
                router.push('/')
            })
            .catch(error => {
                console.log(error);
            })
    },
    [types.ACTION_USER_REGISTER]: ({commit, dispatch}, payload) => {
        axios.post('/api/user/register/', {
            email: payload.email,
            password: payload.password
        }).then(res => {
            if (Object.keys(res.data).length === 0 && res.data.constructor === Object) {
                return
            }
            let payload = {
                'token': res.data.token,
                'expire': new Date(res.data.expire_on * 1000)
            };
            dispatch(types.ACTION_SET_TOKEN, payload);
            commit(types.MUTATE_UPDATE_JWT, payload);
            router.push('/')
        }).catch(error => {
            console.log(error);
        })
    },
    [types.ACTION_USER_LOGOUT]: ({commit, dispatch}) => {
        dispatch(types.ACTION_CLEAR_TOKEN);
        commit(types.MUTATE_CLEAR_AUTH);
        router.push("/user/login");
    },
    [types.ACTION_CLEAR_TOKEN]: () => {
        localStorage.removeItem('jwt_token');
        localStorage.removeItem('jwt_expire');
    },
    [types.ACTION_SET_TOKEN]: ({commit}, payload) => {
        localStorage.setItem("jwt_token", payload.token);
        localStorage.setItem("jwt_expire", payload.expire);
    }     
};

This is my store/getters.js 这是我的store/getters.js

import * as types from './types';

export default {
    [types.GETTER_IS_AUTHENTICATED]: state => {
        return state.jwt.token !== null
    }
};

This is my store/mutations.js 这是我的store/mutations.js

import * as types from './types'; 从'./types'导入*作为类型;

export default {
    [types.MUTATE_UPDATE_JWT]: (state, payload) => {
        state.jwt = payload
    },
    [types.MUTATE_CLEAR_AUTH]: (state) => {
        state.jwt.token = null;
        state.jwt.expire = null;
    },
};

This is my axios.js custom instance 这是我的axios.js自定义实例

import {store} from './stores/store'
import axios from 'axios'

const instance = axios.create({
    baseURL: 'http://127.0.0.1:8000',
    withCredentials: true
});

instance.interceptors.request.use(config => {
    if (store.state.jwt.token != null) {
        config.headers.common['Authorization'] = 'Bearer ' + store.state.jwt.token;
    }
    return config;
});

export default instance

My store/types.js contains all the constant variable string, so I don't think it is necessary to include here. 我的store/types.js包含所有常量变量字符串,因此我认为没有必要在此处包含。

Please let me know if I need to provide more information on the problem. 如果需要提供有关此问题的更多信息,请告诉我。

I have solved the issue by assigning a unique key to every router-link in Header.vue . 通过为Header.vue每个router-link分配唯一的key ,我已经解决了该问题。 So the final Header.vue file looks like this after modifications: 因此,修改后的最终Header.vue文件如下所示:

<template>
    <nav class="navbar navbar-expand-lg navbar-light bg-light" id="header">
        <a class="navbar-brand" href="#">CMAP</a>
        <button class="navbar-toggler" type="button" data-toggle="collapse"
        data-target="#navbarSupportedContent"
        aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav ml-auto">
                <router-link to="/user/register" active-class="active" tag="li" v-if="!auth" key="link_reg">
                    <a class="nav-link">Register</a>
                </router-link>
                <router-link to="/user/login" active-class="active" tag="li" v-if="!auth" key="link_login">
                    <a class="nav-link">Login</a>
                </router-link>
                <router-link to="/query" active-class="active" tag="li" v-if="auth" key="link_query">
                    <a class="nav-link">Query</a>
                </router-link>
                <router-link to="/result" active-class="active" tag="li" v-if="auth" key="link_result">
                    <a class="nav-link">Result</a>
                </router-link>
                <li @click="logout" class="nav-item" v-if="auth">
                    <a class="nav-link">Logout</a>
                </li>
            </ul>
        </div>
    </nav>
</template>

<script>
    import * as  types from '../../stores/types'

    export default {
        methods: {
            logout() {
                // clear state.token and redirect user to '/user/login'
                this.$store.dispatch(types.ACTION_USER_LOGOUT);
            }
        },
        computed: {
            auth() {
                // return true if state.token is set in vuex
                return this.$store.getters[types.GETTER_IS_AUTHENTICATED]
            }
        }
    }
</script>

It seems that VueJS couldn't update the DOM properly without associated key in each v-if for router-link . 似乎在每个v-if没有相关的key的情况下,VueJS无法正确更新DOM,以用于router-link

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM