你好,我是大圣。

在上一讲中,我给你介绍了如何在Vue 3中实现页面和接口的权限管理。我们把vue-router的动态路由、导航守卫、axios的接口拦截等功能配合到一起使用,实现了页面的权限控制,这也算是Vue中进阶使用vue-router和axios的一个方式。

今天,我们再来学习另一个进阶玩法,聊一下如何在Vue 3中使用和引入更多的框架。可别小看这里的门道,有的第三方工具框架跟Vue耦合性不高,而有的需要做适配,这一讲我就详细给你说说实操中的注意事项,帮助你在提高开发效率的同时少走弯路。

独立的第三方库

首先我们要介绍的第三方框架是axios,这是一个完全独立于Vue的框架,我们可以使用axios发送和获取网络接口数据。在Vue、React框架下,axios可以用来获取后端数据。甚至在Node.js环境下,也可以用axios去作为网络接口工具去实现爬虫。

axios这种相对独立的工具对于我们项目来说,引入的难度非常低。通常来说,使用这种独立的框架需要以下两步。

以页面进度条工具NProgress为例,第一步是,我们先进入到项目根目录下,使用下面的命令去安装NProgress。

npm install nprogress -D

第二步,就是在需要使用NProgress的地方进行import的相关操作,比如在页面跳转的时候,我们就需要使用NProgress作为进度条。导入NProgress库之后,我们就不需要使用Vue3的插件机制进行注册,只需要通过router.beforeEach来显示进度条,通过afterEach来结束进度条就可以了。

