论坛项目动态路由菜单以及渲染
用户登录全局前置拦截器获取用户的菜单以及接口执行过程
   解析菜单数据,渲染伟动态路由。
菜单数据
   将数据源解析为类似路由配置对象的格式(./xxx/xxx 这种格式)。
下方是路由实例的代码,后面封装了很多方法这里也需要做响应的改变
//创建路由实例
// /login
// /admin/login
const router = new vueRouter({
  mode: "hash",
  routes,
  base: "/admin",
  // 配置滚动的---- 用户切换路由组件回到顶部
  scrollBehavior() {
    //带返回值   x  y 指坐上角坐标
    return { x: 0, y: 0 };
  },
});
//设置守卫
//定义白名单
const whiteList = ["/login", "/error"];
//定义变量接收菜单
let menu = null;
router.beforeEach(async (to, from, next) => {
  //白名单路由不检测token直接放行
  //读取token
  let token = findStorage("_token");
  //判断token
  if (token) {
    //用户登录过
    //获取系统菜单数据 实现路由的动态绑定
    if (!menu) { //加这个判断的目的就是防止用户不停地访问路由
      //ajax 获取数据
      let { menuList } = await userMenu();
      //数据解析
      let newMenu = patterMenu(menuList);//把通过ajax获取的数据传递回去,这个写法是es6的对象结构,相当于把menuList拿出来
      //进行menu赋值
      menu = newMenu;
        //写入缓存(因为菜单组件需要使用数据)  需要转化字符串 存储,因为在封装的h5哪里没有改变字符串所以在这改
      setStorage("_menu", JSON.stringify(menu));
      //解析转化的数组menu 添加动态路由配置
      for (let i = 0; i < menu.length; i++) {
        addRouter(menu[i].children);
      }
      //添加完动态路由之后  重新跳转
      next({ path: to.path });
    } else {
      //如果进入的是登录界面  直接到系统首页
      if (to.path == "/login") {
        next({ path: "/" });
      }
      next();
    }
  } else {
    //用户未登录
    if (whiteList.indexOf(to.path) !== -1) {
      next();
    } else {
      next({ path: "/login", query: { redirect: to.path } });
    }
  }
});
 
 
 
 使用递归方法解析菜单数据(这是一个封装 在router下的index.js文件中)
/**
 * 解析对象数据
 * @params {menu集合数据} list
 * @return {返回对象}
 * **/
function patterMenu(list) {
   //定义数组,因为要返回一个全新的数组
  let arr = [];
 //检测list集合是否存在 ,如果服务器崩了就没有 数据了,那就给他返回一个空集合
  if (!list) return [];
 //遍历数组,目的是拿出每一个具体的数据
  list.forEach((item, index) => {
    //定义对象
    let obj = {//拿出要的数据,不要的数据过滤掉
      children: item.list,//路由的子集叫children,所以这里定义为children
      path: item.url,
      name: item.name, //菜单中也要有name,所以这里也要写name
      icon: item.icon, //菜单左边还有图标,所以这里写icon
    };
    //使用递归算法  进行children集合转化 ,因为children也跟父及一样需要改成路由的形式
    //有的菜单下有children有的菜单下没有,所以还要再去做判断。有的有children属性,但是下面没有子集
    if (obj.children && obj.children.length) {//obj.children属性存在,并且obj.children的长度是存在的
      obj.children = patterMenu(obj.children);//把内层转换的集合,赋给原本的集合
    }
    arr[index] = obj;
  });
  return arr;
}
 
   将解析的菜单数据 关联变量menu,防止用户跳转路由多次发送请求
           //进行menu赋值
      menu=newMenu;
      //写入缓存  需要转化字符串 存储
      setStorage("_menu",JSON.stringify(menu));
      //缓存的目的是菜单组件需要使用数据
 将解析的菜单数据 添加到路由的配置
//获取路由的配置
//获取路由的静态配置
router.options.routes;
 
   获取路由的所有配置
router.getRoutes();//包含静态配置和动态配置
 
   解析菜单数据为路由配置格式
/**
 * 实现动态路由添加
 * @params {menu集合数据} list
 * **/
function addRouter(list) {//这里的list就是一个children的集合
  for (let i = 0; i < list.length; i++) {
    console.log(list[i]);//拿到children的每一个值放到变量now中
    let now = list[i];
    let route = {
      path: now.path,
      component: loadView(now.path),//把每一个children中的path拿出来,调用下面的方法把格式改成路由格式
    };
  }
}
 同时封装根据路径加载路由组件方法
/**
 * 根据路径懒加载组件
 * @params {组件路径} url
 * **/
function loadView(url) {
  console.log(`../views/${url}.vue`);
  //前去看webpack 英文版官方 方法解析用法 这里建议最好使用变量来存储这个对象,这里因为传进来的是个路径,而且是个变量,所以刚还写在这里,如果不是就声明一个变量去接收,然后把变量写到${}中
  return () => import(`../views/${url}.vue`);
}
//需要注意import 懒加载方法用法
 
   最终将转化的路由配置添加到路由
   /**
 * 实现动态路由添加
 * @params {menu集合数据} list
 * **/
function addRouter(list) { //这里的list就是一个children的集合
  for (let i = 0; i < list.length; i++) { 
    let now = list[i];//拿到children的每一个值放到变量now中
    let route = {
      path: now.path,
      component: loadView(now.path), //把每一个children中的path拿出来,调用下面的方法把格式改成路由格式
    };
    // 将解析之后的对象添加给路由配置
    //调用add方法,第一个参数是父路由的名称,所以要去router下的routers.js文件中给admin加一个name属性,属性为admin。
    router.addRoute("admin", route); 
  }
}
/**
 * 根据路径懒加载组件
 * @params {组件路径} url
 * **/
function loadView(url) {
  //前去看webpack 英文版官方 方法解析用法 这里建议最好使用变量来存储这个对象,这里因为传进来的是个路径,而且是个变量,所以刚还写在这里,如果不是就声明一个变量去接收,然后把变量写到${}中
  return () => import(`../views${url}.vue`);
}
 
 查看所有的路由配置
console.log(router.getRoutes());
 
   当前系统存在/—-/login 登录 /login—-/ 会产生路由多次定向错误导致路由无法进入。
   重写replace或者push的底层方法 抓异常抛出
//获取replace 方法 进行重写
let replaceOrigin = vueRouter.prototype.replace;
//重写replace 方法
/**
 * location 参数  push  replace   ({path:"/"})
 * **/
vueRouter.prototype.replace = function (location) {
  console.log("测试", location);
  //目的是抓异常
  return replaceOrigin.call(this, location).catch((error) => error);
};
 可能会存在,异常处理了,但是登录路由不跳转,需要在守卫中添加添加类似重定向的业务代码
   系统菜单的渲染
获取用户登录之后的数据缓存_menu
   注意后端返回的数据path路径没有带/,会导致路由路径多余sys ,处理:
   
   处理scrollbar
   实现header功能
   使用的是组件的自定义事件通信完成
   
   


















