|

再谈Vue单页面应用的路由、组件和权限优化

上一篇的文章有介绍了 Vue 单页面应用的权限管理方案,为了方便快速讲解,上一篇文章中实现是方案雏形,并非项目中真实方式。真正用在项目中的,还需要考虑性能和载入方案等一些因素,这篇文章我将介绍这部分的实现,这些方案也应用在我最近负责的工业互联网项目中。

为什么优化

理由其实很简单。

当一个 Web 应用的功能越来越多时,也就滋味这整个 SPA 的体积也就越来越大,单次加载的成本也就越来越高,总体来说就是影响用户体验。所以需要对整个页面应用尽心优化,并期望达到以下目的:

  • 减少应用加载时用户的等待时间;
  • 提供友好的交互;
  • 减少代码体积,减少冗余,减少耦合;
  • 增强可维护性;
  • 增强安全性;
  • 有助于业务数据的统计,能辅助业务分析;

这几个方面其实我之前的文章也有涉及,只是未成系统,今天的文章将试着较为系统的整理一下。

如何优化

针对上面所列的不同点,优化方案也是略有不同的。例如,减少用户等待时间,可以通过减少打包体积来实现;提供友好的交互,则可以通过框架图和载入动画来实现;增强安全性则可以加入权限管理;数据埋点上报则可以借助 Vue 的自定义事件或者混入器来批处理。

普通页面的懒加载

路由页面懒加载,组件懒加载,控制粒度,避免长时间等待,用户友好

整体上来讲,懒加载不是为了减少所有页面的用户等待时间,而是为了减少页面初次载入的时间,提高用户体验。实现页面懒加载又分为路由页面懒加载和组件懒加载,一般来说,为了能在初次加载时提供良好的体验,我们在打包时会将需要用户触发的路由放到懒加载里实现。

实现懒加载的方式也很简单,可以借助 webpack 的 import() 方法来实现,这个方法在 webpack 打包过程中就可以把 import 里的组件按照新的 bundle 来打包,进而提供一个懒加载的文件。

之前的文章也对懒加载有一些介绍,比如我们可以把组件用异步的形式加载进来,下面的代码使用了 require 来引入:

export default {
  components: {
    HelloWorld: resolve => (require(["@/components/HelloWorld.vue"], resolve))
  }
}

当然使用 import 也是可以的,webpack 的 import 方法默认返回一个 Promise ,所以直接 return 即可。

export default {
  name: 'Home',
  components: {
    HelloWorld: () => import( "@/components/HelloWorld.vue")
  }
}

同理路由组件也可以按需来引入。

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  }
]

权限页面的懒加载

上面说的是对于普通的路由页面或者组件,可以进行懒加载,其实很多时候,我们在做前端权限管理的时候,用户可访问的路由和功能组件也是懒加载的。

举个例子,一个普通用户可以访问 个人中心 页面,但是不能访问 用户管理 页面,所以不能访问的页面就不加载,可以访问的页面再懒加载,这样就能提高首次载入速度,提高用户体验。

另一方面,对于前段来说,代码是暴露的,所以我们不能给所有用户都打包完整的 SPA 代码,而是应该按需打包,按需引入。这里的按需打包不是说要给每一种不同的用户都打包一份代码,而是按照不同用户之间页面的交集,来实现一个最小打包。

以工业互联网的项目为例,任何用户,登录到系统以后都是可以看到设备列表页面的,这个页面也是一个 index 页面,用户登录进来后就会跳转到这里。所以这个页面连带登录和注册页面这些公共的部分就可以一起打包发布,因为这是所有用户的交集页面,处了这些公共页面以外的不是,则可以按照不同的粒度来异步加载。

模块化

这里的模块化主要指组件的模块化,组件的诞生就是为了分割代码功能,把具有一定功能的模块作为组件,这有助于功能解耦。

如果加上之前文章中所将的组件权限管理,那么组件权限也将是一个模块,来跟对应的组件来隔离,就好比 CSS 与 HTML 的隔离一样。

这样的好处也是显而易见的,组件代表着功能,而功能是否能被使用则通过另外一个文件来定义,这样即便组件有变化,也不会影响到其权限文件,这边是业务上的解耦了。

微应用

微应用是微前端的概念,也属于模块化范畴。微应用就是让前端页面仿照后端的微服务的概念,将一个大型前端项目进行拆分,然后根据路由进行懒加载。

既然是懒加载,那跟路由懒加载有什么区别呢?

区别还是有的,从微前端的核心思想来说,区别有二:

  1. 懒加载的子应用不限制技术栈,不限制实现方案;
  2. 子应用可以独立开发,独立维护和独立运行。

从上面两个点来说,Vue 中的路由懒加载就是实习不了的。Vue 的路由懒加载首先是限制了这是一个 Vue 应用,所以限制了技术栈;第二点,一个懒加载的路由也不太方便独立运行、独立维护和独立开发,路由的组件原则上还是大项目的一部分,所以这就是本质的区别。

