|
|
1. 技术栈说明vue2.6 + vue-router + vuex + element-ui
2. 起头:新建项目条件条件:在小我电脑上安装好nodejs(我的是14.15.1)以后,操纵nodejs自带的npm包治理器安装好vue(我的是@vue/cli 4.5.12)
在号令行中经过以下指令在指定目录下安装脚手架vue-cli[/ol]npm install -g @vue/cli
利用vue的建立项目号令,vue create xxx (xxx是指项目称号)[/ol]挑选项目所需要的插件? Check the features needed for your project:◉ Choose Vue version // 挑选vue版本◉ Babel // 支持babel◯ Typescript // 支持利用 Typescript 誊写源码◯ Progressive Web App (PWA) Support // PWA 支持◉ Router // 支持 vue-router◉ Vuex // 支持 vuex◯ CSS Pre-processors // 支持 CSS 预处置器他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。◉ Linter / Formatter // 支持代码气概检查和格式化他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。◯ Unit Testing // 支持单元测试他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。◯ E2E Testing // 支持 E2E 测试他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。// 留意:你要集成什么就选就行了(注:空格键是选中与取消,A键是全选)
挑选vue的版本,由于vue3今朝只出来了8个月左右,受众面不广,所以挑选vue 2.x版本至此,项目搭建完成,可以cd翻开项目,run起来了他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
3. 增加element-ui以及nprogress和normalize.css和设置vue.config.js先安装element-ui,nprogress和normalize.cssnpm install element-ui nprogress normalize.css
由于element-ui利用到了sass-loader,所以这里也是需要安装的
npm install sass-loader
当前项目插件以下:
设置vue.config.jsvue.config.js 是一个可选的设置文件,假如项目标 (和 package.json 同级的) 根目录中存在这个文件,那末它会被 @vue/cli-service 自动加载他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。你也可以利用 package.json 中的 vue 字段,可是留意这类写法需要你严酷遵照 JSON 的格式来写他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
在根目录中建立 vue.config.js
官方设置vue.config.js的具体详解
'use strict'const path = require('path')function resolve(dir) {return path.join(__dirname, dir)}// All configuration item explanations can be find in https://cli.vuejs.org/config/module.exports = {publicPath: '/', // 摆设利用包时的根基 URL,用法和 webpack 自己的 output.publicPath 分歧outputDir: 'dist', // 构建输出目录(打包位置)assetsDir: 'static', // 放置天生的静态资本(js,css,img,fonts)的(相对于outputDir)的目录lintOnSave: false, // 能否校验语法productionSourceMap: false, // 假如你不需要生产情况的 source map,可以将其设备为 false 以加速生产情况构建devServer: {port: 8888,open: true,},configureWebpack: { // 绝对途径resolve: {alias: {'@': resolve('src')}}}}
3. 功用实现先讲思绪,让大伙有个大要的印象,不至于看代码云里雾里他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
分以下几步走:
前端在当地写好路由表,以及每个路由对应的脚色,也就是哪些脚色可以看到这个菜单 / 路由他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。登录的时辰,向后端请求获得登录用户的脚色(治理者,普通用户)操纵路由保卫者(router.beforeEach),按照取到的用户脚色,跟当地的路由表停止对照,过滤出用户对应的路由,并操纵路由停止菜单衬着
我们将贮存在将storage中的token作为用户能否登录的标志,假如当前storage中有token,表白当前系统已被登录将系统一切页面分为两类,需要登录才能检察的页面,不需要登录的login.vue, register.vue等前端每次跳转路由时,做以下判定:[/ol]接下来从技术栈的角度补充几点:
在vue-router的beforeEach方式中实现以上逻辑,判定前端跳转去向;出于教程斟酌,不引入后端,用模拟数据的用户信息作为阻挡axios倡议的办事请求响应;经过window.localStorage.setItem做userInfo的状态治理;[/ol]4. 实现按照上述的步调,我们停止每一个步调的实现
1. 写好mock数据,用以模拟后端返回的数据源dynamicUser里面就是模拟的后端数据,一般的背景数据库里面,就是分为一个user用户表,一个role权限路由表,这里不触及后端,所以只给出最初后端输出的数据源他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
一个完整的后端数据示例以下:
const dynamicUser = [{name: "治理员",avatar: "https://p3.toutiaoimg.com/img/user-avatar/ccb565eca95535ab2caac9f6129b8b7a~300x300.image",desc: "治理员 - admin",username: "admin",password: "654321",token: "rtVrM4PhiFK8PNopqWuSjsc1n02oKc3f",routes: [{ id: 1, name: "/", path: "/", component: "Layout", redirect: "/index", hidden: false, children: [{ name: "index", path: "/index", meta: { title: "index" }, component: "index/index" },]},{ id: 2, name: "/form", path: "/form", component: "Layout", redirect: "/form/index", hidden: false, children: [{ name: "/form/index", path: "/form/index", meta: { title: "form" }, component: "form/index" }]},{ id: 3, name: "/example", path: "/example", component: "Layout", redirect: "/example/tree", meta: { title: "example" }, hidden: false, children: [{ name: "/tree", path: "/example/tree", meta: { title: "tree" }, component: "tree/index" },{ name: "/copy", path: "/example/copy", meta: { title: "copy" }, component: "tree/copy" }] },{ id: 4, name: "/table", path: "/table", component: "Layout", redirect: "/table/index", hidden: false, children: [{ name: "/table/index", path: "/table/index", meta: { title: "table" }, component: "table/index" }] },{ id: 5, name: "/admin", path: "/admin", component: "Layout", redirect: "/admin/index", hidden: false, children: [{ name: "/admin/index", path: "/admin/index", meta: { title: "admin" }, component: "admin/index" }] },{ id: 6, name: "/people", path: "/people", component: "Layout", redirect: "/people/index", hidden: false, children: [{ name: "/people/index", path: "/people/index", meta: { title: "people" }, component: "people/index" }] }]},{name: "普通用户",avatar: "https://p3.toutiaoimg.com/img/user-avatar/6364348965908f03e6a2dd188816e927~300x300.image",desc: "普通用户 - people",username: "people",password: "123456",token: "4es8eyDwznXrCX3b3439EmTFnIkrBYWh",routes: [{ id: 1, name: "/", path: "/", component: "Layout", redirect: "/index", hidden: false, children: [{ name: "index", path: "/index", meta: { title: "index" }, component: "index/index" },]},{ id: 2, name: "/form", path: "/form", component: "Layout", redirect: "/form/index", hidden: false, children: [{ name: "/form/index", path: "/form/index", meta: { title: "form" }, component: "form/index" }]},{ id: 3, name: "/example", path: "/example", component: "Layout", redirect: "/example/tree", meta: { title: "example" }, hidden: false, children: [{ name: "/tree", path: "/example/tree", meta: { title: "tree" }, component: "tree/index" },{ name: "/copy", path: "/example/copy", meta: { title: "copy" }, component: "tree/copy" }] },{ id: 4, name: "/table", path: "/table", component: "Layout", redirect: "/table/index", hidden: false, children: [{ name: "/table/index", path: "/table/index", meta: { title: "table" }, component: "table/index" }] },{ id: 6, name: "/people", path: "/people", component: "Layout", redirect: "/people/index", hidden: false, children: [{ name: "/people/index", path: "/people/index", meta: { title: "people" }, component: "people/index" }] }]}]export default dynamicUser
由此可以看出,一般登录后,返回的数据里,包括了一个用户的姓名,头像,简述以及token(username和password只是用以模拟登录用到的数据,在一般营业流中,后端不成能带出来的他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。),routes就是admin治理员和people普通用户的差别化静态路由了,admin多了一个admin的页面,而people是没有的他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
实在这里是有多种思绪的
有些开辟者喜好完整的静态路由都在前端里面,然后按照router的meta属性,写上对应user的role,登录的时辰,再按照后端返回的权限,去过滤比对权限,把该用户脚色所对应的路由处置好,衬着处置,这也是支流的一种处置方式他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。这类就等因而把一切的路由和权限营业处置都放在了前端,一旦上线公布后,想要点窜就需要重新打包处置,而且不能经事背景静态新增删除
例如:
//代码位置:router/index.js{path: '',component: layout, //整体页面的结构(包括左侧菜单跟主内容地区)children: [{path: 'main',component: main,meta: {title: '首页', //菜单称号roles: ['user', 'admin'], //当前菜单哪些脚色可以看到}}]}
还有一种解法,就是一切的路由权限等,都交给后端,后端按照前真个账号密码,去获得脚色权限,处置路由,丢出就是已经婚配对应脚色的路由了他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。这类写法前端运算量不会太大,而且易于点窜和前期保护以及静态的增删改查,本文就是以该种形式实现他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
2. 模拟用户登录,获得用户的权限和路由在main.js里面,引入该页面,用于做路由保卫者[/ol]import Vue from "vue"import App from "./App.vue"import router from "./router"import store from "./store"import ElementUI from "element-ui"import 'element-ui/lib/theme-chalk/index.css'import "./router/router-config" // 路由保卫,做静态路由的地方Vue.config.productionTip = falseVue.use(ElementUI)new Vue({router,store,render: (h) => h(App),}).$mount("#app")
登录[/ol]原本我是写了mock数据,模拟用户登录,请求后端脚色的接口,何如mock挂了,
所以我就间接模拟了:
取到用户脚色,寄存进localStorage,然后跳转主页
在这里,由于用了element-ui的form表单提交,所以间接this.$refs.userForm.validateelement-ui的form表单提交文档!
这里的dynamicUser是mock的数据流,一般后端是间接间接返回对应的成果,可由于fastmock轻易挂掉,所以就间接手写mock了他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。界说flag用于做登录校验,假如循环都找不到对应的username和password的话,就告诉用户,该账号密码毛病,登录失利..可倘使有一次是成功的,那末flag就是为!0的,而且返回对应的用户信息,用户路由等他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。最初还会停止路由的跳转初始化页面(首页),并停止静态路由加载和路由跳转他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
import dynamicUser from "../../mock"import { Message } from "element-ui"login() {this.$refs.userForm.validate(( valid ) => {if(valid) {let flag = !1window.localStorage.removeItem("userInfo")dynamicUser.forEach(item => {if(item["username"] == this.user['username'] && item["password"] == this.user['password']) {flag = !0Message({ type: 'success', message: "登录成功", showClose: true, duration: 3000 })window.localStorage.setItem("userInfo", JSON.stringify(item))// 这里用catch捕捉毛病,而且不打印,诠释鄙人方this.$router.replace({ path: "/" }).catch(() => {})}})if(!flag) Message({ type: 'warning', message: "账号密码毛病,请重试!", showClose: true, duration: 3000 })} else return false})}
诠释:假如不捕捉catch毛病,而且不打印的话,就会出现如图所示的毛病他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
缘由:vue-router路由版本更新发生的题目,致使路由跳转失利抛出该毛病,但并不影响法式功用
处理计划1:在利用编程式导航跳转时,每次利用,前面都跟上.catch方式,捕捉毛病信息this.$router.push("/xxx").catch(() => {})
处理方式2:全局处理,替换路由的Push和replace方式,放在src/router/index.js中:
const originalPush = VueRouter.prototype.pushVueRouter.prototype.push = function push(location, onResolve, onReject) {if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject)return originalPush.call(this, location).catch(err => err)}
处理计划3:对vue-router的版本下降到3.0.7,手动点窜,然后删除node_modules,改完再npm install
点窜成:
3. (重点)路由保卫者阻挡beforeach, 并静态衬着前途由表1. 在router文件夹下,建立router.config.js文件,用于做路由保卫者的阻挡页面2. 引入router,Layout,NProgress三个插件router说明router是援用router/index.js里面导出的router
在router/index.js里面, router是new vue-router,相当于vue-router工具
layout说明这是页面的大致框架,具体页面详情以下
NProgress是进度条插件import router from "./index"import Layout from "../layout/index"import NProgress from 'nprogress' // progress barimport 'nprogress/nprogress.css' // progress bar style
在router的beforeEach里面别离有三个参数to, form和next,别离对应着去哪儿,从哪儿来,下一步接下来,按照去哪儿(to), 就需要判定路由指向能否需要过滤的路由地址数组里,假如在,则间接进入页面,无需判定,例如登录页面, 注册页面, 找回密码等(具体看营业需求)const filterRoutes = ["/login"]if (filterRoutes.indexOf(to.path) !== -1) {// 假如是无需权限的静态路由,可以间接跳走next()return false}
然后就是进入静态路由首要部分了,首先判定当前路由栈的数目,假如路由栈的数目即是你在router/index.js里面的静态路由的数目,那末表白当前仍未加载静态路由,需要处置路由了,反之,则可以让它间接进入循环// 由于我今朝的教程里面,只是做了一个login的登录页面,所以静态页面也是唯一一个而已if (router.options.routes.length == 1) {// 此处静态加载路由} else next() // 表白路由已加载,可间接进入页面
当路由未加载时,就需要获得登录时缓存的token和路由栈,由于革新的时辰,vuex的数据没法持久化,所以倡议最好routes和token都放在缓存storage里面,固然,cookies里面也是可以的,可是这样的话,阅读器一旦封闭,那末下次翻开就需要重新登录了他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。// 获得token和原始路由数组// 这里需要做空值合并操纵,避免路由存在时,可token已生效,然后JSON.parse转义失利的情况致使的报错const userInfo = JSON.parse(window.localStorage.getItem("userInfo")) ?? ""// 当token和原始路由都存在的时辰// 进入路由履行路由过滤和跳转封装函数// 否则,跳回登录页面if(userInfo.token && userInfo.routes) onFilterRoutes(to, next, userInfo.routes)else next({ path: "/login", replace: true })
Tips小常识
空值合并操纵符( ?? )
只要当左侧为null和undefined时,才会返回右侧的数
空值合并操纵符(??)是一个逻辑操纵符,当左侧的操纵数为 null大概 undefined时,返回其右侧操纵数,否则返回左侧操纵数他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
当进入路由过滤和跳转封装的时辰先履行异步请求,确保路由过滤和途径补全已完成他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。先把routes传入递归函数(filterASyncRoutes),用于做途径的补全和Layout的判定并赋值,而且当routes存在children(子级路由)的时辰,路由需要再次回调递归函数(filterASyncRoutes),最初并把处置好的路由栈,返回给路由过滤函数按照异步请求返回的routes,停止路由的排序,究竟当用户静态处置了路由后,展现出来的顺序跟处置时的顺序纷歧致,那就不太好了他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。路由都处置完成后,把路由循环,并静态增加进router.options.routes里面,而且路由router里面,要利用addRoute(item),把路由一点点增加进路由内外他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。最初履行路由跳转,跳回当前需要跳转的页面[/ol]// 路由拼接function loadView(view) {return () => import(`@/views/${ view }`)}// 路由过滤和跳转async function onFilterRoutes(to, next, e) {const routes = await filterASyncRoutes(e) // 路由过滤routes.sort((a, b) => a['id'] - b['id'])routes.forEach(item => {router.options.routes.push(item)router.addRoute(item)})next({ ...to, replace: true })}// 路由过滤 遍历路由 转换为组件工具和途径function filterASyncRoutes(data) {const routes = data.filter(item => {if(item["component"] === "Layout") item.component = Layoutelse item["component"] = loadView(item["component"])// 路由递归,转换组件工具和途径if(item["children"] && item["children"].length > 0) item["children"] = filterASyncRoutes(item.children)return true})return routes}
tips:为什么利用router.addroute,而不利用router.addRoutes[/ol]新版本router.addRoutes已烧毁:利用 router.addRoute() 取代他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
官方的诠释是 router.addRoute 接管的是一个路由法则,也就是一个工具,大概接管一个字符串和一个工具他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
为什么要利用() => import(@/views/${ view })来做路由拼接[/ol]懒加载:又叫延时加载,即在需要的时辰停止加载,随用即载
【相关题目】import() webpack4懒加载利用变量报错处理:https://www.cnblogs.com/chenxi188/p/13662036.html
import和require的区分
node编程中最重要的思惟就是模块化,import和require都是被模块化所利用他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
遵守标准require 是 AMD标准引入方式import是es6的一个语法标准,假如要兼容阅读器的话必须转化成es5的语法挪用时候require是运转时挪用,所以require理论上可以应用在代码的任何地方import是编译时挪用,所以必须放在文件开首本质require是赋值进程,实在require的成果就是工具、数字、字符串、函数等,再把require的成果赋值给某个变量import是解构进程,可是今朝一切的引擎都还没有实现import,我们在node中利用babel支持ES6,也仅仅是将ES6转码为ES5再履行,import语法会被转码为require
项目&源码源码地址(gitee):https://gitee.com/lemonote/vue2-dynamic-routing
项目地址:https://dynamic.lemonotes.cn/#/login
整体流程走完了,再轻易让人蒙的地方1. 按照路由停止菜单展现
代码位置:/src/Layout/sideBar/sidebaritem.vue,
先看下elementUI菜单组件,把一些根本的参数先领会一下,
这里我把菜单衬着写成了一个组件:
用到了递归属性,保证可以天生多级菜单,
我倡议不熟悉的,大师用组件先模拟着写一个包括跳转功用、icon展现的菜单,然后再看我写的组件
2. 用户退出系统
代码位置:/src/layout/headerTemp/index.vue
退出的时辰,记得断根掉存在localStorage的用户脚色,
然后操纵this.$router.replace({ path: "/login" })跳转到登录页,
为什么要用location.reload(),这样会把之前addRoute的路由断根掉,确保下个用户登陆后,会重新衬着正确的菜单
// 退出登录handleLogout(key) {if(key == "logout") {window.localStorage.removeItem("userInfo")Message({ type: 'success', message: "退出登录", showClose: true, duration: 3000 })this.$router.replace({ path: "/login" })location.reload()}}
3. 为什么不用vuex
原本确切是筹算用vuex来做路由的处置的,可是后来发现,当阅读器手动革新大概被动革新的时辰,vuex没法做数据持久化,简而言之,就是vuex里面的state的值会被清空,所以为了稳妥起见,我是挑选了缓存storage来处置路由题目他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。
若有不正确的地方,还望小伙伴斧正哈
最初公众号:小何长大,佛系更文,都是自己已经踩过的坑大概是学到的工具
有爱好的小伙伴接待关注我哦,我是:何小玍他早就发现系统有个隐藏的缝隙私下花了好几个早晨优化了代码。大师一路进步鸭
|
|