搭建Vue从Vue-cli到router路由护卫的实现


Posted in Javascript onNovember 14, 2019

别的不多说,开始动爪把,

首先安装vue-cli  mac: sudo npm install -g @vue/cli

github:

https://github.com/XinYueXiao/vue-routes

1、Vue-cli基础使用

1.1 创建测试项目 vue create vue-routes

搭建Vue从Vue-cli到router路由护卫的实现

1.2 创建成功,启动项目 yarn serve

在 http://localhost:8080/ 就可以看到欢迎:clap:页面了

搭建Vue从Vue-cli到router路由护卫的实现

1.3 搞点自定义配置,新建vue.config.js

const title = '双11剁手啦'
const port = '1111'
module.exports = {
  publicPath: '/wxy',
  //自定义端口号
  devServer: {
    port
  },
  //自定义变量
  configureWebpack: {
    name: title
  }
}

配置完成后重新启动 yarn serve 效果图

搭建Vue从Vue-cli到router路由护卫的实现

如何配置svg图标

1)准备一个svg,例如: src/icons/svg/hg.svg

2)安装loader yarn add svg-sprite-loader

3)对config进行链式操作即可修改loader

const path = require('path')
//处理地址
function resolve(dir) {
  return path.join(__dirname, dir)
}
module.exports = {
  ...,
  chainWebpack(config) {
    //安装loader,对config进行链式操作即可修改loader、plugins
    //1.svg rule中要排除icons目录
    config.module.rule('svg')
      //转换为绝对地址
      .exclude.add(resolve('src/icons'))
      //查看配置后svg规则 vue inspect --rule svg
    //2.添加一个规则icons
    config.module.rule('icons')
      .test(/\.svg$/)
      .include.add(resolve('src/icons')).end()
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })
  }
}

4)svg rule中要排除icons目录后配置

搭建Vue从Vue-cli到router路由护卫的实现

5) 添加一个规则icons配置

搭建Vue从Vue-cli到router路由护卫的实现

6) 新建 src/components/SvgIcon.vue 模板

<template>
 <svg :class="svgClass" aria-hidden="true" v-on="$listeners">
  <use :xlink:href="iconName" rel="external nofollow" />
 </svg>
</template>
<script>
export default {
 name: "SvgIcon",
 props: {
  iconClass: {
   type: String,
   required: true
  },
  className: {
   type: String,
   default: ""
  }
 },
 computed: {
  iconName() {
   return `#icon-${this.iconClass}`;
  },
  svgClass() {
   if (this.className) {
    return "svg-icon " + this.className;
   } else {
    return "svg-icon";
   }
  }
 }
};
</script>
<style scoped>
.svg-icon {
 width: 1em;
 height: 1em;
 vertical-align: -0.15em;
 fill: currentColor;
 overflow: hidden;
}
</style>

7)新建 src/icons/index.js  在main.js下引入icon

//src/icons/index.js
import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon'
//图标自动加载
const req = require.context('./svg', false, /\.svg$/)
req.keys().map(req)
Vue.component('svg-icon', SvgIcon)

//main.js
import "./icons";

8)在App.vue引入图标

<svg-icon icon-class="hg"></svg-icon>

效果如下:

搭建Vue从Vue-cli到router路由护卫的实现

2、router路由守卫

何为守卫,即为阻止无身份者进入组织内部

安装yarn add vue-router 控制路由

安装yarn add vuex 存储身份认证

搭建Vue从Vue-cli到router路由护卫的实现

2.1 路由配置

src/router/index.js

import Vue from "vue";
import Router from "vue-router";
import Layout from '@/layout'; // 布局页
Vue.use(Router);
// 通用页面:不需要守卫,可直接访问 
export const constRoutes = [
  {
    path: "/login",
    component: () => import("@/views/Login"),
    hidden: true // 导航菜单忽略该项
  }, {
    path: "/",
    component: Layout,// 应用布局
    redirect: "/home",
    children: [
      {
        path: "home",
        component: () =>
          import(/* webpackChunkName: "home" */ "@/views/Home.vue"),
        name: "home",
        meta: {
          title: "Home", // 导航菜单项标题
          icon: "hg" // 导航菜单项图标 
        }
      }]
  }];
// 权限页面:受保护页面,要求用户登录并拥有访问权限的角色才能访问 
export const asyncRoutes = [
  {
    path: "/about",
    component: Layout,
    redirect: "/about/index",
    children: [
      {
        path: "index",
        component: () =>
          import(/* webpackChunkName: "home" */ "@/views/About.vue"),
        name: "about",
        meta: {
          title: "About",
          icon: "hg",
          roles: ['admin', 'editor']
        },
      }
    ]
  }
];
export default new Router({
  mode: "history",
  base: process.env.BASE_URL,
  routes: constRoutes
});

布局组件 src/layout

<template>
 <div class="app-wrapper">
  <div class="main-container">
   <router-view />
  </div>
 </div>
