搭建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 相关文章推荐
符合W3C网页标准的iframe标签的使用方法
Jul 19 Javascript
根据IP的地址,区分不同的地区,查看不同的网站页面的js代码
Feb 26 Javascript
window.print打印指定div实例代码
Dec 13 Javascript
JavaScript中判断整字类型最简洁的实现方法
Nov 08 Javascript
JS实现方向键切换输入框焦点的方法
Aug 19 Javascript
javascript拖拽应用实例(二)
Mar 25 Javascript
javaScript生成支持中文带logo的二维码(jquery.qrcode.js)
Jan 03 Javascript
JavaScript数组复制详解
Feb 02 Javascript
利用百度地图API获取当前位置信息的实例
Nov 06 Javascript
JavaScript原型对象原理与应用分析
Dec 27 Javascript
微信小程序全局变量改变监听的实现方法
Jul 15 Javascript
echarts浮动显示单位的实现方法示例
Dec 04 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+SQL 注入攻击的技术实现以及预防办法
2011/01/27 PHP
PHP设置images目录不充许http访问的方法
2016/11/01 PHP
php头像上传预览实例代码
2017/05/02 PHP
PHP连接MySQL数据库的三种方式实例分析【mysql、mysqli、pdo】
2019/11/04 PHP
PHP超级全局变量【$GLOBALS,$_SERVER,$_REQUEST等】用法实例分析
2019/12/11 PHP
使用JS CSS去除IE链接虚线框的三种方法
2013/11/14 Javascript
jquery ajax跨域解决方法(json方式)
2014/02/04 Javascript
用jquery写的菜单从左往右滑动出现
2014/04/11 Javascript
jQuery filter函数使用方法
2014/05/19 Javascript
Google官方支持的NodeJS访问API,提供后台登录授权
2014/07/29 NodeJs
jquery实现仿新浪微博带动画效果弹出层代码(可关闭、可拖动)
2015/10/12 Javascript
浅析Node.js 中 Stream API 的使用
2015/10/23 Javascript
jQuery 中的 DOM 操作
2016/04/26 Javascript
Node.js+Express配置入门教程详解
2016/05/19 Javascript
angularJs关于指令的一些冷门属性详解
2016/10/24 Javascript
angularJS深拷贝详解
2017/03/23 Javascript
Ionic3 UI组件之Gallery Modal详解
2017/06/07 Javascript
微信小程序生成海报分享朋友圈的实现方法
2019/05/06 Javascript
vue使用代理解决请求跨域问题详解
2019/07/24 Javascript
解决vue elementUI中table里数字、字母、中文混合排序问题
2020/01/07 Javascript
解决Vue-cli3没有vue.config.js文件夹及配置vue项目域名的问题
2020/12/04 Vue.js
JavaScript canvas实现跟随鼠标移动小球
2021/02/09 Javascript
儿童学习python的一些小技巧
2018/05/27 Python
python实现文件的分割与合并
2019/08/29 Python
python数据爬下来保存的位置
2020/02/17 Python
使用Python爬取弹出窗口信息的实例
2020/03/14 Python
Pyspark读取parquet数据过程解析
2020/03/27 Python
HTML5几个设计和修改的页面范例分享
2015/09/29 HTML / CSS
丹尼尔惠灵顿手表天猫官方旗舰店:Daniel Wellington
2017/08/25 全球购物
Sport-Thieme荷兰:购买体育用品
2019/08/25 全球购物
Linux如何命名文件--使用文件名时应注意
2012/01/22 面试题
自动化系在校本科生求职信
2013/10/23 职场文书
打架检讨书100字
2014/01/19 职场文书
党员个人批评与自我批评
2014/10/14 职场文书
2015年宣传工作总结
2015/04/08 职场文书
股权投资协议书
2016/03/23 职场文书