搭建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 EasyUI API 中文文档 - Spinner微调器使用
Oct 21 Javascript
Javascript的各种节点操作实例演示代码
Jun 27 Javascript
jquery 表格排序、实时搜索表格内容(附图)
May 19 Javascript
详解JavaScript函数
Dec 01 Javascript
Angular设置title信息解决SEO方面存在问题
Aug 19 Javascript
Angular 理解module和injector,即依赖注入
Sep 07 Javascript
基于AngularJS实现iOS8自带的计算器
Sep 12 Javascript
JavaScript生成验证码并实现验证功能
Sep 24 Javascript
VUE element-ui 写个复用Table组件的示例代码
Nov 18 Javascript
vue 注册组件的使用详解
May 05 Javascript
vue中使用gojs/jointjs的示例代码
Aug 24 Javascript
Electron autoUpdater实现Windows安装包自动更新的方法
Dec 24 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
PHP生成静态页面详解
2006/11/19 PHP
PHP实现格式化文件数据大小显示的方法
2015/01/03 PHP
php通过asort()给关联数组按照值排序的方法
2015/03/18 PHP
php自定义函数br2nl实现将html中br换行符转换为文本输入中换行符的方法【与函数nl2br功能相反】
2017/02/17 PHP
jQuery UI的Dialog无法提交问题的解决方法
2011/01/11 Javascript
jquery里的正则表达式说明
2011/08/03 Javascript
jquery动态加载select下拉框示例代码
2013/12/10 Javascript
从数组中随机取x条不重复数据的JS代码
2013/12/24 Javascript
IE10中flexigrid无法显示数据的解决方法
2015/07/26 Javascript
理解jquery事件冒泡
2016/01/03 Javascript
JS递归遍历对象获得Value值方法技巧
2016/06/14 Javascript
微信小程序-图片、录音、音频播放、音乐播放、视频、文件代码实例
2016/11/22 Javascript
JavaScript用构造函数如何获取变量的类型名
2016/12/23 Javascript
angular4 如何在全局设置路由跳转动画的方法
2017/08/30 Javascript
javascript深拷贝、浅拷贝和循环引用深入理解
2018/05/27 Javascript
Layer弹出层动态获取数据的方法
2018/08/20 Javascript
vue项目持久化存储数据的实现代码
2018/10/01 Javascript
JointJS JavaScript流程图绘制框架解析
2019/08/15 Javascript
JS实现使用POST方式发送请求
2019/08/30 Javascript
支付宝小程序实现省市区三级联动
2020/06/21 Javascript
原生js实现购物车
2020/09/23 Javascript
微信小程序接入vant Weapp组件的详细步骤
2020/10/28 Javascript
在nuxt中使用路由重定向的实例
2020/11/06 Javascript
浅谈python脚本设置运行参数的方法
2018/12/03 Python
python用线性回归预测股票价格的实现代码
2019/09/04 Python
python3 常见解密加密算法实例分析【base64、MD5等】
2019/12/19 Python
TensorFlow 多元函数的极值实例
2020/02/10 Python
CSS3混合模式mix-blend-mode/background-blend-mode简介
2018/03/15 HTML / CSS
CSS3感应鼠标的背景闪烁和图片缩放动画效果
2014/05/14 HTML / CSS
厨师岗位职责
2013/11/12 职场文书
怎样写好自我评价呢?
2014/02/16 职场文书
2014年社区学雷锋活动总结
2014/03/09 职场文书
安全标兵事迹材料
2014/08/17 职场文书
2014最新房贷收入证明范本
2014/09/12 职场文书
自荐信模板大全
2015/03/27 职场文书
一行代码python实现文件共享服务器
2021/04/22 Python