</template>

路由展示src/App.vue

<template>
 <div id="app">
  <!-- 路由 -->
  <div id="nav">
   <router-link to="/">
    <svg-icon icon-class="wx"></svg-icon>
    <!-- <svg>
     <use xlink:href="#icon-wx" rel="external nofollow" ></use>
    </svg>-->
    Home
   </router-link>|
   <router-link to="/about">
    <svg-icon icon-class="hg"></svg-icon>About
   </router-link>
  </div>
  <!-- 4.路由视图 -->
  <!-- 问题:router-link和router-view是哪来的 -->
  <router-view></router-view>
 </div>
</template>

<script>
export default {
 name: "app",
 components: {}
};
</script>

<style>
#app {
 font-family: "Avenir", Helvetica, Arial, sans-serif;
 -webkit-font-smoothing: antialiased;
 -moz-osx-font-smoothing: grayscale;
 text-align: center;
 color: #2c3e50;
 margin-top: 60px;
}
</style>

2.2  准备页面

src/views/About.vue

<template>
 <div class="about">
  <h1>This is an about page</h1>
 </div>
</template>

src/views/Home.vue

<template>
 <div class="home">
  <img alt="Vue logo" src="../assets/logo.png" />
  <HelloWorld msg="Welcome to Your Vue.js App" />
 </div>
</template>

<script>
// @ is an alias to /src
import HelloWorld from "@/components/HelloWorld.vue";
export default {
 name: "home",
 components: {
  HelloWorld
 }
};
</script>

src/views/Login.vue

<template>
 <div>
  <h2>用户登录</h2>
  <div>
   <input type="text" v-model="username" />
   <button @click="login">登录</button>
  </div>
 </div>
</template>
<script>
export default {
 data() {
  return {
   username: "admin"
  };
 },
 methods: {
  login() {
   this.$store
    .dispatch("user/login", { username: this.username })
    .then(() => {
     this.$router.push({
      path: this.$route.query.redirect || "/"
     });
    })
    .catch(error => {
     alert(error);
    });
  }
 }
};
</script>

2.3  身份认证

import router from "./router";
import store from "./store";
const whiteList = ["/home", "/login"]; // 无需令牌白名单
// 全局路由守卫
router.beforeEach(async (to, from, next) => {
  // 获取令牌判断用户是否登录
  const hasToken = localStorage.getItem("token");

  // 已登录
  if (hasToken) {
    if (to.path === "/login") {
      // 若已登录没有必要显示登录页,重定向至首页
      next({ path: "/" });
    } else {
      // 去其他路由,暂时放过
      //  next()
      // 接下来执行用户角色逻辑, todo
      //  1.判断用户是否拥有角色
      const hasRoles =
        store.state.user.roles && store.state.user.roles.length > 0;

      if (hasRoles) {
        next();
      } else {
        // 2.获取用户角色
        const roles = await store.dispatch("user/getInfo");

        const accessRoutes = await store.dispatch("permission/generateRoutes", roles);

        //  动态添加路由到路由器
        router.addRoutes(accessRoutes);

        // 跳转
        next({ ...to });
      }
    }
  } else {
    // 未登录
    if (whiteList.indexOf(to.path) !== -1) {
      // 白名单中路由放过
      next();
    } else {
      // 重定向至登录页
      next(`/login?redirect=${to.path}`);
    }
  }
});

2.4  用户信息设置

import Vue from "vue";
import Vuex from "vuex";
import user from './modules/user'
import permission from './modules/permission'

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    user, permission
  }
});

src/store/modules/user.js

const state = {
  token: localStorage.getItem("token"),
  // 其他用户信息
  roles: []
};

const mutations = {
  SET_TOKEN: (state, token) => {
    state.token = token;
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles;
  },
};

const actions = {
  // 模拟用户登录
  login({ commit }, userInfo) {
    const { username } = userInfo;
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (username === "admin" || username === "jerry") {
          commit("SET_TOKEN", username);
          localStorage.setItem("token", username);
          resolve();
        } else {
          reject("用户名、密码错误");
        }
      }, 1000);
    });
  },
  getInfo({ commit, state }) {
    return new Promise((resolve) => {
      setTimeout(() => {
        const roles = state.token === 'admin' ? ['admin'] : ['editor']
        commit('SET_ROLES', roles)
        resolve(roles)
      }, 1000);
    })
  }
};

export default {
  namespaced: true,
  state,
  mutations,
  actions,
};

2.5  用户路由权限 src/store/modules/permission.js

// 导入asyncRoutes,过滤它看当前用户是否拥有响应权限
import {asyncRoutes, constRoutes} from '@/router'

const state = {
  routes: [], // 完整路由
  addRoutes: [], // 权限路由
}

const mutations = {
  // routes: 用户可访问的权限路由
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes;
    state.routes = constRoutes.concat(routes);
  }
}

