如何在 vue-router 之前得到一些东西到 运行
How to get something to run before vue-router
我正在构建我的第一个 SPA,我遇到了一些问题。它是这样设计的:
- Laravel & Laravel 视图处理登录和注册相关页面。
- SPA 从用户登录页面开始。
- 我的 app.js 定义了一个默认的 VueJS 应用程序,我在其中使用
mounted()
方法来设置登录用户的状态 (VUEX)。理想情况下,它所做的只是通过对 Laravel 后端的 axios 调用获取用户详细信息并填充 VUEX 状态。
- 我在路由定义中使用
beforeEnter()
方法来确保只有授权人员才能导航到该路由。
这就是我面临的问题。当用户登录时,似乎在设置 vuex 之前执行了路由器。假设我有一个 url /dashboard 和 /user/1。当我尝试转到 user/1 时,如果它是在我加载应用程序之后,它会完美运行。但是,如果我在 user/1 时刷新网页,则路由器 beforeEnter
无法找到用户的 vuex 状态,因此它将用户重定向到仪表板。这是因为当路由器运行时beforeEnter
,如果它是一个新的页面加载,它不会访问用户Vuex状态或者它有访问权限,但尚未设置该值。
正因为如此,我最大的问题是我无法 link 直接进入路线页面,因为它总是登陆仪表板,然后用户必须转到路线才能工作。我该如何处理这种情况?
我使用 $root 作为总线并转向 VueX 作为最后的手段,这是我从我正在使用的插件中删除的一些代码,我稍微调整了它让你直接进入你的代码..,应该让你去。
此配置支持VUE Cli
不用担心会话过期,拦截器会监视来自 Laravel 的 401 响应,以提示用户重新进行身份验证。
放弃 bootstrap.js 中的 axios 配置并用此设置替换它并配置 Access-Control-Allow-Origin,通配符将适用于本地开发。
axios.defaults.withCredentials = true;
axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': undefined,
'Access-Control-Allow-Origin': '*'
};
axios.interceptors.response.use(
function (response) {
if(response.headers.hasOwnProperty('x-csrf-token')) {
axios.defaults.headers['X-CSRF-TOKEN'] = response.headers['x-csrf-token'];
}
return response;
},
function (error) {
if(typeof error !== 'object' || !error.response) {
return Promise.reject(error);
}
if(error.response.hasOwnProperty('status')) {
switch(error.response.status) {
case 401:
case 419:
// DO RE-AUTHENTICATE CALL
break;
}
}
return Promise.reject(error);
}
);
剩下的...
在main.js
data() {
return {
user: {},
authenticating: false
}
},
computed: {
isAuthenticated() {
// Check a credential only an authorized user would have.
if(this.$router.app.hasOwnProperty('user') === false || this.$router.app.user === null) {
return false;
}
return this.$router.app.user.hasOwnProperty('id');
}
},
methods: {
checkAuth: function () {
this.$set(this.$router.app, 'authenticating', true);
axios.get('/auth/user').then(response => {
this.$set(this.$router.app, 'user', response.data.user);
if (this.$router.app.isAuthenticated()) {
this.$router.push(this.$router.currentRoute.query.redirect || '/', () => {
this.$set(this.$router.app, 'authenticating', false);
});
}
}).catch(error => {
// TODO Handle error response
console.error(error);
this.$set(this.$router.app, 'user', {});
}).finally(() => {
this.$set(this.$router.app, 'authenticating', false);
});
},
login: function (input) {
axios.post('/login', input).then(response => {
this.$set(this.$router.app, 'user', response.data.user);
this.$router.push(this.$router.currentRoute.query.redirect || '/');
}).catch(error => {
// TODO Handle errors
console.error(error);
});
},
logout: function () {
axios.post('/logout').then(response => {
this.$set(this.$router.app, 'user', {});
this.$nextTick(() => {
window.location.href = '/';
});
}).catch(error => {
console.error(error);
});
},
}
beforeCreate: function () {
this.$router.beforeResolve((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth) && !this.$router.app.isAuthenticated()) {
next({
name: 'login',
query: {
redirect: to.fullPath
}
});
return;
}
next();
});
}
在Auth/LoginController.php添加方法
public final function authenticated(Request $request)
{
return response()->json([
'user' => Auth::user()
]);
}
创建app/Http/Middleware/AfterMiddleware.php
它只会在更改时而不是在每次请求时传回新的 CSRF 令牌。 axios 拦截器将在检测到新令牌时摄取新令牌。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Cookie;
class AfterMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(Cookie::get('X-CSRF-TOKEN',false) !== csrf_token())
return $next($request)->header('X-CSRF-TOKEN',csrf_token());
return $next($request);
}
}
您可以使用此设置有效地将静态登录表单替换为 Vue 登录表单。
这是路由器设置的样子:
new Router({
mode: 'history',
routes: [
{
path: '/login',
name: 'login',
component: AuthLogin,
meta: {
requiresAuth: false,
layout: 'auth'
}
},
{
path: '/login/recover',
name: 'login-recover',
component: AuthLoginRecover,
meta: {
requiresAuth: false,
layout: 'auth'
}
},
{
path: '/',
name: 'index',
component: Dashboard,
meta: {
requiresAuth: true,
layout: 'default'
}
},
{
path: '/settings',
name: 'settings',
component: Settings,
meta: {
requiresAuth: true,
layout: 'default'
}
}
]
});
这就是我最后做的。我定义了一个用于初始化 Vue 的函数。
在 app.js 结束时,我使用 Axios 通过 Ajax 检索当前用户。在 promise 的 then
方法中,我使用在 promise 中收到的用户详细信息设置 store
,然后调用我在上面为 Vue 初始化定义的函数。这样当 vue 初始化时,商店用户已经有了数据。
代码更改非常少,我不必更改现有的 axios 实现。
这是我的新实现:
Axios.get('/api/user/info')
.then(response => {
(new Vue).$store.commit('setUser', response.data);
initializeVue();
})
.catch(error => initializeVue());
function initializeVue()
{
window.app = new Vue({
el: '#app',
router,
components: {
UserCard,
Sidebar,
},
methods: mapMutations(['setUser']),
computed: mapState(['user']),
});
}
我正在构建我的第一个 SPA,我遇到了一些问题。它是这样设计的:
- Laravel & Laravel 视图处理登录和注册相关页面。
- SPA 从用户登录页面开始。
- 我的 app.js 定义了一个默认的 VueJS 应用程序,我在其中使用
mounted()
方法来设置登录用户的状态 (VUEX)。理想情况下,它所做的只是通过对 Laravel 后端的 axios 调用获取用户详细信息并填充 VUEX 状态。 - 我在路由定义中使用
beforeEnter()
方法来确保只有授权人员才能导航到该路由。
这就是我面临的问题。当用户登录时,似乎在设置 vuex 之前执行了路由器。假设我有一个 url /dashboard 和 /user/1。当我尝试转到 user/1 时,如果它是在我加载应用程序之后,它会完美运行。但是,如果我在 user/1 时刷新网页,则路由器 beforeEnter
无法找到用户的 vuex 状态,因此它将用户重定向到仪表板。这是因为当路由器运行时beforeEnter
,如果它是一个新的页面加载,它不会访问用户Vuex状态或者它有访问权限,但尚未设置该值。
正因为如此,我最大的问题是我无法 link 直接进入路线页面,因为它总是登陆仪表板,然后用户必须转到路线才能工作。我该如何处理这种情况?
我使用 $root 作为总线并转向 VueX 作为最后的手段,这是我从我正在使用的插件中删除的一些代码,我稍微调整了它让你直接进入你的代码..,应该让你去。
此配置支持VUE Cli
不用担心会话过期,拦截器会监视来自 Laravel 的 401 响应,以提示用户重新进行身份验证。
放弃 bootstrap.js 中的 axios 配置并用此设置替换它并配置 Access-Control-Allow-Origin,通配符将适用于本地开发。
axios.defaults.withCredentials = true;
axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': undefined,
'Access-Control-Allow-Origin': '*'
};
axios.interceptors.response.use(
function (response) {
if(response.headers.hasOwnProperty('x-csrf-token')) {
axios.defaults.headers['X-CSRF-TOKEN'] = response.headers['x-csrf-token'];
}
return response;
},
function (error) {
if(typeof error !== 'object' || !error.response) {
return Promise.reject(error);
}
if(error.response.hasOwnProperty('status')) {
switch(error.response.status) {
case 401:
case 419:
// DO RE-AUTHENTICATE CALL
break;
}
}
return Promise.reject(error);
}
);
剩下的...
在main.js
data() {
return {
user: {},
authenticating: false
}
},
computed: {
isAuthenticated() {
// Check a credential only an authorized user would have.
if(this.$router.app.hasOwnProperty('user') === false || this.$router.app.user === null) {
return false;
}
return this.$router.app.user.hasOwnProperty('id');
}
},
methods: {
checkAuth: function () {
this.$set(this.$router.app, 'authenticating', true);
axios.get('/auth/user').then(response => {
this.$set(this.$router.app, 'user', response.data.user);
if (this.$router.app.isAuthenticated()) {
this.$router.push(this.$router.currentRoute.query.redirect || '/', () => {
this.$set(this.$router.app, 'authenticating', false);
});
}
}).catch(error => {
// TODO Handle error response
console.error(error);
this.$set(this.$router.app, 'user', {});
}).finally(() => {
this.$set(this.$router.app, 'authenticating', false);
});
},
login: function (input) {
axios.post('/login', input).then(response => {
this.$set(this.$router.app, 'user', response.data.user);
this.$router.push(this.$router.currentRoute.query.redirect || '/');
}).catch(error => {
// TODO Handle errors
console.error(error);
});
},
logout: function () {
axios.post('/logout').then(response => {
this.$set(this.$router.app, 'user', {});
this.$nextTick(() => {
window.location.href = '/';
});
}).catch(error => {
console.error(error);
});
},
}
beforeCreate: function () {
this.$router.beforeResolve((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth) && !this.$router.app.isAuthenticated()) {
next({
name: 'login',
query: {
redirect: to.fullPath
}
});
return;
}
next();
});
}
在Auth/LoginController.php添加方法
public final function authenticated(Request $request)
{
return response()->json([
'user' => Auth::user()
]);
}
创建app/Http/Middleware/AfterMiddleware.php 它只会在更改时而不是在每次请求时传回新的 CSRF 令牌。 axios 拦截器将在检测到新令牌时摄取新令牌。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Cookie;
class AfterMiddleware
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(Cookie::get('X-CSRF-TOKEN',false) !== csrf_token())
return $next($request)->header('X-CSRF-TOKEN',csrf_token());
return $next($request);
}
}
您可以使用此设置有效地将静态登录表单替换为 Vue 登录表单。
这是路由器设置的样子:
new Router({
mode: 'history',
routes: [
{
path: '/login',
name: 'login',
component: AuthLogin,
meta: {
requiresAuth: false,
layout: 'auth'
}
},
{
path: '/login/recover',
name: 'login-recover',
component: AuthLoginRecover,
meta: {
requiresAuth: false,
layout: 'auth'
}
},
{
path: '/',
name: 'index',
component: Dashboard,
meta: {
requiresAuth: true,
layout: 'default'
}
},
{
path: '/settings',
name: 'settings',
component: Settings,
meta: {
requiresAuth: true,
layout: 'default'
}
}
]
});
这就是我最后做的。我定义了一个用于初始化 Vue 的函数。
在 app.js 结束时,我使用 Axios 通过 Ajax 检索当前用户。在 promise 的 then
方法中,我使用在 promise 中收到的用户详细信息设置 store
,然后调用我在上面为 Vue 初始化定义的函数。这样当 vue 初始化时,商店用户已经有了数据。
代码更改非常少,我不必更改现有的 axios 实现。
这是我的新实现:
Axios.get('/api/user/info')
.then(response => {
(new Vue).$store.commit('setUser', response.data);
initializeVue();
})
.catch(error => initializeVue());
function initializeVue()
{
window.app = new Vue({
el: '#app',
router,
components: {
UserCard,
Sidebar,
},
methods: mapMutations(['setUser']),
computed: mapState(['user']),
});
}