vue项目实战

142 阅读4分钟

(一)项目配置

**项目配置策略**
1.基础配置:指定应用上下文,端口号。vue.config.js
const port=7070;
module.export={
 publicPath:"/best-practice",//部署应用包时的基础
 devServer:{
   port
 }
}
2.配置webpack:configureWebpack
    1)设置一个组件存放路径的别名。vue.config.js
    const path=require('path')
    module.exports={
     resolve:{
      alias:{
       comps:path.join("_dirname",'src/components')
      }
     }
    }
    
    2)设置⼀个webpack配置项⽤于⻚⾯title,vue.config.js
    module.exports = {
     configureWebpack: {
       name: "vue项⽬最佳实践"
         }
      };
     在宿主⻚⾯使⽤lodash插值语法使⽤它,./public/index.html
     <title><%= webpackConfig.name %></title>
     
     3)基于环境有条件地配置,vue.config.js
     // 传递⼀个函数给configureWebpack
     // 可以直接修改,或返回⼀个⽤于合并的配置对象
     configureWebpack:config=>{
       config.resolve.alias.comps=path.join(_dirname,"src/components");
       if(process.en.NODE_ENV==='development'){
           config.name='vue项目最佳实践'
       }else{
          config.name=‘Vue Best Practice'
       }
     }
     
     4)chainWebpack称为链式操作,可以更细粒度控制webpack内部配置。
        4.1 下载图标 ,存⼊src/icons/svg中;
        4.2 安装依赖:svg-sprite-loader(npm i svg-sprite-loader -D)
        4.3 修改规则和新增规则,vue.config.js
          // resolve定义⼀个绝对路径获取函数
          const path = require('path');
          
          function resolve(dir) {
            return path.join(__dirname, dir)
          }
          
          chainWebpack(config) {
          // 配置svg规则排除icons⽬录中svg⽂件处理
          // ⽬标给svg规则增加⼀个排除选项exclude:['path/to/icon']
          config.module.rule("svg")
          .exclude.add(resolve("src/icons"))
          
          // 新增icons规则,设置svg-sprite-loader处理icons⽬录中的svg
          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.4使⽤图标,App.vue
          <template>
              <svg>
                  <use xlink:href="#icon-wx" />
              </svg>
          </template>
          <script>
              import '@/icons/svg/wx.svg'
          </script>
          4.5做图标的自动导入
              #创建icons/index.js
              const req = require.context('./svg', false, /\.svg$/)
              req.keys().map(req);
              
              #创建SvgIcon组件,components/SvgIcon.vue
               <template>
               <svg :class="svgClass" v-on="$listeners">
                   <use :xlink:href="iconName" />
               </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>
    5)环境变量和模式
    如果想给多种环境做不同的配置,可以利用vue-cli提供的模式。默认有developement,production,test三种模式,对应的配置文件的形式是.env.development
     5.1定义一个开发时间可用的配置项,创建.env.dev
         # 只能⽤于服务端
         foo=bar
         # 可⽤于客户端
         VUE_APP_DONG=dong
       修改mode选项覆盖模式名称,package.json
         "serve": "vue-cli-service serve --mode dev"

(二)权限

路由分为两种: constantRoutes 和 asyncRoutes ,前者是默认路由可直接访问,后者中定义的路由 需要先登录,获取⻆⾊并过滤后动态加⼊到Router中。

1.无标题.png 1)路由定义,router/index.js

2)创建用户登录页面,views/Login.vue

3)路由守卫:创建./src/permission.js,并在main.js中引入

4)维护用户登录状态:路由守卫=》用户登录=》获取token并缓存

import router from './router'
import store from './store'
const whiteList = ['/login'] // 无需令牌白名单
router.beforeEach(async (to, from, next) => {
// 获取令牌判断用户是否登录
const hasToken = localStorage.getItem('token')
// 已登录
if (hasToken) {
if (to.path === '/login') {
  // 若已登录没有必要显示登录页,重定向至首页
  next('/')
} else {
  const hasRoles = store.getters.roles && store.getters.roles.length > 0;

  if (hasRoles) {
    // 说明用户已获取过角色信息,放行
    next()
  } else {
    try {
      // 先请求获取用户信息
      const { roles } = await store.dispatch('user/getInfo')

      // 根据当前用户角色过滤出可访问路由
      const accessRoutes = await store.dispatch('permission/generateRoutes', roles)

      // 添加至路由器
      router.addRoutes(accessRoutes)

      // 继续路由切换,确保addRoutes完成
      next({ ...to, replace: true })
    } catch (error) {
      // 出错需重置令牌并重新登录(令牌过期、网络错误等原因)
      await store.dispatch('user/resetToken')
      next(`/login?redirect=${to.path}`)
      alert(error || '未知错误')
    }
  }
}
} else {// 未登录
if (whiteList.indexOf(to.path) !== -1) {
  // 白名单中路由放过
  next()
} else {
  // 重定向至登录页
  next(`/login?redirect=${to.path}`)
  }
 }
})

2.异步获取路由表。可以当⽤户登录后向后端请求可访问的路由表,从⽽动态⽣成可访问⻚⾯,操作和原来是相同的,这⾥多了⼀步将后端返回路由表中组件名称和本地的组件映射步骤:

// 前端组件名和组件映射表
const map = {
  //xx: require('@/views/xx.vue').default // 同步的⽅式
  xx: () => import('@/views/xx.vue') // 异步的⽅式
 }
// 服务端返回的asyncRoutes
const asyncRoutes = [
  { path: '/xx', component: 'xx',... }
 ]
// 遍历asyncRoutes,将component替换为map[component]
function mapComponent(asyncRoutes) {
   asyncRoutes.forEach(route => {
   route.component = map[route.component];
   if(route.children) {
  route.children.map(child => mapComponent(child))
  }
 })
}
mapComponent(asyncRoutes)

3.按钮权限

⻚⾯中某些按钮、链接有时候需要更细粒度权限控制,这时候可以封装⼀个指令v-permission,放在需 要控制的按钮上,从⽽实现按钮级别权限控制

3.1创建指令,src/directives/permission.js

import store from "@/store";
 const permission = {
    inserted(el, binding) {
   // 获取指令的值:按钮要求的角色数组
   const { value:pRoles } = binding;
    // 获取用户角色
   const roles = store.getters && store.getters.roles;
   if (pRoles && pRoles instanceof Array && pRoles.length > 0) {      
   // 判断用户角色中是否有按钮要求的角色
   const hasPermission = roles.some(role => {
      return pRoles.includes(role);
   });
  // 如果没有权限则删除当前dom
  if (!hasPermission) {
    el.parentNode && el.parentNode.removeChild(el);
    }
   } else {
    throw new Error(`需要指定按钮要求角色数组,如v-permission="['admin','editor']"`);
    }
  }
 };

export default permission;

3.1指令只能删除挂载指令的元素,对于那些额外⽣成的和指令⽆关的元素⽆能为⼒.此时只能通过v-if来实现。

<template>
<el-tab-pane v-if="checkPermission(['admin'])">
</template>
<script>
export default {
    methods: {
     checkPermission(permissionRoles) {
         return roles.some(role => {
         return permissionRoles.includes(role);
      });
    }
   }
 }
</script>