import NProgress from 'nprogress' // progress bar
router.beforeEach(async (to, from, next) => {
  // start progress bar
  NProgress.start()
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

在项目中,我们之后还会依赖很多和NProgress类似的库,比如处理Excel的xlsx库,处理剪切板的clipboard库等等。

组件的封装

下面我们以可视化组件为例,来分析复杂组件的封装。之所以选择可视化组件为示例,是因为管理系统中的统计数据、销售额数据等等,都喜欢用饼图或柱状图的方式来展示。

虽然可视化本身和Vue没有太大关系,但我们需要在页面中以组件的形式显示可视化图表。对此,我们的选择是用可视化框架ECharts去封装Vue的组件,来实现可视化组件。

我们再简单介绍一下可视化框架的使用方式,不管你选择用百度的ECharts,还是蚂蚁的G2等框架,在框架的使用方法上,都是类似的。首先,你需要完成图表库的配置,并且填入图表数据,然后把这个数据渲染在一个DOM上就可以了。

下面的代码展示了一个ECharts的入门案例,代码中我们首先使用echarts.init初始化一个DOM标签;然后在options中配置了图表的结构,包括标题、x轴等;并且我们还通过series配置了页面的销量数据;最后使用myChart.setOption的方式渲染图表就可以了。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>ECharts</title>
    <!-- 引入刚刚下载的 ECharts 文件 -->
    <script src="echarts.js"></script>
  </head>
  <body>
    <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
    <div id="main" style="width: 600px;height:400px;"></div>
    <script type="text/javascript">
      // 基于准备好的dom,初始化echarts实例
      var myChart = echarts.init(document.getElementById('main'));
      // 指定图表的配置项和数据
      var option = {
        title: {
          text: 'ECharts 入门示例'
        },
        tooltip: {},
        legend: {
          data: ['销量']
        },
        xAxis: {
          data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
        },
        yAxis: {},
        series: [
          {
            name: '销量',
            type: 'bar',
            data: [5, 20, 36, 10, 10, 20]
          }
        ]
      };
      // 使用刚指定的配置项和数据显示图表。
      myChart.setOption(option);
    </script>
  </body>
</html>

看上面的代码,我们先配置好图表需要的数据,然后使用setOption初始化图表,之后在浏览器中打开项目主页面,就可以看到下图所示的这种可视化结果。在你理解了ECharts的使用方法后,下一个要解决的问题是,我们该如何在Vue 3中集成这个框架呢?答案就是我们自己实现与ECharts对应的Vue组件即可。

图片

在Vue 3中集成ECharts的最简单的方式,就是封装一个Chart组件,把上面代码中的option配置以参数的形式传递给Chart组件,然后组件内部进行渲染即可。

我们还是结合代码直观体验一下。在下面的代码中,template设置了一个普通的div作为容器,通过mount和onUnmounted生命周期内部去初始化图表,实现ECharts框架中图表的渲染和清理,然后initChart内部使用echart的API进行渲染,这样就实现了图表的渲染。

<template>
  <div ref="chartRef" class="chart"></div>
</template>

<script setup>
import * as echarts from 'echarts'
import {ref,onMounted,onUnmounted} from 'vue'
// 通过ref获得DOM
let chartRef = ref()
let myChart 
onUnmounted(()=>{
  myChart.dispose()
  myChart = null
})
onMounted(()=>{
    myChart = echarts.init(chartRef.value)
     const option = {
        tooltip: {
            trigger: 'item'
        },
        color: ['#ffd666', '#ffa39e', '#409EFF'],
        // 饼图数据配置
        series: [
            {
                name: '前端课程',
                type: 'pie',
                radius: '70%',
                data: [
                    {value: 43340, name: '重学前端'},
                    {value: 7003, name: 'Javascript核心原理解析'},
                    {value: 4314, name: '玩转Vue3全家桶'}
                ]
            }
        ]
    }
    myChart.setOption(option)
})
</script>

在上面,我们虽然实现了可视化组件的封装,但因为逻辑并不复杂,所以我们的实现还比较简略。

我们当然可以尝试去实现一下更详细的可视化组件封装,但因为ECharts是一个非常复杂的可视化框架,有饼图,地图等不同的图表类型,如果引入ECharts全部代码的话,项目的体积会变得非常臃肿。所以,如果我们能按照不同的图表类型按需引入ECharts,那么除了能够让组件使用起来更方便之外,整体项目的包的大小也会优化很多。

指令的封装

接下来,我们再介绍一下指令增强型组件的封装。

比如我们常见的图片懒加载的需求,这一需求的实现方式就是在img的标签之上,再加上一个v-lazy的属性。而图片懒加载和指令增强型组件的封装的关系在于,v-lazy指令的使用方式是在HTML标签上新增一个属性。Vue内置的指令我们已经很熟悉了,包括v-if、v-model等等。像图片懒加载这种库和DOM绑定,但是又没有单独的组件渲染逻辑的情况,通常在Vue中以指令的形式存在。

在Vue中注册指令和组件略有不同,下面的代码中我们注册实现了v-focus指令,然后在input标签中加上v-focus指令,在指令加载完毕后,鼠标会自动聚焦到输入框上,这个实现在登录注册窗口中很常见。

// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})

指令的生命周期和组件类似,首先我们要让指令能够支持Vue的插件机制,所以我们需要在install函数内注册lazy指令。这种实现Vue插件的方式,在vuex和vue-router两讲中已经带你学习过了,这里的代码里我们使用install方法,在install方法的内部去注册lazy指令,并且实现了mounted、updated、unmounted三个钩子函数。

const lazyPlugin = {
  install (app, options) {
    app.directive('lazy', {
      mounted: ...,
      updated: ...,
      unmounted: ...
    })
  }
}

我们通过lazy指令获取到当前图片的标签,并且计算图片的位置信息,判断图片是否在首页显示。如果不在首页的话,图片就加载一个默认的占位符就可以了,并且在页面发生变化的时候,重新进行计算,这样就实现了页面图片的懒加载。

与懒加载类似的,还有我们组件库中常用的v-loading指令,它用来显示组件内部的加载状态,我们在Element3中。也有类似的指令效果,下面的代码中,我们注册了loadingDirective指令,并且注册了mounted、updated、unmounted三个钩子函数,通过v-loading的值来对显示效果进行切换,实现了组件内部的loading状态。

动态切换的Loading组件能够显示一个circle的div标签,通过v-loading指令的注册,在后续表格、表单等组件的提交状态中,加载状态就可以很方便地使用v-loading来实现。

const loadingDirective = {
  mounted: function (el, binding, vnode) {
    const mask = createComponent(Loading, {
      ...options,
      onAfterLeave() {
        el.domVisible = false
        const target =
          binding.modifiers.fullscreen || binding.modifiers.body
            ? document.body
            : el
        removeClass(target, 'el-loading-parent--relative')
        removeClass(target, 'el-loading-parent--hidden')
      }
    })
    el.options = options
    el.instance = mask.proxy
    el.mask = mask.proxy.$el
    el.maskStyle = {}

    binding.value && toggleLoading(el, binding)
  },

  updated: function (el, binding) {
    el.instance.setText(el.getAttribute('element-loading-text'))
    if (binding.oldValue !== binding.value) {
      toggleLoading(el, binding)
    }
  },

  unmounted: function () {
    el.instance && el.instance.close()
  }
}

export default {
  install(app) {
    // if (Vue.prototype.$isServer) return
    app.directive('loading', loadingDirective)
  }
}

引入第三方库的注意事项

我们封装第三方库的目的是实现第三方框架和Vue框架的融合,提高开发效率。这里我跟你聊几个和引入第三方库相关的注意事项。

首先,无论是引用第三方库还是你自己封装的底层库,在使用它们之初就要考虑到项目的长期可维护性;其次,尽可能不要因为排期等问题,一股脑地把第三方库堆在一起,虽然这样做可以让项目在早期研发进度上走得很快,但这样会导致项目中后期的维护成本远远大于重写一遍代码xxx的成本。

然后是Vue中的mixin,extends机制能不用就不用,这两个API算是从Vue 2时代继承下来的产物,都是扩展和丰富Vue 2中this关键字,在项目复杂了之后,mixin和extends隐式添加的API无从溯源,一旦多个mixin有了命名冲突,调试起来难度倍增。

项目中的全局属性也尽可能少用,全局变量是最原始的共享数据的方法,Vue 3中我们使用app.config.globalProperties.x注册全局变量,要少用它的主要原因也是项目中的全局变量会极大的提高维护成本。有些监控场景必须要用到,就要把所有注册的全局变量放在一个独立的文件去管理。

最后,我们引入第三方框架和库的时候一定要注意按需使用,比如我们只用到了ECharts中的某几种类型的图,也只用到了Element3中的部分组件。现在引入全部代码的方式会让项目体积越来越大,关于代码体积优化的内容,我们在18讲谈性能优化时也会详细介绍。

总结

今天的这一讲的内容就学完了,我们来复习一下今天学到的知识。首先,我们介绍了Vue中如何封装第三方工具框架,比如axios,NProgress等,这些框架和Vue耦合性不强,直接引入使用即可。然后,我讲到了Vue中封装可视化组件库的方式,也就是把第三方库放在Vue的组件内部执行。

就像对于ECharts、wangEditor等成熟的框架来说,对这些框架进行Vue 3框架的适配工作,主要适配的载体就是组件。你可以选择直接透传所有option配置来做一个很浅的封装,也可以针对需要用的组件类型逐个封装,比如ECharts可以封装Pie,Bar等不同类型的组件。

之后我们讲解了指令增强型组件的封装,这种库和DOM绑定,但是又没有单独的组件渲染逻辑,通常在Vue中以指令的形式存在,比较常见的就是图片懒加载指令以及loading指令,我们项目中也会用到这两个指令来增强组件。

最后,我也给你介绍了一些引入第三方库时,需要你注意的事项,例如从项目开始之初就要考虑到长期维护的成本,不要一股脑地堆砌代码,要学会全面使用Composition API 组织代码、少用全局变量,以及不要引入第三方库全部代码,这些都是很值得你注意的地方

思考题

最后留一个思考题,你的项目中还有什么第三方框架需要引入呢?大家可以在评论区分享你的答案,也欢迎你把这一讲的内容分享给你的同事、朋友们。