JavaScript的Vue.js库入门学习教程

Vue是一个小巧轻便的javascript库。它有一个简单易懂的API,能够让开发者在开发web应用的时候更加简易便捷。实际上,一直让Vue引以为豪的是它的便捷性、执行力、灵活性。

这篇教程的目的就是通过一些例子,让你能够概览一些基本的概念和特性。在接下来的其他教程里,你会学到Vue更多的有用的特性,从而用Vue搭建一个可扩展的项目。

MVVM 数据绑定

MVVM的本质是通过数据绑定链接View和Model,让数据的变化自动映射为视图的更新。Vue.js在数据绑定的API设计上借鉴了Angular的指令机制:用户可以通过具有特殊前缀的HTML 属性来实现数据绑定,也可以使用常见的花括号模板插值,或是在表单元素上使用双向绑定:

{{msg}}

插值本质上也是指令,只是为了方便模板的书写。在模板的编译过程中,Vue.js会为每一处需要动态更新的DOM节点创建一个指令对象。每当一个指令对象观测的数据变化时,它便会对所绑定的目标节点执行相应的DOM操作。基于指令的数据绑定使得具体的DOM操作都被合理地封装在指令定义中,业务代码只需要涉及模板和对数据状态的操作即可,这使得应用的开发效率和可维护性都大大提升。

与Angular不同的是,Vue.js的API里并没有繁杂的module、controller、scope、factory、service等概念,一切都是以“ViewModel 实例”为基本单位:

// 原生对象即数据 var data = { msg: 'hello!' } // 创建一个 ViewModel 实例 var vm = new Vue({ // 选择目标元素 el: '#app',// 提供初始数据 data: data })

渲染结果:

在渲染的同时,Vue.js也已经完成了数据的动态绑定:此时如果改动data.msg的值,DOM将自动更新。是不是非常简单易懂呢?除此之外,Vue.js对自定义指令、过滤器的API也做了大幅的简化,如果你有Angular的开发经验,上手会非常迅速。

数据观测的实现

Vue.js的数据观测实现原理和Angular有着本质的不同。了解Angular的读者可能知道,Angular的数据观测采用的是脏检查(dirty checking)机制。每一个指令都会有一个对应的用来观测数据的对象,叫做watcher;一个作用域中会有很多个watcher。每当界面需要更新时,Angular会遍历当前作用域里的所有watcher,对它们一一求值,然后和之前保存的旧值进行比较。如果求值的结果变化了,就触发对应的更新,这个过程叫做digest cycle。脏检查有两个问题:

任何数据变动都意味着当前作用域的每一个watcher需要被重新求值,因此当watcher的数量庞大时,应用的性能就不可避免地受到影响,并且很难优化。 当数据变动时,框架并不能主动侦测到变化的发生,需要手动触发digest cycle才能触发相应的DOM 更新。Angular通过在DOM事件处理函数中自动触发digest cycle部分规避了这个问题,但还是有很多情况需要用户手动进行触发。 Vue.js采用的则是基于依赖收集的观测机制。从原理上来说,和老牌MVVM框架Knockout是一样的。依赖收集的基本原理是:

将原生的数据改造成 “可观察对象”。一个可观察对象可以被取值,也可以被赋值。 在watcher的求值过程中,每一个被取值的可观察对象都会将当前的watcher注册为自己的一个订阅者,并成为当前watcher的一个依赖。 当一个被依赖的可观察对象被赋值时,它会通知所有订阅自己的watcher重新求值,并触发相应的更新。 依赖收集的优点在于可以精确、主动地追踪数据的变化,不存在上述提到的脏检查的两个问题。但传统的依赖收集实现,比如Knockout,通常需要包裹原生数据来制造可观察对象,在取值和赋值时需要采用函数调用的形式,在进行数据操作时写法繁琐,不够直观;同时,对复杂嵌套结构的对象支持也不理想。 Vue.js利用了ES5的Object.defineProperty方法,直接将原生数据对象的属性改造为getter和setter,在这两个函数内部实现依赖的收集和触发,而且完美支持嵌套的对象结构。对于数组,则通过包裹数组的可变方法(比如push)来监听数组的变化。这使得操作Vue.js的数据和操作原生对象几乎没有差别[注:在添加/删除属性,或是修改数组特定位置元素时,需要调用特定的函数,如obj.$add(key,value)才能触发更新。这是受ES5的语言特性所限。],数据操作的逻辑更为清晰流畅,和第三方数据同步方案的整合也更为方便。