数据埋点

数据埋点是为了做业务统计,之前有文章讲过有关数据埋点的问题和解决方案。总的来说,数据上报可以分为自动上报的数据和需要主动上报的数据,数据埋点则是为了主动上报的数据,在开发时我们进行主动埋点。

埋点的可以通过 Vue 的自定义指令来实现,也可以通过 Vue 的混入器来实现,只不过这两个方案对应着不同的统计粒度,开发者还需根据业务需求来进行选择。

例如使用自定义指令,则可以定义一个 databoss 指令,在需要埋点的地方加上 v-databoss 同时也可以在这个组件加上 data- 属性,以便可以获取埋点的数据。

<template>
  <div class="home">
    <meter-table data-report="postData" v-databoss/>
  </div>
</template>

亦或者,通过在 v-databoss 上绑定数据或者函数,来决定上报的数据。

<template>
  <div class="home">
    <meter-table v-databoss="postData"/>
  </div>
</template>

然后在组件中定义对应的上报数据。

export default {
  data: () => {
    return {
      // 上报数据为一个 JSON 对象
      postData: {
        pagename: 'mainpage',
        event: 'meter_table_click',
      }
    }
  }
}

具体方案可参考之前的文章。

使用自定义指令和混入器

这里的自定义指令和混入器,已经不单单是为数据埋点了,而是更高级的用法。

理解了 Vue 中自定义指令和混入器的使用及其原理后,我们发现定义的指令和混入器完全可以当做插件来使用。以组件权限为例,之前的文章中,我们定义组件的权限还需在组件里进行引用赋值,组件权限同步的时候也需要在组件内完成。

import defaultPermisson from "@/permisson/sl-button"

export default {
  name: 'sl-buttom',
  data: () => {
    return {
      permission: {}
    }
  },
  created() {
    let userPermis = this.store.getters.userPermis;
    let userinfo = this.store.getters.userinfo;
    let groupid = userinfo.groupid;
    let uid = userinfo.uid;
    this.permission = userPermis.indexOf(this.name) ? userPermis[this.name] : defaultPermisson[groupid];
    this.permissionGette(uid).then(userPermissions => {
        this.store.commit('updateUserPermisson', userPermissions);
        this.permission = userPermissions;
    })
  }
}

例如上面的代码,我们是在 created 生命周期函数中完成的权限设置,虽然这是一个很简单的例子,只有一个组件,不过我们试想如果不是一个组件,而是几十个,如果全部通过在每个组件的 created 生命周期函数中来完成设置肯定是不合理的。

这个时候就要请出 Vue 的自定义指令或者混入器了。这两种方案使用场景不同,但都是为了简化代码和解耦,我拿混入器为例。

假设有下面的固定菜单,我们希望是否显示。

<Submenu name="1">
    <template slot="title">
        <Icon type="ios-paper" />
        采集管理
    </template>
    <MenuItem name="metertype">
        <Icon type="ios-arrow-forward" />
        表计类型管理
    </MenuItem>
    <MenuItem name="metermodel">
        <Icon type="ios-arrow-forward" />
        表计型号管理
    </MenuItem>
    <MenuItem name="serialconf">
        <Icon type="ios-arrow-forward" />
        通讯参数管理
    </MenuItem>
</Submenu>

菜单的某些项目对有些用户是可见的,对有些用户是不可见的,如何能快速的进行设置呢?这是便可以借助混入来实现。

假设 permission 对象是默认的权限对象,actions 是操作定义。

Vue.mixin({
  methods: {
    permissionOnShow (name) {
      if (store.getters.actions) {
        return name in store.getters.actions;
      }
      return name in persmission.actions;
    }
  }
})

由于上面的汇入是一个全局所以任意一个需要进行鉴权的组件都可以使用,我们修改上述的菜单项。

<Submenu name="1">
    <template slot="title">
        <Icon type="ios-paper" />
        采集管理
    </template>
    <MenuItem v-show="permissionOnShow('menu-metertype')" name="metertype">
        <Icon type="ios-arrow-forward" />
        表计类型管理
    </MenuItem>
    <MenuItem v-show="permissionOnShow('menu-metermodel')" name="metermodel">
        <Icon type="ios-arrow-forward" />
        表计型号管理
    </MenuItem>
    <MenuItem v-show="permissionOnShow('menu-serialconf')" name="serialconf">
        <Icon type="ios-arrow-forward" />
        通讯参数管理
    </MenuItem>
</Submenu>

这样,只要我们有一个最基本的 permission 定义文件,就可以让菜单按照定义显示出来,同时,如果想与后端进行权限同步,则可以在登录的时候获取到用户的操作列表,通过 Vuex 保存在 store 中,然后可以先通过 store 来鉴权,如果没有,则通过默认的 permission 来鉴权。

类似文章

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注