felix-cao / Blog

A little progress a day makes you a big success!
31 stars 4 forks source link

Vue 配合vue-router Vuex Mock.js实现登录功能 #160

Open felix-cao opened 5 years ago

felix-cao commented 5 years ago

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架,适合大中型应用,简单的应用建议就不要使用它了

Mock.js 可以生成随机数据,拦截 Ajax 请求,让前端攻城师独立于后端进行开发。通过随机数据,模拟各种场景。不需要修改既有代码,就可以拦截 Ajax 请求,返回模拟的响应数据。

一、状态管理器 Vuex

1.1、安装 vuex

npm install vue --save

1.2、使用 vuex 的 文件和代码组织,新建文件夹 store

|-- store                        // vuex的状态管理
|       |-- index.js            // 加载各种store模块
|       |-- user.js              // 用户store

1.3、各个store模块的统一入口 /src/store/index.js:

import Vue from 'vue'
import Vuex from 'vuex'
import user from './user'

Vue.use(Vuex)

const store = new Vuex.Store({
  strict: process.env.NODE_ENV !== 'production', // 非生产环境下使用严格模式
  modules: { user }
})

export default store

解读一下上面的代码:

export default { state: { isLogin: sessionStorage.getItem('isLogin') || false, userInfo: JSON.parse(sessionStorage.getItem('userInfo')) || {} }, mutations: { [USER_SIGNIN] (state, userInfo) { sessionStorage.setItem('isLogin', true) sessionStorage.setItem('userInfo', JSON.stringify(userInfo)) Object.assign(state, {userInfo, isLogin: true}) }, [USER_SIGNOUT] (state) { sessionStorage.removeItem('userInfo') sessionStorage.removeItem('isLogin') Object.keys(state).forEach(k => Vue.delete(state, k)) } }, actions: { [USER_SIGNIN] ({commit}, user) { return axios.post('/api/index/login', user).then(res => { const { httpCode, msg, data } = res.data if (httpCode !== 1) { throw new Error(msg) }

    commit(USER_SIGNIN, {...data, username: user.telephone})
  })
},
[USER_SIGNOUT] ({commit}) {
  commit(USER_SIGNOUT)
}

} }

解读一下上面的代码:
- 我们使用了常量 `USER_SIGNIN` 和 `USER_SIGNOUT`,来替代 `Mutation` 事件 和 `Action` 事件类型, [请阅读](https://vuex.vuejs.org/zh/guide/mutations.html)
- `mutations` 里的 `USER_SIGNIN` 和 `USER_SIGNOUT` 都是同步函数
- `actions` 里提交的是 `mutation`,而不是直接变更状态。异步操作应该放在 `actions` 里。
- 在上面的 `actions` 里,我们使用了用户账号和密码去 `post` 登录,拿到用户资料,提交给 `mutation`,`mutation`再去更改状态。

### 1.5、在登录中使用 actions 事件
```js
// other code
import { USER_SIGNIN } from '../../store/user'
// other code

methods: {
  onSignIn() {
    const params = { telephone: this.loginForm.username, password: this.loginForm.password }
    this.$store.dispatch(USER_SIGNIN, params)
        .then(() => {
          this.$router.replace({ path: '/admin' })
        })
        .catch(err => this.$message.error(err.message))
  },
// other code
}

解读一下上面的代码

1.1、路由权限参数配置

// other code
{ path: '/admin',
  component: Admin,
  meta: { Auth: true }
}
// other code

我们在 /admin 路由的 meta 对象中配置了 Auth,用以告诉 系统,这个路由需要登录状态才能访问

1.2、路由的导航守护 --- 全局导航守卫

vue-router 给我们提供了一个全局的导航守护,router.beforeEach,因此,在路由配置文件 /src/router/index.js 中:

// other code
router.beforeEach(function (to, from, next) {
  const isAuth = to.matched.some(m => m.meta.Auth)
  if (isAuth) {
    const isLogin = _.get(store, 'state.user.isLogin', '')
    if (!isLogin) {
      next({path: '/login'})
    }
  }
  next()
})

export default router

上面的代码 isLogin 是从 storeuser 模块中获取的, to.matched 的值是一个数组对象, 包含当前路由的所有嵌套路径片段的路由记录 。路由记录就是 routes 配置数组中的对象副本 (还有在 children 数组)。请阅读 路由对象属性

1.3、路由的导航守卫 --- 独享导航守卫

不过,我们仍然可以直接通过在地址栏输入 http://youDomain.com/#/admin,访问到 /admin 这个路由 path,所以我们要在路由的导向导航守卫,即在配置上直接定义 beforeEnter 守卫:

// other code
{ path: '/admin',
      component: Admin,
      meta: { Auth: true },
      beforeEnter: (to, from, next) => {
        if (!store.state.user.isLogin) {
          next({ path: '/login' })
          return false
        }
        next()
      }
}
// other code

三、Mock 数据

通过上面的代码,我们已经实现了常见的用户登录场景管理。下面就要实现与后端的数据通信,但是我们的后端工程师工作比较饱和,我们不得不继续向前赶任务,于是我们就得利用 Mock.js

mock 有“愚弄、欺骗”之意,在前端领域,mock 可以理解为我们前端开发人员制造的假数据。也就是说不是由后台真实制造,而是由我们其他一些方式模拟的数据,

安装

npm install mockjs --save-dev

新建 /src/mock/index.js,添加一条 userInfomock 规则

const Mock = require('mockjs');

const Random = Mock.Random; // 获取 mock.Random 对象

const userInfo= function() { // mock一组用户数据
   return {
     username: Random.cname(),
     date: Random.date()
   }
}

Mock.mock('/api/index/login', 'post', userInfo);

在入口 main.js 中引入 mock 规则

// other code
require('./mock.js')
// other code

OK, 至此,我们的系统就使用了我们定义的 mock 数据。