组件系统

在大型的应用中,为了分工、复用和可维护性,我们不可避免地需要将应用抽象为多个相对独立的模块。在较为传统的开发模式中,我们只有在考虑复用时才会将某一部分做成组件;但实际上,应用类 UI 完全可以看作是全部由组件树构成的:

因此,在Vue.js的设计中将组件作为一个核心概念。可以说,每一个Vue.js应用都是围绕着组件来开发的。

注册一个Vue.js组件十分简单:

{{msg}} {{privateMsg}}
',// 接受参数 props: { msg: String

},// 私有数据,需要在函数中返回以避免多个实例共享一个对象
data: function () {
return {
privateMsg: 'component!'
}
}
})

注册之后即可在父组件模板中以自定义元素的形式调用一个子组件:

渲染结果:

hello component!

Vue.js的组件可以理解为预先定义好了行为的ViewModel类。一个组件可以预定义很多选项,但最核心的是以下几个:

  • 模板(template):模板声明了数据和最终展现给用户的DOM之间的映射关系。
  • 初始数据(data):一个组件的初始数据状态。对于可复用的组件来说,这通常是私有的状态。
  • 接受的外部参数(props):组件之间通过参数来进行数据的传递和共享。参数默认是单向绑定(由上至下),但也可以显式地声明为双向绑定。
  • 方法(methods):对数据的改动操作一般都在组件的方法内进行。可以通过v-on指令将用户输入事件和组件方法进行绑定。
  • 生命周期钩子函数(lifecycle hooks):一个组件会触发多个生命周期钩子函数,比如created,attached,destroyed等等。在这些钩子函数中,我们可以封装一些自定义的逻辑。和传统的MVC相比,可以理解为 Controller的逻辑被分散到了这些钩子函数中。
  • 私有资源(assets):Vue.js当中将用户自定义的指令、过滤器、组件等统称为资源。由于全局注册资源容易导致命名冲突,一个组件可以声明自己的私有资源。私有资源只有该组件和它的子组件可以调用。
  • 除此之外,同一颗组件树之内的组件之间还可以通过内建的事件API来进行通信。Vue.js提供了完善的定义、复用和嵌套组件的API,让开发者可以像搭积木一样用组件拼出整个应用的界面。这个思路的可行性在Facebook开源的React当中也得到了印证。

基于构建工具的单文件组件格式

Vue.js的核心库只提供基本的API,本身在如何组织应用的文件结构上并不做太多约束。但在构建大型应用时,推荐使用Webpack+vue-loader这个组合以使针对组件的开发更高效。

Webpack是由Tobias Koppers开发的一个开源前端模块构建工具。它的基本功能是将以模块格式书写的多个JavaScript文件打包成一个文件,同时支持CommonJS和AMD格式。但让它与众不同的是,它提供了强大的loader API来定义对不同文件格式的预处理逻辑,从而让我们可以将CSS、模板,甚至是自定义的文件格式当做JavaScript模块来使用。Webpack 基于loader还可以实现大量高级功能,比如自动分块打包并按需加载、对图片资源引用的自动定位、根据图片大小决定是否用base64内联、开发时的模块热替换等等,可以说是目前前端构建领域最有竞争力的解决方案之一。

我在Webpack的loader API基础上开发了vue-loader插件,从而让我们可以用这样的单文件格式 (*.vue) 来书写Vue组件:

.my-component h2 { color: red; }

同时,还可以在*.vue文件中使用其他预处理器,只需要安装对应的Webpack loader即可:

<template lang="jade">
div.my-component
h2 Hello from {{msg}}

<script lang="babel">
// 利用 Babel 编译 ES2015
export default {
data () {
return {
msg: 'Hello from Babel!'
}
}
}

这样的组件格式,把一个组件的模板、样式、逻辑三要素整合在同一个文件中,即方便开发,也方便复用和维护。另外,Vue.js本身支持对组件的异步加载,配合Webpack的分块打包功能,可以极其轻松地实现组件的异步按需加载。

其他特性