const actions = {
  generateRoutes({commit}, roles) {
    // 过滤出能访问的路由表
    const routes = filterAsyncRoutes(asyncRoutes, roles)
    commit('SET_ROUTES', routes)
    return routes;
  }
}

function filterAsyncRoutes(routes, roles) {
  const res = [];

  routes.forEach(route => {
    // 复制一份路由
    const tmp = {...route};
    // 拥有访问权限
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        // 递归子路由
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }

      res.push(tmp);
    }    
  })

  return res;
}

function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    // 路由定义中没有roles选项,则不需要权限即可访问
    return true;
  }
}

export default {
  namespaced: true, 
  state,
  mutations,
  actions
}

2.6 最终效果图

搭建Vue从Vue-cli到router路由护卫的实现

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
基于jquery实现漂亮的动态信息提示效果
Aug 02 Javascript
jQuery 阴影插件代码分享
Jan 09 Javascript
javascript中创建对象的几种方法总结
Nov 01 Javascript
jQuery学习笔记之jQuery.fn.init()的参数分析
Jun 09 Javascript
js实现仿Discuz文本框弹出层效果
Aug 13 Javascript
基于Node.js的JavaScript项目构建工具gulp的使用教程
May 20 Javascript
基于JS实现移动端向左滑动出现删除按钮功能
Feb 22 Javascript
JavaScript之map reduce_动力节点Java学院整理
Jun 29 Javascript
JavaScript 隐性类型转换步骤浅析
Mar 15 Javascript
layer.prompt使文本框为空的情况下也能点击确定的方法
Sep 24 Javascript
浅谈JavaScript中你可能不知道URL构造函数的属性
Jul 13 Javascript
解决VUE 在IE下出现ReferenceError: Promise未定义的问题
Nov 07 Javascript
关于vue.js中实现方法内某些代码延时执行
Nov 14 #Javascript
用Node写一条配置环境的指令
Nov 14 #Javascript
解决vue语法会有延迟加载显现{{xxx}}的问题
Nov 14 #Javascript
微信公众号H5之微信分享常见错误和问题(小结)
Nov 14 #Javascript
Jquery让form表单异步提交代码实现
Nov 14 #jQuery
vue之延时刷新实例
Nov 14 #Javascript
浅谈Vue.js之初始化el以及数据的绑定说明
Nov 14 #Javascript
You might like
PHPlet在Windows下的安装
2006/10/09 PHP
浅析php fwrite写入txt文件的时候用 \r\n不能换行的问题
2013/08/06 PHP
PHP之sprintf函数用法详解
2014/11/12 PHP
php连接oracle数据库及查询数据的方法
2014/12/29 PHP
php静态文件生成类实例分析
2015/01/03 PHP
php进程daemon化的正确实现方法
2018/09/06 PHP
php实现微信分享朋友链接功能
2019/02/18 PHP
web页面数据展示新想法(json)
2010/06/08 Javascript
Jquery之美中不足小结
2011/02/16 Javascript
jquery实现倒计时代码分享
2014/06/13 Javascript
使用jQuery获得内容以及内容的属性
2015/02/26 Javascript
JavaScript setTimeout使用闭包功能实现定时打印数值
2015/12/18 Javascript
原生javascript实现解析XML文档与字符串
2016/03/01 Javascript
jQuery Dialog对话框事件用法实例分析
2016/05/10 Javascript
学做Bootstrap的第一个页面
2016/05/15 HTML / CSS
点击按钮出现60秒倒计时的简单js代码(推荐)
2016/06/07 Javascript
Vuejs仿网易云音乐实现听歌及搜索功能
2017/03/30 Javascript
vue2.0 实现导航守卫(路由守卫)
2018/05/21 Javascript
在vue中读取本地Json文件的方法
2018/09/06 Javascript
使用Typescript和ES模块发布Node模块的方法
2020/05/25 Javascript
Vue 同步异步存值取值实现案例
2020/08/05 Javascript
python中zip和unzip数据的方法
2015/05/27 Python
理论讲解python多进程并发编程
2018/02/09 Python
python遍历一个目录,输出所有的文件名的实例
2018/04/23 Python
pycharm: 恢复(reset) 误删文件的方法
2018/10/22 Python
50行Python代码获取高考志愿信息的实现方法
2019/07/23 Python
使用Tensorflow将自己的数据分割成batch训练实例
2020/01/20 Python
Django操作session 的方法
2020/03/09 Python
CSS3对图片照片进行边缘模糊处理的实现
2018/08/08 HTML / CSS
Vilebrequin美国官方网上商店:法国豪华泳装品牌
2020/02/22 全球购物
药店主任岗位责任制
2014/02/10 职场文书
大学学生会竞选演讲稿
2014/04/25 职场文书
python将图片转为矢量图的方法步骤
2021/03/30 Python
使用python如何删除同一文件夹下相似的图片
2021/05/07 Python
利用python进行数据加载
2021/06/20 Python
以下牛机,你有几个
2022/04/05 无线电