Vue.js还有几个值得一提的特性:

  • 异步批量DOM更新:当大量数据变动时,所有受到影响的watcher会被推送到一个队列中,并且每个watcher只会推进队列一次。这个队列会在进程的下一个 “tick” 异步执行。这个机制可以避免同一个数据多次变动产生的多余DOM操作,也可以保证所有的DOM写操作在一起执行,避免DOM读写切换可能导致的layout。
  • 动画系统:Vue.js提供了简单却强大的动画系统,当一个元素的可见性变化时,用户不仅可以很简单地定义对应的CSS Transition或Animation效果,还可以利用丰富的JavaScript钩子函数进行更底层的动画处理。
  • 可扩展性:除了自定义指令、过滤器和组件,Vue.js还提供了灵活的mixin机制,让用户可以在多个组件中复用共同的特性。

利用new Vue()创建一个Vue实例

我们可以先初始化一个html页面,然后我们需要引入Vue 的 JS 文件。引入的方式有很多,我们可以在script中引入Vue的cdn,或者去官网上下载Vue的min.js,或者用 npm 安装一个Vue的依赖。方便起见,本文中就用cdn引入。

从零开始学Vue //之后这行会省略

无论用户输入多少次,这个name都会被自动更新。并且,如果name的值被改变了,其他有映射name的地方的值也会被修改。 这种input框和映射的同步修改的原因,就是利用v-model这个指令,让数据通过底层的数据流进行绑定后直接修改。这就是数据的双向绑定的概念。

为了证明这个概念,我们可以利用$data打印出数据的映射来看看。

{{ $data | json }}

//#1

{{ name }}

//#2
var V = new Vue({ el : '#vueInstance',data : { name : '_Appian' } });

其1: $data是Vue实例观察的数据对象,本质类型是一个对象,所以可以转成JSON。可以用一个新的对象替换。实例代理了它的数据对象的属性。 {{}},利用两个花括号进行插值。这里插入的值是$data实时变化的值。 | json,只是一个更直观的能让数据展示出来的方法。也可以看做是一个过滤器,就像JSON.stringify()的效果一样。

其2: {{ name }},就是直接在插值表达式,两个花括号中间插入数据变量,直接映射name的值。

Vue就是这么简单的进行数据的双向绑定,只需要一个v-model指令就可以,而不需要利用js或者jq来控制数据。相信你能从上面的例子中理清逻辑。

利用v-on进行事件绑定

Vue是利用v-on指令进行事件监听和事件分发的。你可以在Vue的实例中创建一个方法来绑定监听事件,可以创建一个方法来分派一个点击事件。

下面的例子中,我们将创建一个say方法,这个方法绑定在一个button上。点击产生的效果就是弹出一个带有用户name的欢迎框。为了将这个方法指派给button,我们需要用v-on:click来进行事件绑定。

//#1
var V = new Vue({ el : '#vueInstance',data : { name : '_Appian' },methods : { say : function(){ alert('欢迎' + this.name); } } });

当然了,不仅是可以绑定click点击事件,还可以绑定其他鼠标事件,键盘输入事件等一些js的事件类型。比如v-on:mouseover,v-on:keydown, v-on:submit, v-on:keypress,v-on:keyup.13等等,或者是一些其他的自定义事件。

在开发过程中,你可能会频繁的用到事件绑定,v-on写起来有点麻烦,所以上例中提供了两种写法,#2就是对#1写法的缩写。利用@代替v-on,这里不多说。

利用v-if或者v-show进行条件判定

如果我们希望用户在登录之后才能看到欢迎弹窗,而如果没有登录则给它一个登录界面。Vue会提供给我们v-if指令和v-show指令用来控制不同登录状态下的显示内容。

利用先前的例子,我们可以用loginStatus的值来控制是否登录,如果是true则显示输入框和按钮让他能够看到欢迎弹窗,但如果是false(即未登录),则只能看到输入账号、密码的输入框和提交按钮(暂时不进行身份验证,只改变登录状态)。

var V = new Vue({ el : '#vueInstance',data : { name : '_Appian',loginStatus : false },methods : { say : function(){ alert('欢迎' + this.name); },switchLoginStatus : function(){ this.loginStatus = !this.loginStatus; } } });

this的执行就是实例V。this的指向是一个需要自己去搞懂的问题,这里不多说。 在上述例子中,只要把V-if换成v-show,一样可以获得等同的效果。同时v-if和v-show他们都支持v-else,但是绑定v-else命令的标签的前一兄弟元素必须有 v-if 或 v-show。

在上面的例子中,只要点击“登录”或者“退出登录”按钮都会触发switchLoginStatus方法,只要触发了这个方法就会导致loginStatus的状态变化(在true和false中进行切换),从而改变了html中的v-if的判断条件结果的变化,基于当前的loginStatus的布尔值的状态,使得显示的section是不同状态下的section。

v-show和v-if之间有什么区别呢?

在切换 v-if 块时,Vue有一个局部编译/卸载过程,因为 v-if 之中的模板也可能包括数据绑定或子组件。v-if 是真实的条件渲染,因为它会确保条件块在切换当中合适地销毁与重建条件块内的事件监听器和子组件。 v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——在条件第一次变为真时才开始局部编译(编译会被缓存起来)。 相比之下,v-show 简单得多——元素始终被编译并保留,只是简单地基于 CSS 切换。 一般来说,v-if 有更高的切换消耗而 v-show 有更高的初始渲染消耗。因此,如果需要频繁切换 v-show 较好,如果在运行时条件不大可能改变 v-if 较好。

这个差别也许对你目前的开发来说并不重要,但是你还是要注意和留心,因为当你的项目开发变大的时候,这点会变得重要起来。

利用v-for输出列表

如果你是经营一个电商平台的商人的话,你一定有很多页面都需要渲染商品列表的输出。v-for指令允许循环我们的数组对象,用 “element in arrayObj” 的方式,念作“循环arrayObj这个数据对象里的每一个element”。

下面的例子中,我们将会利用v-for指令循环输出一个商品列表。每一个商品都会在一个li中,li中输出商品的名称、价格和商品类型。

  • var V = new Vue({ el : '#vueInstance',data : { products : [ {name: 'microphone',price: 25,category: 'electronics'},{name: 'laptop case',price: 15,category: 'accessories'},{name: 'screen cleaner',price: 17,{name: 'laptop charger',price: 70,{name: 'mouse',price: 40,{name: 'earphones',price: 20,{name: 'monitor',price: 120,category: 'electronics'} ] } });

    当然了,data中的数组对象,可以不用像上面这样定义也可以,我们可以从数据库导入,或者是利用ajax请求得到。这里只是为了演示v-for。

    有时候我们可能会需要拿到商品在数组对象里的对应下标。我们可以用$index来获得。

    //#2
    <li v-for="(index,el) in products">
    {{ index }} - {{ el.name }} - ¥ {{ el. price }} - {{ el. category }}

    计算属性Computed

    计算属性的应用场景,一般是在有一个变量的值需要其他变量计算得到的时候,会用到。

    比如,例如用户在输入框输入一个数 x,则自动返回给用户这个数的平方 x²。你需要对输入框进行数据绑定,然后当数据修改的时候,就马上计算它的平方。

    计算结果:{{ square }}

       var V = new Vue({
          el : '#vueInstance',data : {
             value : 1
          },computed : {
             square : function(){
                return this.value * this.value;
             }
          }
       });
    
    

    计算属性定义数值是通过定义一系列的function,就像我们先前定义methods对象的时候是一样的。比如square方法是用来计算变量“square”的,其方法的返回值是两个this.value的乘积。

    接下来可以用computed做一个复杂一点例子。系统会随机取一个1~10以内的数字,用户可以在输入框随机输入一个1~10之内的数字,如果刚好用户的输入和系统的随机数刚好匹配,则游戏成功,反之失败。

    计算结果:{{ resultMsg }}

    var V = new Vue({ el : '#vueInstance',data : { value : null,randNum : 5//第一次随机数为5 },methods : { getRandNum: function(min,max){ return Math.floor(Math.random() * (max - min + 1)) + min; } },computed : { resultMsg : function(){ if (this.value == this.randNum) { this.randNum = this.getRandNum(1,10); return '你猜对了!'; } else { this.randNum = this.getRandNum(1,10); return '猜错了,再来!'; } } } });

    后记

    到此为止,你就已经能够掌握了Vue的基本使用,世界上最简洁最漂亮的框架之一,它的构建有着自己完整的设计思想,而且越来越流行。这个框架足够小而轻,在你的开发中会给你带来更加流畅的用户体验,并有效提高开发效率。上文中举了一系列例子,都掌握了吗?

    版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

    JavaScriptjsMVVM

    相关推荐


    https://segmentfault.com/a/1190000022018995 https://www.jianshu.com/p/8c3599dda094 vuex教程中,有这样一句话和这样一段代码: 实践中,我们会经常用到 ES2015 的参数解构来简化代码(特别是我们需要调用commi
    ES6 (ECMAScript 6)中的模块是一个包含 JavaScript 代码的文件,在这个模块中所有的变量都对其他模块是不可见的,除非我们导出它。 ES6的模块系统大致分为导出(export)和导入(import)两个模块。 1、模块导出(export) 可以 导出 所有的最外层 函数 、 类
    from https://mp.weixin.qq.com/s/-rc1lYYlsfx-wR4mQmIIQQ Vue知识点汇总(含Vue3) 一、Vue 基础 1. Vue的基本原理 当一个Vue实例创建时,Vue会遍历data中的属性,用 Object.defineProperty(vue3.0使
    D:\Temp&gt;npm init vite@latest vue3study --template vuenpm ERR! code ETIMEDOUTnpm ERR! errno ETIMEDOUTnpm ERR! network request to https://registry.np
    文章浏览阅读1.2k次。最近自己从零撸起的甘特图组件需要子组件的滚动条同步滚动这就涉及到子组件之间的互相通信,通过 消息总线可以达到我们的需求 ,首先建立一个标志位,拖动左边滚动条的时候,右边的滚动条事件不处理,反之拖动右边滚动条时,左边的滚动条事件不做处理,建立一个公共的变量用于两者的互斥store.jsimport Vue from 'vue'export let store = Vue.observable({ scrollFlag: true})export let mutations =.._vue 能不能同时有两个滚动事件
    文章浏览阅读3.3k次,点赞3次,收藏16次。静默打印是什么?简单来说就是不需要用户点击"打印",自动去打印,但是使用浏览器web打印不可避免的要弹出以下画面面对这种问题也只能用"富客户端"技术来解决,在浏览器的沙盒安全模型中无法做到,那么只能使用插件的技术,这个我们就不自己花力气去做了,我找来了 lodop 这个免费的打印组件,功能还是挺强大的,下载下图的发行包解压后安装下图两个exe如果你的系统是64位的,可以安装install_lodop64.exe上图的LodopFuncs.js 是客户端要使用的核心库文件..._this.$getlodop().then((lodop) =>{
    文章浏览阅读1.7k次。个人觉得大屏展示其实很简单,噱头多过技术含量,下面使用了 DataV (不是阿里的那个DataV哈,具体链接在这里)开发了一个大屏展示,使用了css flex弹性布局,使用了DataV的一些比较酷炫的边框(SVG写的),基本上功能没有全部完成,但是模子已经刻出来了,只是后端推送的内容没有全部写出来前端<template> <dv-full-screen-container class="screen-container"> <div class="ti_用signalr做一个简单的实时大屏显示
    文章浏览阅读3.4k次,点赞3次,收藏10次。【说明】导入的Excel 字体颜色和背景色只能识别【标准色】,别的如"主题颜色",exceljs 解析出来不是颜色值。导入的样式包括字体,字号,列宽,合并单元格,【部分能识别】的背景色,文字颜色。导入到 x-data-spreadsheet 如下图。原Excel样式如下。_x-data-spreadsheet
    文章浏览阅读1.7k次。之前参考某文章把 router-view 放在 el-tab-pane 外面都不起作用,问题根本不是出在 el-tab-pane,而是v-for 里面有多个route-view , keep-alive 时 tab 并未销毁掉,而是缓存隐藏了起来。需要把 router-view 的 name 与路由的 index.js 名称对应起来。之前参照很多文章修改试图修正这个问题,结果都徒劳,终于让我找到。我做了如下修改,主页面 main.vue。_el-tab-pane 后面接router-view
    文章浏览阅读533次。今天在一台虚拟机上面运行老项目,报各种类型上图的错误提示,一开始还以为是less的问题,结果一个个装完还是报错,后面又说webpack, webpack cli有问题,头有点大了,google 一下,发现一个命令。讨论这个命令的文章,可以了解一下。运行以后终于出现了期待已久的。_npm install 忽略依赖
    文章浏览阅读8k次,点赞3次,收藏12次。从这篇文章得到启发先定义一个组件从外部接收Template,然后在组件里调用<template > <div ref="markedContent"></div></template><script>import Vue from 'vue/dist/vue.esm.js'export default { name: 'wf-marked-content', props: ['content'], mounte.._vue components 动态传入模板
    文章浏览阅读5.4k次。参考上一篇知识开发的一个功能,制作一个打印模板的管理模块,如下(就是保存froala编辑后的html文本,其中包括Vue的Template,这样我们可以利用Vue的模板的优势来动态绑定一些数据源进行HTML的打印,基本上跟过去水晶报表做一个模板再绑定数据源的方法异曲同工)在 main.js 里引用 froala 组件// Import and use Vue Froala lib.import VueFroala from 'vue-froala-wysiwyg'// 引入 Fr.._vue设计网页打印模板
    文章浏览阅读992次。计划是这样,公司的项目一直在持续改动,安装包总是需要频繁生成新的,由此我想到了"持续集成"!有自动化工具不用,岂不可惜?这周的主要时间就用来学习CruiseControl.Net全面实现持续集成_怎么在vue的 script部分使用 eldigloa
    文章浏览阅读1.2k次。其实Element UI 只用了文字提示的 el-tooltip 组件,不喜欢可以去掉,不记得是从哪拿到的原始代码,我给加了高亮渐变显示,图标,和拖拽时只能拖拽图标的位置,效果如上图,可以水平方向拖动,也可以垂直方向拖动。样式是less写的,css写嵌套样式太繁琐了。拿来主义,改造有理!下面贴代码<template> <div ref="splitPane" class="split-pane" :class="direction" :"{ fl..._element ui拉条样式
    文章浏览阅读953次,点赞2次,收藏2次。接上一篇,这次加入的是从x-speadsheet导出Excel,并且带有x-speadsheet中的样式,重点关注 exportExcel 这个方法,我加入了 tinycolor 这个库用来翻译颜色值,值得注意的是, exceljs的颜色值是 argb 不是 rgba,一定不要弄混了a 是代表的透明度放在最前面_x-data-spreadsheet 导出
    文章浏览阅读5.5k次,点赞2次,收藏21次。尝试了两个连线库 jsplumb 和 leadline ,其实两个库都很强大,但是基于个人使用的习惯,决定还是用 leadline ,在Vue 下我使用它的一个包装库 leader-line-vue 下面是上图的连接线示例代码,连接线很轻松的就实现了一个渐变效果..._vue 连线
    文章浏览阅读4.2k次,点赞2次,收藏5次。首先官网推荐的安装方法没有生成dist文件,导致样式表等这些文件并没有生成npm install element-plus --save以上方法是有问题的,如果不幸执行了上面的命令,那么先执行卸载npm uninstall element-plus删除 main.js文件对element ui的引用,输入以下命令vue add element-plus..._elementui3.0
    文章浏览阅读3.1k次。如上图,下面贴代码<template> <div> <el-date-picker size="large" style ="width:120px" v-model="selectYear" format="yyyy 年" value-format="yyyy" type="year" :clearable = "false" placeholder="选择年">.._vue多选周
    文章浏览阅读1.8k次,点赞6次,收藏6次。经过 2021年的一个春节,从年前到现在,大致撸出一个 甘特图,进度条是用SVG画的,使用了几个工具库 (interactjs 用来处理拖拽和修改尺寸,snap.svg 用来处理 svg 的dom 操作,moment.js用来处理时间的操作),其他没有依赖任何的UI组件,目前初见雏形,还比较粗糙,后面会不断更新源码地址点击期间也摸索了怎么把vs code的项目上传到 GitHub 上面进行源代码的管理,基本上是参考的这篇文章做的..._vue gantt demo
    文章浏览阅读2.1k次。接上两篇vue 下使用 exceljs + x-spreadsheet 带样式导入Excelvue 下使用 exceljs + x-spreadsheet 带样式导出Excel下面封装好一个组件调用组件的页面效果如图,目前“导出Json”还没有做_x-spreadsheet导入导出