• [技术干货] element-ui el-select/el-cascader设置默认值,默认值不显示
    最近接手了一个后台管理项目,项目使用了Element-ui组件, 使用了Select 下拉菜单展示并选择内容,使用了cascader做级联城市选择器 看了官方文档我们知道可以使用v-model绑定数据,设置初始值/默认值 <el-form-item label="班级" prop="classId">    <el-select v-model="form.classId" placeholder="请选择班级">      <el-option v-for="(value) of classList" :key="value.classId" :label="value.className" :value="value.classId"></el-option>    </el-select> </el-form-item> <el-form-item label="机构地址" prop="district">    <el-cascader        style="width: 216px;"         v-model="form.district"        :options="dataDistrict"        @change="handleChange"      >     </el-cascader> </el-form-item> 问题来了,明明已经设置了v-model但是没有显示,百度发现问题在于数据类型,form.classId的值为 String ,而 el-select列表的 value 为 Number,el-cascader的value是Array,form.district却是JSON字符串,并不全等,导致无法找到对应的项,把数据类型进行转换后就能正常显示啦 ————————————————  原文链接:https://blog.csdn.net/weixin_43144209/article/details/120891475 
  • [技术干货] vue中el-select无法显示默认选中值的name,直接显示值value
    一、背景 在写项目的过程中,突然发现el-select这个组件,绑定的值没办法显示name,而是直接显示value。在编辑项目的弹框中,要显示项目绑定了哪个数据库(项目外连数据库表主键ID),结果发现直接把关联数据库的ID显示了出来,这与我的设想不一致,因此来找找是什么问题 二、解决 有问题的,有文档的当然第一时间去看文档,一看数据类型是string。 value-key 作为 value 唯一标识的键名,绑定值为对象类型时必填 数据类型string 再去检查项目传值,发现el-option绑定的list中key的类型是string,但v-model绑定的值是int,所以就导致了无法显示正确的名字,而显示主键ID  知道问题解决起来就很简单了,直接parseInt()转一下数据类型 <el-form-item label="归属数据库:" prop="databaseId">     <el-select v-model="projectForm.databaseId" placeholder="归属项目" style="width: 400px">         <el-option v-for="item in databaseList" :key="item.id" :value="parseInt(item.id)" :label="item.name" />     </el-select> </el-form-item> 但是可以的话,还是尽量数据格式一致,如果都是int的话,也是能可以正确显示的,那就无须做额外的处理了 ————————————————  原文链接:https://blog.csdn.net/weixin_40295575/article/details/124250581 
  • Vue实现贪吃蛇【转】
     前言 // 呵呵, 1024 发一波 基础知识 的库存  缘于一些 小的需求   因为一些原因 可能到这里就不会再往后面设计了   主要分为了两个版本, html + js 的版本 和 vue 的版本   核心的意义是 面向对象的程序设计   不过是 基于了这种常见的小游戏 展现出来了   关于设计 关于 面向对象的程序设计  内容很多很杂   这里 也不想 一一文字描述, 需要自己去领悟了   具体的 代码实现的设计, 这里 也不多说了, 文字描述有限   整个流程大概如下, 其中第一点至关重要   1. 设计各个对象 Game, Snake, SnakeNode, Arena, WallNode, FoodNode 的各个属性, 以及方法, 方法可以留空, 定义好方法名字就行 2. 实现 Arena 以及 墙体节点, 并绘制 Arena 的整个墙体 3. 实现 食物节点 并绘制食物, 需要按照频率闪烁 4. 实现按键操作 贪吃蛇 的方向, 按空格键暂停游戏  5. 实现 贪吃蛇 触碰到食物之后, 吃掉食物, 贪吃蛇 的长度+1 6. 实现 FoodNode 的随机初始化, 以及吃掉食物之后随机初始化 [需要避开墙体 和 贪吃蛇 占用的节点]   7. 实现贪吃蛇 撞到墙 或者 自己 暂停游戏, 整条蛇闪烁, 标志着游戏结束 8. 拆分整个 H5DrawImage.html 按照对象拆分到各自的 js 9. 增加元素 Grass, Water, Steel     Grass 的特征为可以穿过, 无任何影响     Water 的特征为到 Water 之后不在向前走, 只能左右调整方向     Steel 的特征和墙一样, 碰到之后游戏结束 10. 增加分数的显示, 更新 11. 写一个简单的自动导航贪吃蛇的 "外挂" 12. 将一些配置提取出来, 比如 墙体的长宽, 所有元素节点的宽高, 贪吃蛇的移动频率, 外部导入 "地图" 等等 另外还有一些想法, 因为种种原因 算了, 不想继续再做了   增加道具, 比如 坦克大战的船, 获得船之后, 再规定的时间内可以穿过水   增加 坦克大战的星星, 获得两个之后, 可以撞开 Steel   html + js 版本 主要包含了一个 html 来驱动, 各个对象 js 来处理核心业务   H5DrawImage.html  <!DOCTYPE html> <html lang="en"> <head>   <meta charset="UTF-8">   <title>H5DrawImage.html</title> </head> <body>   <canvas id="canvas" width="1500" height="1000"></canvas>   <div id="scoreDiv" style="position: absolute; left: 1000px; top:50px" >分数 : 12</div>   </body>   <script src="./js/jquery.min.js" type="text/javascript"></script> <script src="./js/constants.js" type="text/javascript"></script> <script src="./js/utils.js" type="text/javascript"></script> <script src="./js/food.js" type="text/javascript"></script> <script src="./js/arena.js" type="text/javascript"></script> <script src="./js/snake.js" type="text/javascript"></script> <script src="./js/game.js" type="text/javascript"></script> <script>   // $('html').css('font-size', $(window).width() / 10)       // --------------------------- game initiator ---------------------------     let game = new Game()   game.init(canvasCtx)   game.draw(canvasCtx)   game.run(canvasCtx)     // key event register   document.onkeydown = function (event) {     game.onKeyPressed(event, canvasCtx)   }     // // auto direction   game.auto(canvasCtx, moveIntervalInMs/2);   // update score panel   setInterval(function() {     let score = game.score()     document.getElementById("scoreDiv").innerText = `分数 : ${score}`   }, moveIntervalInMs)   </script>   </html> 
  • Vue 和 React 的区别 及 两者的应用场景【转】
     目录:  1. 监听数据变化的实现原理不同  2. 数据流的不同  3. HoC和mixins  4. 组件通信的区别  5. 模板渲染方式的不同  6. 渲染过程不同  7. 框架本质不同  8. 开发过程中  9. 开发应用时  应用场景:  react 和 vue 是我们做前端中必不可少的,一般看公司都在使用什么,你就要用什么,一般规模大的公司都会使用react+ts/react+hook,小公司使用vue2.0多一点,但不是全方面的,那么我们来说说react和vue有什么区别:  1. 监听数据变化的实现原理不同  Vue通过 getter/setter以及一些函数的劫持,能精确知道数据变化  React默认是通过比较引用的方式(diff)进行的,如果不优化可能导致大量不必要的 VDOM的重新渲染  2. 数据流的不同  Vue1.0中可以实现两种双向绑定:父子组件之间,props可以双向绑定;组件与DOM 之间可以通过v-model双 向绑定  React一直不支持双向绑定,提倡的是单向数据流,称之为onChange/setState()模式  3. HoC和mixins  Vue组合不同功能的方式是通过mixin,Vue中组件是一个被包装的函数,并不简单的就是我们定义组件的时候传 入的对象或者函数  React组合不同功能的方式是通过HoC(高阶组件)  4. 组件通信的区别  React 本身并不支持自定义事件,而Vue中子组件向父组件传递消息有两种方式:事件和回调函数,但Vue更倾 向于使用事件  在React中我们都是使用回调函数的,这可能是他们二者最大的区别  5. 模板渲染方式的不同  在表层上,模板的语法不同,React是通过JSX渲染模板;而Vue是通过一种拓展的HTML语法进行渲染  模板的原理不同,React是在组件JS代码中,通过原生JS实现模板中的常见语法,比如插值,条件,循环等,都 是通过JS语法实现的,更加纯粹更加原生。而Vue是在和组件JS代码分离的单独的模板中,通过指令来实现的  6. 渲染过程不同  Vue可以更快地计算出Virtual DOM的差异,这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要 重新渲染整个组件树  React在应用的状态被改变时,全部子组件都会重新渲染。通过shouldComponentUpdate这个生命周期方法可 以进行控制,但Vue将此视为默认的优化  7. 框架本质不同  Vue本质是MVVM框架,由MVC发展而来   React是前端组件化框架,由后端组件化发展而来  8. 开发过程中  vue适合开发小型应用  react适合开发大型应用  9. 开发应用时  vue使用uni-app开发app  react使用 react-Native ———————————————— 版权声明:本文为CSDN博主「樱颜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_58160355/article/details/124544957 
  • [技术干货] Vue常用插件【转】
    一、UI组件及框架 element - 饿了么出品的Vue2的web UI工具套件  mint-ui - Vue 2的移动UI元素  iview - 基于 Vuejs 的开源 UI 组件库  Keen-UI - 轻量级的基本UI组件合集  vue-material - 通过Vue Material和Vue 2建立精美的app应用  muse-ui - 三端样式一致的响应式 UI 库  vuetify - 为移动而生的Vue JS 2组件框架  vonic - 快速构建移动端单页应用  vue-blu - 帮助你轻松创建web应用  vue-multiselect - Vue.js选择框解决方案  VueCircleMenu - 漂亮的vue圆环菜单  vue-chat - vuejs和vuex及webpack的聊天示例  radon-ui - 快速开发产品的Vue组件库  vue-waterfall - Vue.js的瀑布布局组件  vue-carbon - 基于 vue 开发MD风格的移动端  vue-beauty - 由vue和ant design创建的优美UI组件  bootstrap-vue - 应用于Vuejs2的Twitter的Bootstrap 4组件  vueAdmin - 基于vuejs2和element的简单的管理员模板  vue-ztree - 用 vue 写的树层级组件  vue-tree - vue树视图组件  vue-tabs - 多tab页轻型框架  二、滚动scroll组件 vue-scroller - Vonic UI的功能性组件  vue-mugen-scroll - 无限滚动组件  vue-infinite-loading - VueJS的无限滚动插件  vue-virtual-scroller - 带任意数目数据的顺畅的滚动  vue-infinite-scroll - VueJS的无限滚动指令  vue-scrollbar - 最简单的滚动区域组件  vue-scroll - vue滚动  vue-pull-to-refresh - Vue2的上拉下拉  mint-loadmore - VueJS的双向下拉刷新组件  vue-smoothscroll - smoothscroll的VueJS版本  三、slider组件 vue-awesome-swiper - vue.js触摸滑动组件  vue-slick - 实现流畅轮播框的vue组件  vue-swipe - VueJS触摸滑块  vue-swiper - 易于使用的滑块组件  vue-images - 显示一组图片的lightbox组件  vue-carousel-3d - VueJS的3D轮播组件  vue-slide - vue轻量级滑动组件  vue-slider - vue 滑动组件  vue-m-carousel - vue 移动端轮播组件  dd-vue-component - 订单来了的公共组件库  vue-easy-slider - Vue 2.x的滑块组件  四、编辑器 markcook - 好看的markdown编辑器  eme - 优雅的Markdown编辑器  vue-syntax-highlight - Sublime Text语法高亮  vue-quill-editor - 基于Quill适用于Vue2的富文本编辑器  Vueditor - 所见即所得的编辑器  vue-html5-editor - html5所见即所得编辑器  vue2-editor - HTML编辑器  vue-simplemde - VueJS的Markdown编辑器组件  vue-quill - vue组件构建quill编辑器  五、图表 vue-table - 简化数据表格  vue-chartjs - vue中的Chartjs的封装  vue-charts - 轻松渲染一个图表  vue-chart - 强大的高速的vue图表解析  vue-highcharts - HighCharts组件  chartjs - Vue Bulma的chartjs组件  vue-chartkick - VueJS一行代码实现优美图表  六、日历 vue-calendar - 日期选择插件  vue-datepicker - 日历和日期选择组件  vue-datetime-picker - 日期时间选择控件  vue2-calendar - 支持lunar和日期事件的日期选择器  vue-fullcalendar - 基于vue.js的全日历组件  vue-datepicker - 漂亮的Vue日期选择器组件  datepicker - 基于flatpickr的时间选择组件  vue2-timepicker - 下拉时间选择器  vue-date-picker - VueJS日期选择器组件  vue-datepicker-simple - 基于vue的日期选择器  七、地址选择 vue-city - 城市选择器  vue-region-picker - 选择中国的省份市和地区  八、地图 vue-amap - 基于Vue 2和高德地图的地图组件  vue-google-maps - 带有双向数据绑定Google地图组件  vue-baidu-map- 基于 Vue 2的百度地图组件库  vue-cmap - Vue China map可视化组件  九、播放器 vue-video-player - VueJS视频及直播播放器  vue-video - Vue.js的HTML5视频播放器  vue-music-master - vue手机端网页音乐播放器  十、文件上传 vue-upload-component - Vuejs文件上传组件  vue-core-image-upload - 轻量级的vue上传插件  vue-dropzone - 用于文件上传的Vue组件  十一、图片处理 vue-lazyload-img - 移动优化的vue图片懒加载插件  vue-image-crop-upload - vue图片剪裁上传组件  vue-svgicon - 创建svg图标组件的工具  vue-img-loader - 图片加载UI组件  vue-image-clip- 基于vue的图像剪辑组件  vue-progressive-image - Vue的渐进图像加载插件  十二、提示 vue-toast-mobile - VueJS的toast插件  vue-msgbox - vuejs的消息框  vue-tooltip - 带绑定信息提示的提示工具  vue-verify-pop - 带气泡提示的vue校验插件  十三、进度条 vue-radial-progress - Vue.js放射性进度条组件  vue-progressbar - vue轻量级进度条  vue2-loading-bar - 最简单的仿Youtube加载条视图  十四、开发框架汇总 vue-admin - Vue管理面板框架  electron-vue - Electron及VueJS快速启动样板  vue-2.0-boilerplate - Vue2单页应用样板  vue-webgulp - 仿VueJS Vue loader示例  vue-bulma - 轻量级高性能MVVM Admin UI框架  vue-spa-template - 前后端分离后的单页应用开发  Framework7-Vue - VueJS与Framework7结合  vue-element-starter - vue启动页  十五、实用库汇总 vuelidate - 简单轻量级的基于模块的Vue.js验证  qingcheng - qingcheng主题  vuex - 专为 Vue.js 应用程序开发的状态管理模式  vue-axios - 将axios整合到VueJS的封装  vue-desktop - 创建管理面板网站的UI库  vue-meta - 管理app的meta信息  avoriaz - VueJS测试实用工具库  vue-framework7 - 结合VueJS使用的Framework7组件  vue-lazy-render - 用于Vue组件的延迟渲染  vue-svg-icon - vue2的可变彩色svg图标方案  vue-online - reactive的在线和离线组件  vue-password-strength-meter - 交互式密码强度计  vuep - 用实时编辑和预览来渲染Vue组件  vue-bootstrap-modal - vue的Bootstrap样式组件  element-admin - 支持 vuecli 的 Element UI 的后台模板  vue-shortkey - 应用于Vue.js的Vue-ShortKey 插件  cleave - 基于cleave.js的Cleave组件  vue-events - 简化事件的VueJS插件  http-vue-loader - 从html及js环境加载vue文件  vue-electron - 将选择的API封装到Vue对象中的插件  vue-router-transition - 页面过渡插件  vuemit - 处理VueJS事件  vue-cordova - Cordova的VueJS插件  vue-qart - 用于qartjs的Vue2指令  vue-websocket - VueJS的Websocket插件  vue-gesture - VueJS的手势事件插件  vue-local-storage - 具有类型支持的Vuejs本地储存插件  lazy-vue - 懒加载图片  vue-lazyloadImg - 图片懒加载插件  vue-bus - VueJS的事件总线  vue-observe-visibility - 当元素在页面上可见或隐藏时检测  vue-notifications - 非阻塞通知库  v-media-query - vue中添加用于配合媒体查询的方法  vuex-shared-mutations - 分享某种Vuex mutations  vue-lazy-component - 懒加载组件或者元素的Vue指令  vue-reactive-storage - vue插件的Reactive层  vue-ts-loader - 在Vue装载机检查脚本  vue-pagination-2 - 简单通用的分页组件  vuex-i18n - 定位插件  Vue.resize - 检测HTML调整大小事件的vue指令  vue-zoombox - 一个高级zoombox  leo-vue-validator - 异步的表单验证组件  modal - Vue Bulma的modal组件  Famous-Vue - Famous库的vue组件  vue-input-autosize - 基于内容自动调整文本输入的大小  vue-file-base64 - 将文件转换为Base64的vue组件  Vue-Easy-Validator - 简单的表单验证  vue-truncate-filter - 截断字符串的VueJS过滤器  十六、服务端 vue-*** - 结合Express使用Vue2服务端渲染  nuxt.js - 用于服务器渲染Vue app的最小化框架  vue-*** - 非常简单的VueJS服务器端渲染模板  vue-easy-renderer - Nodejs服务端渲染  express-vue - 简单的使用服务器端渲染vue.js  十七、辅助工具 DejaVue - Vuejs可视化及压力测试  vue-generate-component - 轻松生成Vue js组件的CLI工具  vscode-VueHelper - 目前vscode最好的vue代码提示插件  vue-play - 展示Vue组件的最小化框架  VuejsStarterKit - vuejs starter套件  vue-multipage-cli - 简单的多页CLI  十八、应用实例 pagekit - 轻量级的CMS建站系统  vuedo - 博客平台  koel - 基于网络的个人音频流媒体服务  CMS-of-Blog - 博客内容管理器  vue-cnode - 重写vue版cnode社区  vue-ghpages-blog - 依赖GitHub Pages无需本地生成的静态博客  swoole-vue-webim - Web版的聊天应用  fewords - 功能极其简单的笔记本  jackblog-vue - 个人博客系统  vue-blog - 使用Vue2.0 和Vuex的vue-blog  vue-dashing-js - nuvo-dashing-js的fork  rss-reader - 简单的rss阅读器  十九、Demo示例 eleme - 高仿饿了么app商家详情  NeteaseCloudWebApp - 高仿网易云音乐的webapp  vue-zhihu-daily - 知乎日报 with Vuejs  Vue-cnodejs - 基于vue重写Cnodejs.org的webapp  vue2-demo - 从零构建vue2 + vue-router + vuex 开发环境  vue-wechat - vue.js开发微信app界面  vue-music - Vue 音乐搜索播放  maizuo - vue/vuex/redux仿卖座网  vue-demo - vue简易留言板  spa-starter-kit - 单页应用启动套件  zhihudaily-vue - 知乎日报web版  douban - 模仿豆瓣前端  vue-Meizi - vue最新实战项目  vue-demo-kugou - vuejs仿写酷狗音乐webapp  vue2.0-taopiaopiao - vue2.0与express构建淘票票页面  node-vue-server-webpack - Node.js+Vue.js+webpack快速开发框架  VueDemo_Sell_Eleme - Vue2高仿饿了么外卖平台  vue-leancloud-blog - 一个前后端完全分离的单页应用  vue-fis3 - 流行开源工具集成demo  mi-by-vue - VueJS仿小米官网  vue-demo-maizuo - 使用Vue2全家桶仿制卖座电影  vue2.x-douban - Vue2实现简易豆瓣电影webApp  vue-adminLte-vue-router - vue和adminLte整合应用  vue-zhihudaily - 知乎日报 Web 版本  Zhihu-Daily-Vue.js - Vuejs单页网页应用  vue-axios-github - 登录拦截登出功能  vue2.x-Cnode - 基于vue全家桶的Cnode社区  hello-vue-django - 使用带有Django的vuejs的样板项目  websocket_chat - 基于vue和websocket的多人在线聊天室  x-blog - 开源的个人blog项目  vue-cnode - vue单页应用demo  vue-express-mongodb - 简单的前后端分离案例  photoShare - 基于图片分享的社交平台  notepad - 本地存储的记事本  vue-zhihudaily-2.0 - 使用Vue2.0+vue-router+vuex创建的zhihudaily  vueBlog - 前后端分离博客  Zhihu_Daily - 基于Vue和Nodejs的Web单页应用  vue-ruby-china - VueJS框架搭建的rubychina平台  vue-koa-demo - 使用Vue2和Koa1的全栈demo  life-app-vue - 使用vue2完成多功能集合到小webapp  vue-trip - vue2做的出行webapp  github-explorer - 寻找最有趣的GitHub库  vue-***-boilerplate - 精简版的ofvue-hackernews-2  vue-bushishiren - 不是诗人应用  houtai - 基于vue和Element的后台管理系统  ios7-vue - 使用vue2.0 vue-router vuex模拟ios7  Framework7-VueJS - 使用移动框架的示例  cnode-vue - 基于vue和vue-router构建的cnodejs web网站SPA  vue-cli-multipage-bootstrap - 将vue官方在线示例整合到组件中  vue-cnode - 用 Vue 做的 CNode 官网  seeMusic - 跨平台云音乐播放器  HyaReader - 移动友好的阅读器  zhihu-daily - 轻松查看知乎日报内容  vue-cnode - 使用cNode社区提供的接口  zhihu-daily-vue - 知乎日报  vue-dropload - 用以测试下拉加载与简单路由  vue-cnode-mobile - 搭建cnode社区  Vuejs-SalePlatform - vuejs搭建的售卖平台demo  vue-memo - 用 vue写的记事本应用  sls-vuex2-demo - vuex2商城购物车demo  v-notes - 简单美观的记事本  vue-starter - VueJs项目的简单启动页  二十、其他实用插件汇总  vue-dragging- 使元素可以拖拽  Vue.Draggable- 实现拖放和视图模型数组同步  vue-picture-input- 移动友好的图片文件输入组件  rubik- 基于Vuejs2的开源 UI 组件库  VueStar- 带星星动画的vue点赞按钮  vue-tables-2- 显示数据的bootstrap样式网格  DataVisualization- 数据可视化  vue-drag-and-drop-list- 创建排序列表的Vue指令  vuwe- 基于微信WeUI所开发的专用于Vue2的组件库  vue-typer- 模拟用户输入选择和删除文本的Vue组件  vue-impression- 移动Vuejs2 UI元素  vue-datatable- 使用Vuejs创建的DataTableView  vue-instant- 轻松创建自动提示的自定义搜索控件  vue-slider-component- 在vue1和vue2中使用滑块  vue-touch-ripple- vuejs的触摸ripple组件  coffeebreak- 实时编辑CSS组件工具  vue-datasource- 创建VueJS动态表格  handsontable- 网页表格组件  vue-bootstrap-table- 可排序可检索的表格  vue-google-signin-button- 导入谷歌登录按钮  vue-float-label- VueJS浮动标签模式  vue-tagsinput- 基于VueJS的标签组件  vue-social-sharing- 社交分享组件  vue-popup-mixin- 用于管理弹出框的遮盖层  cubeex- 包含一套完整的移动UI  vue-fullcalendar- vue FullCalendar封装  vue-material-design- Vue MD风格组件  vue-morris- Vuejs组件封装Morrisjs库  we-vue- Vue2及weui1开发的组件  vue-form-2- 全面的HTML表单管理的解决方案  vue-side-nav- 响应式的侧边导航  mint-indicator- VueJS移动加载指示器插件  vue-ripple- 制作谷歌MD风格涟漪效果的Vue组件  vue-touch-keyboard- VueJS虚拟键盘组件  vue-parallax- 整洁的视觉效果  vue-typewriter- vue组件类型  vue-ios-alertview- iOS7+ 风格的alertview服务  paco-ui-vue- PACOUI的vue组件  vue-button- Vue按钮组件  UI组件 element - 饿了么出品的Vue2的web UI工具套件  Vux - 基于Vue和WeUI的组件库  mint-ui - Vue 2的移动UI元素  iview - 基于 Vuejs 的开源 UI 组件库  Keen-UI - 轻量级的基本UI组件合集  vue-material - 通过Vue Material和Vue 2建立精美的app应用  muse-ui - 三端样式一致的响应式 UI 库  vuetify - 为移动而生的Vue JS 2组件框架  vonic - 快速构建移动端单页应用  eme - 优雅的Markdown编辑器  vue-multiselect - Vue.js选择框解决方案  vue-table - 简化数据表格  VueCircleMenu - 漂亮的vue圆环菜单  vue-chat - vuejs和vuex及webpack的聊天示例  radon-ui - 快速开发产品的Vue组件库  vue-waterfall - Vue.js的瀑布布局组件  vue-carbon - 基于 vue 开发MD风格的移动端  vue-beauty - 由vue和ant design创建的优美UI组件  vue-blu - 帮助你轻松创建web应用  vueAdmin - 基于vuejs2和element的简单的管理员模板  vue-syntax-highlight - Sublime Text语法高亮  vue-infinite-scroll - VueJS的无限滚动指令  Vue.Draggable - 实现拖放和视图模型数组同步  vue-awesome-swiper - vue.js触摸滑动组件  vue-calendar - 日期选择插件  bootstrap-vue - 应用于Vuejs2的Twitter的Bootstrap 4组件  vue-swipe - VueJS触摸滑块  vue-amap - 基于Vue 2和高德地图的地图组件  vue-chartjs - vue中的Chartjs的封装  vue-datepicker - 日历和日期选择组件  markcook - 好看的markdown编辑器  vue-google-maps - 带有双向数据绑定Google地图组件  vue-progressbar - vue轻量级进度条  vue-picture-input - 移动友好的图片文件输入组件  vue-infinite-loading - VueJS的无限滚动插件  vue-upload-component - Vuejs文件上传组件  vue-datetime-picker - 日期时间选择控件  vue-scroller - Vonic UI的功能性组件  vue2-calendar - 支持lunar和日期事件的日期选择器  vue-video-player - VueJS视频及直播播放器  vue-fullcalendar - 基于vue.js的全日历组件  rubik - 基于Vuejs2的开源 UI 组件库  VueStar - 带星星动画的vue点赞按钮  vue-mugen-scroll - 无限滚动组件  mint-loadmore - VueJS的双向下拉刷新组件  vue-tables-2 - 显示数据的bootstrap样式网格  vue-virtual-scroller - 带任意数目数据的顺畅的滚动  DataVisualization - 数据可视化  vue-quill-editor - 基于Quill适用于Vue2的富文本编辑器  Vueditor - 所见即所得的编辑器  vue-html5-editor - html5所见即所得编辑器  vue-msgbox - vuejs的消息框  vue-slider - vue 滑动组件  vue-core-image-upload - 轻量级的vue上传插件  vue-slide - vue轻量级滑动组件  vue-lazyload-img - 移动优化的vue图片懒加载插件  vue-drag-and-drop-list - 创建排序列表的Vue指令  vue-progressive-image - Vue的渐进图像加载插件  vuwe - 基于微信WeUI所开发的专用于Vue2的组件库  vue-dropzone - 用于文件上传的Vue组件  vue-charts - 轻松渲染一个图表  vue-swiper - 易于使用的滑块组件  vue-images - 显示一组图片的lightbox组件  vue-carousel-3d - VueJS的3D轮播组件  vue-region-picker - 选择中国的省份市和地区  vue-typer - 模拟用户输入选择和删除文本的Vue组件  vue-impression - 移动Vuejs2 UI元素  vue-datatable - 使用Vuejs创建的DataTableView  vue-instant - 轻松创建自动提示的自定义搜索控件  vue-dragging - 使元素可以拖拽  vue-slider-component - 在vue1和vue2中使用滑块  vue2-loading-bar - 最简单的仿Youtube加载条视图  vue-datepicker - 漂亮的Vue日期选择器组件  vue-video - Vue.js的HTML5视频播放器  vue-toast-mobile - VueJS的toast插件  vue-image-crop-upload - vue图片剪裁上传组件  vue-tooltip - 带绑定信息提示的提示工具  vue-highcharts - HighCharts组件  vue-touch-ripple - vuejs的触摸ripple组件  coffeebreak - 实时编辑CSS组件工具  vue-datasource - 创建VueJS动态表格  vue2-timepicker - 下拉时间选择器  vue-date-picker - VueJS日期选择器组件  vue-scrollbar - 最简单的滚动区域组件  vue-quill - vue组件构建quill编辑器  vue-google-signin-button - 导入谷歌登录按钮  vue-svgicon - 创建svg图标组件的工具  vue-float-label - VueJS浮动标签模式  vue-baidu-map - 基于 Vue 2的百度地图组件库  vue-social-sharing - 社交分享组件  vue2-editor - HTML编辑器  vue-tagsinput - 基于VueJS的标签组件  vue-easy-slider - Vue 2.x的滑块组件  datepicker - 基于flatpickr的时间选择组件  vue-chart - 强大的高速的vue图表解析  vue-music-master - vue手机端网页音乐播放器  handsontable - 网页表格组件  vue-simplemde - VueJS的Markdown编辑器组件  vue-popup-mixin - 用于管理弹出框的遮盖层  cubeex - 包含一套完整的移动UI  vue-fullcalendar - vue FullCalendar封装  vue-material-design - Vue MD风格组件  vue-morris - Vuejs组件封装Morrisjs库  we-vue - Vue2及weui1开发的组件  vue-image-clip - 基于vue的图像剪辑组件  vue-bootstrap-table - 可排序可检索的表格  vue-radial-progress - Vue.js放射性进度条组件  vue-slick - 实现流畅轮播框的vue组件  vue-pull-to-refresh - Vue2的上拉下拉  vue-form-2 - 全面的HTML表单管理的解决方案  vue-side-nav - 响应式的侧边导航  mint-indicator - VueJS移动加载指示器插件  chartjs - Vue Bulma的chartjs组件  vue-scroll - vue滚动  vue-ripple - 制作谷歌MD风格涟漪效果的Vue组件  vue-touch-keyboard - VueJS虚拟键盘组件  vue-chartkick - VueJS一行代码实现优美图表  vue-ztree - 用 vue 写的树层级组件  vue-m-carousel - vue 移动端轮播组件  vue-datepicker-simple - 基于vue的日期选择器  vue-tabs - 多tab页轻型框架  vue-verify-pop - 带气泡提示的vue校验插件  vue-parallax - 整洁的视觉效果  vue-img-loader - 图片加载UI组件  vue-typewriter - vue组件类型  vue-smoothscroll - smoothscroll的VueJS版本  vue-city - 城市选择器  vue-tree - vue树视图组件  vue-ios-alertview - iOS7+ 风格的alertview服务  dd-vue-component - 订单来了的公共组件库  paco-ui-vue - PACOUI的vue组件  vue-cmap - Vue China map可视化组件  vue-button - Vue按钮组件  开发框架 vue.js - 流行的轻量高效的前端组件化方案  vue-admin - Vue管理面板框架  electron-vue - Electron及VueJS快速启动样板  vue-2.0-boilerplate - Vue2单页应用样板  vue-spa-template - 前后端分离后的单页应用开发  Framework7-Vue - VueJS与Framework7结合  vue-bulma - 轻量级高性能MVVM Admin UI框架  vue-webgulp - 仿VueJS Vue loader示例  vue-element-starter - vue启动页  实用库 vuex - 专为 Vue.js 应用程序开发的状态管理模式  vuelidate - 简单轻量级的基于模块的Vue.js验证  qingcheng - qingcheng主题  vue-desktop - 创建管理面板网站的UI库  vue-meta - 管理app的meta信息  vue-axios - 将axios整合到VueJS的封装  vue-svg-icon - vue2的可变彩色svg图标方案  avoriaz - VueJS测试实用工具库  vue-framework7 - 结合VueJS使用的Framework7组件  vue-bootstrap-modal - vue的Bootstrap样式组件  vuep - 用实时编辑和预览来渲染Vue组件  vue-online - reactive的在线和离线组件  vue-lazy-render - 用于Vue组件的延迟渲染  vue-password-strength-meter - 交互式密码强度计  element-admin - 支持 vuecli 的 Element UI 的后台模板  vue-electron - 将选择的API封装到Vue对象中的插件  cleave - 基于cleave.js的Cleave组件  vue-events - 简化事件的VueJS插件  vue-shortkey - 应用于Vue.js的Vue-ShortKey 插件  vue-cordova - Cordova的VueJS插件  vue-router-transition - 页面过渡插件  vue-gesture - VueJS的手势事件插件  http-vue-loader - 从html及js环境加载vue文件  vue-qart - 用于qartjs的Vue2指令  vuemit - 处理VueJS事件  vue-websocket - VueJS的Websocket插件  vue-local-storage - 具有类型支持的Vuejs本地储存插件  lazy-vue - 懒加载图片  vue-bus - VueJS的事件总线  vue-reactive-storage - vue插件的Reactive层  vue-notifications - 非阻塞通知库  vue-lazy-component - 懒加载组件或者元素的Vue指令  v-media-query - vue中添加用于配合媒体查询的方法  vue-observe-visibility - 当元素在页面上可见或隐藏时检测  vue-ts-loader - 在Vue装载机检查脚本  vue-pagination-2 - 简单通用的分页组件  vuex-i18n - 定位插件  Vue.resize - 检测HTML调整大小事件的vue指令  vuex-shared-mutations - 分享某种Vuex mutations  vue-file-base64 - 将文件转换为Base64的vue组件  modal - Vue Bulma的modal组件  Famous-Vue - Famous库的vue组件  leo-vue-validator - 异步的表单验证组件  Vue-Easy-Validator - 简单的表单验证  vue-truncate-filter - 截断字符串的VueJS过滤器  vue-zoombox - 一个高级zoombox  vue-input-autosize - 基于内容自动调整文本输入的大小  vue-lazyloadImg - 图片懒加载插件  服务端 nuxt.js - 用于服务器渲染Vue app的最小化框架  express-vue - 简单的使用服务器端渲染vue.js  vue-*** - 非常简单的VueJS服务器端渲染模板  vue-*** - 结合Express使用Vue2服务端渲染  vue-easy-renderer - Nodejs服务端渲染  辅助工具 DejaVue - Vuejs可视化及压力测试  vue-play - 展示Vue组件的最小化框架  vscode-VueHelper - 目前vscode最好的vue代码提示插件  vue-generate-component - 轻松生成Vue js组件的CLI工具  vue-multipage-cli - 简单的多页CLI  VuejsStarterKit - vuejs starter套件  应用实例 koel - 基于网络的个人音频流媒体服务  pagekit - 轻量级的CMS建站系统  vuedo - 博客平台  jackblog-vue - 个人博客系统  vue-cnode - 重写vue版cnode社区  CMS-of-Blog - 博客内容管理器  rss-reader - 简单的rss阅读器  vue-ghpages-blog - 依赖GitHub Pages无需本地生成的静态博客  swoole-vue-webim - Web版的聊天应用  vue-dashing-js - nuvo-dashing-js的fork  fewords - 功能极其简单的笔记本  vue-blog - 使用Vue2.0 和Vuex的vue-blog  Demo示例 Vue-cnodejs - 基于vue重写Cnodejs.org的webapp  NeteaseCloudWebApp - 高仿网易云音乐的webapp  vue-zhihu-daily - 知乎日报 with Vuejs  vue-wechat - vue.js开发微信app界面  vue2-demo - 从零构建vue2 + vue-router + vuex 开发环境  eleme - 高仿饿了么app商家详情  vue-demo - vue简易留言板  maizuo - vue/vuex/redux仿卖座网  spa-starter-kit - 单页应用启动套件  vue-music - Vue 音乐搜索播放  douban - 模仿豆瓣前端  vue-Meizi - vue最新实战项目  zhihudaily-vue - 知乎日报web版  vue-demo-kugou - vuejs仿写酷狗音乐webapp  VueDemo_Sell_Eleme - Vue2高仿饿了么外卖平台  vue2.0-taopiaopiao - vue2.0与express构建淘票票页面  vue-leancloud-blog - 一个前后端完全分离的单页应用  node-vue-server-webpack - Node.js+Vue.js+webpack快速开发框架  mi-by-vue - VueJS仿小米官网  vue-fis3 - 流行开源工具集成demo  vue2.x-douban - Vue2实现简易豆瓣电影webApp  vue-demo-maizuo - 使用Vue2全家桶仿制卖座电影  vue-zhihudaily - 知乎日报 Web 版本  vue-adminLte-vue-router - vue和adminLte整合应用  vue-axios-github - 登录拦截登出功能  Zhihu-Daily-Vue.js - Vuejs单页网页应用  hello-vue-django - 使用带有Django的vuejs的样板项目  vue-cnode - vue单页应用demo  x-blog - 开源的个人blog项目  vue-express-mongodb - 简单的前后端分离案例  websocket_chat - 基于vue和websocket的多人在线聊天室  photoShare - 基于图片分享的社交平台  vue-zhihudaily-2.0 - 使用Vue2.0+vue-router+vuex创建的zhihudaily  notepad - 本地存储的记事本  vueBlog - 前后端分离博客  vue-ruby-china - VueJS框架搭建的rubychina平台  Zhihu_Daily - 基于Vue和Nodejs的Web单页应用  vue-koa-demo - 使用Vue2和Koa1的全栈demo  vue2.x-Cnode - 基于vue全家桶的Cnode社区  life-app-vue - 使用vue2完成多功能集合到小webapp  github-explorer - 寻找最有趣的GitHub库  vue-trip - vue2做的出行webapp  vue-***-boilerplate - 精简版的ofvue-hackernews-2  vue-bushishiren - 不是诗人应用  houtai - 基于vue和Element的后台管理系统  ios7-vue - 使用vue2.0 vue-router vuex模拟ios7  Framework7-VueJS - 使用移动框架的示例  cnode-vue - 基于vue和vue-router构建的cnodejs web网站SPA  vue-cli-multipage-bootstrap - 将vue官方在线示例整合到组件中  vue-cnode - 用 Vue 做的 CNode 官网  HyaReader - 移动友好的阅读器  zhihu-daily - 轻松查看知乎日报内容  seeMusic - 跨平台云音乐播放器  vue-cnode - 使用cNode社区提供的接口  zhihu-daily-vue - 知乎日报  sls-vuex2-demo - vuex2商城购物车demo  vue-dropload - 用以测试下拉加载与简单路由  vue-cnode-mobile - 搭建cnode社区  Vuejs-SalePlatform - vuejs搭建的售卖平台demo  v-notes - 简单美观的记事本  vue-starter - VueJs项目的简单启动页  vue-memo - 用 vue写的记事本应用 ———————————————— 版权声明:本文为CSDN博主「喵不可阉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/beifanggongy/article/details/129661207 
  • [技术干货] vue虚拟列表实现【转】
    Vue虚拟列表(Virtual List)可以在大量数据的场景下,提高页面渲染性能和用户体验。其实现方式是只渲染当前可视区域内的数据部分,而非全部渲染数据,可以通过监听滚动事件,动态计算当前可视区域的起始索引和结束索引,并只渲染这一部分的数据。  简单来说就是在固定数量的dom节点上去对大量的返回数据进行渲染(dom节点具体有多少还是取决于前端列表需要展示的数量),例如,一个列表,需要接收后端返回的10w条数据,此时我们只需要10个dom节点来展示,那么这10w条数据,会根据滚动事件在这10个dom节点上来进行视图层的刷新渲染,从而实现虚拟列表的展示  代码放下面!(下方是组件哈,可以自己引用一下,非常简单)  1. 在模板中      <div class="list-container">       <div class="list-item"             v-for="(item, index) in displayedItems"             :key="startIndex + index"            :style="{ height: itemHeight + 'px' }">         <!-- 渲染每一项的内容 -->       </div>     </div>  2. 列表组件中,定义如下 props 和 data      props: {       // 列表每一项数据的固定高度       itemHeight: {         type: Number,         required: true,       },       // 列表展示的数据源       items: {         type: Array,         required: true,       },       // 可视区域的高度       viewHeight: {         type: Number,         required: true,       },     },     data() {       return {         // 当前可视区域首位元素的索引         startIndex: 0,         // 当前可视区域末尾元素的索引         endIndex: 0,         // 实际渲染的数据         displayedItems: [],       }     },  3. 组件的mounted和watch钩子中,初始化和更新当前可视区域数据      mounted() {       this.updateVisibleData()       window.addEventListener('scroll', this.updateVisibleData)     },     watch: {       items() {         this.startIndex = 0         this.endIndex = 0         this.updateVisibleData()       },     },     methods: {       // 计算当前可视区域的起始索引和结束索引       updateVisibleData() {         const scrollTop = window.pageYOffset         const visibleHeight = this.viewHeight + scrollTop         const startIndex = Math.floor(scrollTop / this.itemHeight)         const endIndex = Math.ceil(visibleHeight / this.itemHeight)               if (startIndex !== this.startIndex || endIndex !== this.endIndex) {           this.startIndex = startIndex           this.endIndex = endIndex           this.displayedItems = this.items.slice(startIndex, endIndex)         }       },     } ———————————————— 版权声明:本文为CSDN博主「大家都是小菜鸡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/m0_68330110/article/details/130624136 
  • [技术干货] Vue实现大文件分片上传,包括断点续传以及上传进度条-转载
     首先解释一下什么是分片上传         分片上传就是把一个大的文件分成若干块,一块一块的传输。这样做的好处可以减少重新上传的开销。比如:如果我们上传的文件是一个很大的文件,那么上传的时间应该会比较久,再加上网络不稳定各种因素的影响,很容易导致传输中断,用户除了重新上传文件外没有其他的办法,但是我们可以使用分片上传来解决这个问题。通过分片上传技术,如果网络传输中断,我们重新选择文件只需要传剩余的分片。而不需要重传整个文件,大大减少了重传的开销。  但是我们要如何选择一个合适的分片呢?因此我们要考虑如下几个事情:  1.分片越小,那么请求肯定越多,开销就越大。因此不能设置太小。  2.分片越大,灵活度就少了。  3.服务器端都会有个固定大小的接收Buffer。分片的大小最好是这个值的整数倍。  分片上传的步骤 1.先对文件进行md5加密。使用md5加密的优点是:可以对文件进行唯一标识,同样可以为后台进行文件完整性校验进行比对。  2.拿到md5值以后,服务器端查询下该文件是否已经上传过,如果已经上传过的话,就不用重新再上传。  3.对大文件进行分片。比如一个100M的文件,我们一个分片是5M的话,那么这个文件可以分20次上传。  4.向后台请求接口,接口里的数据就是我们已经上传过的文件块。(注意:为什么要发这个请求?就是为了能断点续传,比如我们使用百度网盘对吧,网盘里面有续传功能,当一个文件传到一半的时候,突然想下班不想上传了,那么服务器就应该记住我之前上传过的文件块,当我打开电脑重新上传的时候,那么它应该跳过我之前已经上传的文件块。再上传后续的块)。  5.开始对未上传过的文件块进行上传。(这个是第二个请求,会把所有的分片合并,然后上传请求)。  6.上传成功后,服务器会进行文件合并。最后完成。  话不多说,直接开始干代码 <template>   <div>   <!-- on-preview    点击文件列表中已上传的文件时的钩子 -->   <!-- http-request    覆盖默认的上传行为,可以自定义上传的实现 -->   <!-- limit    最大允许上传个数 -->   <!-- before-upload    上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->   <!-- accept    接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->   <!-- multiple    是否支持多选文件 -->   <!-- on-change    文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->   <!-- on-remove    文件列表移除文件时的钩子 -->   <!-- file-list    上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->   <!-- on-exceed    文件超出个数限制时的钩子 -->   <!-- auto-upload    是否在选取文件后立即进行上传 -->   <!-- action    必选参数,上传的地址  例如  action="https://jsonplaceholder.typicode.com/posts/"-->   <el-upload     drag     multiple     :auto-upload="true"     :http-request="checkedFile"     :before-remove="removeFile"     :limit="10"     action="/tools/upload_test/"   >     <i class="el-icon-upload"></i>     <div class="el-upload__text">       将文件拖到此处,或       <em>点击上传</em>     </div>   </el-upload>   <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress> </div> </template>  文件上传时,会走http-request方法,如果定义了这个方法,组件的submit方法就会被拦截掉(注意别在这个方法里面调用组件的submit 方法,会造成死循环),在这个方法里面我就可以搞我想搞的事情了。  http-request 这个传入的回调函数应该返回一个Promise,所以我自己定义了一个无用的Promise进去  const prom = new Promise((resolve, reject) => {}) prom.abort = () => {} return prom 如果要实现断点续传,需要和后端确定好,如何配合。 我这里的方案是,在我把所有的分片全部上传一遍后,会请求一个查询接口,后端在这个接口里面返回给我哪些分片没有上传成功(会给我序号),我这个时候,再去重新上传那些没有被上传成功的分片  直接贴完整代码,注释都在里面,看不懂的可以直接联系我,博客上有联系方式(依赖element-ui、axios、spark-md5) <template>   <div>   <!-- on-preview    点击文件列表中已上传的文件时的钩子 -->   <!-- http-request    覆盖默认的上传行为,可以自定义上传的实现 -->   <!-- limit    最大允许上传个数 -->   <!-- before-upload    上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->   <!-- accept    接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->   <!-- multiple    是否支持多选文件 -->   <!-- on-change    文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->   <!-- on-remove    文件列表移除文件时的钩子 -->   <!-- file-list    上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->   <!-- on-exceed    文件超出个数限制时的钩子 -->   <!-- auto-upload    是否在选取文件后立即进行上传 -->   <!-- action    必选参数,上传的地址  例如  action="https://jsonplaceholder.typicode.com/posts/"-->   <el-upload     drag     multiple     :auto-upload="true"     :http-request="checkedFile"     :before-remove="removeFile"     :limit="10"     action="/tools/upload_test/"   >     <i class="el-icon-upload"></i>     <div class="el-upload__text">       将文件拖到此处,或       <em>点击上传</em>     </div>   </el-upload>   <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress> </div> </template>   <script> import axios from "axios"; import SparkMD5 from "spark-md5"; export default {   data() {     return {       maxSize: 5 * 1024 * 1024 * 1024, // 上传最大文件限制  最小单位是b       multiUploadSize: 100 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传)  100mb       eachSize: 100 * 1024 * 1024, // 每块文件大小   100mb       requestCancelQueue: [], // 请求方法队列(调用取消上传       url: "/tools/upload_test/",        //上传进度       progress: 0,       showProgress: false,       // 每上传一块的进度       eachProgress: 0,       // 总共有多少块。断点续传使用       chunksKeep:0,       // 切割后的文件数组       fileChunksKeep:[],       // 这个文件,断点续传       fileKeep:null     };   },   mounted() {        },   methods: {     async checkedFile(options) {       console.log(options);       const {         maxSize,         multiUploadSize,         getSize,         splitUpload,         singleUpload       } = this; // 解构赋值       const { file, onProgress, onSuccess, onError } = options; // 解构赋值       if (file.size > maxSize) {         return this.$message({           message: `您选择的文件大于${getSize(maxSize)}`,           type: "error"         });       }       this.fileKeep = file       const uploadFunc =         file.size > multiUploadSize ? splitUpload : singleUpload; // 选择上传方式       try {         await uploadFunc(file, onProgress);         this.$message({           message: "上传成功",           type: "success"         });         this.showProgress = false;         this.progress = 0;         onSuccess();       } catch (e) {         console.error(e);         this.$message({           message: e.message,           type: "error"         });         this.showProgress = false;         this.progress = 0;         onError();       }       const prom = new Promise((resolve, reject) => {}); // 上传后返回一个promise       prom.abort = () => {};       return prom;     },     // 格式化文件大小显示文字     getSize(size) {       return size > 1024         ? size / 1024 > 1024           ? size / (1024 * 1024) > 1024             ? (size / (1024 * 1024 * 1024)).toFixed(2) + "GB"             : (size / (1024 * 1024)).toFixed(2) + "MB"           : (size / 1024).toFixed(2) + "KB"         : size.toFixed(2) + "B";     },     // 单文件直接上传    async singleUpload(file, onProgress) {       await this.postFile(         { file, uid: file.uid, fileName: file.fileName ,chunk:0},         onProgress       );       var spark = new SparkMD5.ArrayBuffer();       spark.append(file);       var md5 = spark.end();       console.log(md5);           const isValidate = await this.validateFile({             fileName: file.name,             uid: file.uid,             md5:md5,             chunks:1           });     },     // 大文件分块上传     splitUpload(file, onProgress) {       console.log('file11')       console.log(file)       return new Promise(async (resolve, reject) => {         try {           const { eachSize } = this;           const chunks = Math.ceil(file.size / eachSize);           this.chunksKeep = chunks           const fileChunks = await this.splitFile(file, eachSize, chunks);           this.fileChunksKeep = fileChunks           console.log('fileChunks,文件数组切割后')           console.log(fileChunks)           //判断每上传一个文件,进度条涨多少,保留两位小数                      this.eachProgress = parseInt(Math.floor(100 / chunks * 100) / 100);             this.showProgress = true;           let currentChunk = 0;           for (let i = 0; i < fileChunks.length; i++) {             // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传             console.log(currentChunk, i);             // 此时需要判断进度条               if (Number(currentChunk) === i) {               // 每块上传完后则返回需要提交的下一块的index                await this.postFile(                 {                   chunked: true,                   chunk: i,                   chunks,                   eachSize,                   fileName: file.name,                   fullSize: file.size,                   uid: file.uid,                   file: fileChunks[i]                 },                 onProgress               );               currentChunk++                 // 上传完一块后,进度条增加               this.progress += this.eachProgress;               // 不能超过100               this.progress = this.progress > 100 ? 100 : this.progress;             }           }           var spark = new SparkMD5.ArrayBuffer();           spark.append(file);           var md5 = spark.end();           console.log(md5);           const isValidate = await this.validateFile({             chunks: fileChunks.length,             // chunk: fileChunks.length,             fileName: file.name,             uid: file.uid,             md5:md5,             // task_id:file.uid           });           // if (!isValidate) {           //   throw new Error("文件校验异常");           // }           resolve();         } catch (e) {           reject(e);         }       });     },     againSplitUpload(file, array) {       console.log('file,array')       console.log(file)       console.log(array)       return new Promise(async (resolve, reject) => {         try {           const { eachSize , fileKeep } = this;           const chunks = this.chunksKeep           const fileChunks = this.fileChunksKeep           this.showProgress = true;           // let currentChunk = 0;           for (let i = 0; i < array.length; i++) {             // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传             // console.log(currentChunk, i);             // 此时需要判断进度条               // 每块上传完后则返回需要提交的下一块的index                await this.postFile(                 {                   chunked: true,                   chunk: array[i],                   chunks,                   fileName: file.fileName,                   fullSize: fileKeep.size,                   uid: file.uid,                   file: fileChunks[array[i]]                 },               );               // currentChunk++                 // 上传完一块后,进度条增加               // this.progress += this.eachProgress;               // 不能超过100               this.progress = this.progress > 100 ? 100 : this.progress;           }           var spark = new SparkMD5.ArrayBuffer();           spark.append(fileKeep);           var md5 = spark.end();           console.log(md5);           const isValidate = await this.validateFile({             chunks: fileChunks.length,             // chunk: fileChunks.length,             fileName: file.fileName,             uid: file.uid,             md5:md5,             // task_id:file.uid           });           // if (!isValidate) {           //   throw new Error("文件校验异常");           // }           resolve();         } catch (e) {           reject(e);         }       });     },     // 文件分块,利用Array.prototype.slice方法     splitFile(file, eachSize, chunks) {       return new Promise((resolve, reject) => {         try {           setTimeout(() => {             const fileChunk = [];             for (let chunk = 0; chunks > 0; chunks--) {               fileChunk.push(file.slice(chunk, chunk + eachSize));               chunk += eachSize;             }             resolve(fileChunk);           }, 0);         } catch (e) {           console.error(e);           reject(new Error("文件切块发生错误"));         }       });     },     removeFile(file) {       this.requestCancelQueue[file.uid]();       delete this.requestCancelQueue[file.uid];       return true;     },     // 提交文件方法,将参数转换为FormData, 然后通过axios发起请求     postFile(param, onProgress) {       console.log(param);       const formData = new FormData();       // for (let p in param) {         // formData.append(p, param[p]);       // }       formData.append('file', param.file)  //  改了       formData.append('uid',param.uid)       formData.append('chunk',param.chunk)       const { requestCancelQueue } = this;       const config = {         cancelToken: new axios.CancelToken(function executor(cancel) {           if (requestCancelQueue[param.uid]) {             requestCancelQueue[param.uid]();             delete requestCancelQueue[param.uid];           }           requestCancelQueue[param.uid] = cancel;         }),         onUploadProgress: e => {           if (param.chunked) {             e.percent = Number(               (                 ((param.chunk * (param.eachSize - 1) + e.loaded) /                   param.fullSize) *                 100               ).toFixed(2)             );           } else {             e.percent = Number(((e.loaded / e.total) * 100).toFixed(2));           }           onProgress(e);         }       };       // return axios.post('/api/v1/tools/upload_test/', formData, config).then(rs => rs.data)       return this.$http({         url: "/tools/upload_test/",         method: "POST",                  data: formData         // config       }).then(rs => rs.data);     },     // 文件校验方法     validateFile(file) {       // return axios.post('/api/v1/tools/upload_test/', file).then(rs => rs.data)       console.log(2)       console.log(file)     return  this.$http({         url: "/tools/upload_test/upload_success/",         method: "POST",         data: file       }).then(res => {         if(res && res.status == 1){             this.againSplitUpload(file,res.data.error_file)           return true         }       });     }   } }; </script> <style scoped> .progress{   /* 在当前页面居中 */   position: absolute;   top: 50%;   left: 50%;   transform: translate(-50%, -50%);   /* 宽度 */ } </style>  更新代码,上面的代码使用md5加密后,与后端加密的MD5值不一样,下面的加密过后是一样的   <template>   <div :class="showProgress == true ? 'loading' : ''">     <!-- on-preview    点击文件列表中已上传的文件时的钩子 -->     <!-- http-request    覆盖默认的上传行为,可以自定义上传的实现 -->     <!-- limit    最大允许上传个数 -->     <!-- before-upload    上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->     <!-- accept    接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->     <!-- multiple    是否支持多选文件 -->     <!-- on-change    文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->     <!-- on-remove    文件列表移除文件时的钩子 -->     <!-- file-list    上传的文件列表, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}] -->     <!-- on-exceed    文件超出个数限制时的钩子 -->     <!-- auto-upload    是否在选取文件后立即进行上传 -->     <!-- action    必选参数,上传的地址  例如  action="https://jsonplaceholder.typicode.com/posts/"-->     <el-upload drag multiple :auto-upload="true" :http-request="checkedFile" :before-remove="removeFile" :limit="10"       action="/tools/upload_chunk/" :disabled="showProgress">       <i class="el-icon-upload"></i>       <div class="el-upload__text">         将文件拖到此处,或         <em>点击上传</em>       </div>     </el-upload>     <!-- 正在上传的弹窗 -->     <el-dialog title="正在上传" :visible.sync="showProgress" width="50%">       <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>     </el-dialog>     <!-- <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress> -->   </div> </template> <script> import axios from "axios"; import SparkMD5 from "spark-md5"; export default {   data() {     return {       maxSize: 5 * 1024 * 1024 * 1024, // 上传最大文件限制  最小单位是b       multiUploadSize: 100 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传)  100mb       eachSize: 100 * 1024 * 1024, // 每块文件大小   100mb       requestCancelQueue: [], // 请求方法队列(调用取消上传       url: "/tools/upload_chunk/",       //上传进度       progress: 0,       showProgress: false,       // 每上传一块的进度       eachProgress: 0,       // 总共有多少块。断点续传使用       chunksKeep: 0,       // 切割后的文件数组       fileChunksKeep: [],       // 这个文件,断点续传       fileKeep: null,       // 断点续传,文件md5       fileMd5Keep: ""     };   },   mounted() { },   methods: {     async checkedFile(options) {       // console.log(options);       const {         maxSize,         multiUploadSize,         getSize,         splitUpload,         singleUpload       } = this; // 解构赋值       const { file, onProgress, onSuccess, onError } = options; // 解构赋值       if (file.size > maxSize) {         return this.$message({           message: `您选择的文件大于${getSize(maxSize)}`,           type: "error"         });       }       this.fileKeep = file;       const uploadFunc =         file.size > multiUploadSize ? splitUpload : singleUpload; // 选择上传方式       try {         await uploadFunc(file, onProgress);         onSuccess();       } catch (e) {         console.error(e);         this.$message({           message: e.message,           type: "error"         });         this.showProgress = false;         this.progress = 0;         onError();       }       const prom = new Promise((resolve, reject) => { }); // 上传后返回一个promise       prom.abort = () => { };       return prom;     },     // 格式化文件大小显示文字     getSize(size) {       return size > 1024         ? size / 1024 > 1024           ? size / (1024 * 1024) > 1024             ? (size / (1024 * 1024 * 1024)).toFixed(2) + "GB"             : (size / (1024 * 1024)).toFixed(2) + "MB"           : (size / 1024).toFixed(2) + "KB"         : size.toFixed(2) + "B";     },     // 单文件直接上传     async singleUpload(file, onProgress) {       await this.postFile(         { file, uid: file.uid, fileName: file.fileName, chunk: 0 },         onProgress       );       // var spark = new SparkMD5.ArrayBuffer();       // spark.append(file);       // var md5 = spark.end();       // console.log(md5);         const reader = new FileReader();         reader.readAsArrayBuffer(file);       let hashMd5 = "";       console.log(hashMd5);       const that = this;       function getHash(cb) {         console.log("进入单个上传的getHash");         reader.onload = function (e) {           console.log("进入单个上传的getHash的函数2");           console.log(hashMd5);           console.log(this);           // console.log(e)           const hash = SparkMD5.ArrayBuffer.hash(e.target.result);           // const hash = SparkMD5.ArrayBuffer.hash(file);           console.log(hash);           that.hashMd5 = hash;           console.log(that.hashMd5);           that.fileMd5Keep = hash;           cb(hash);         };       }       await getHash(function (hash) {         console.log(hash);         console.log(that);         // 请求接口         that.validateFile({           name: file.name,           uid: file.uid,           md5: hash,           chunks: 1,           filter_type: "user_data_file"         });       });     },     // getMd5(file, chunkCount) {     //   const spark = new SparkMD5.ArrayBuffer();     //   let currentChunk = 0;       //   const reader = new FileReader();       //   reader.onload = function(e) {     //     spark.append(e.target.result);     //     currentChunk++;       //     if (currentChunk < chunkCount) {     //       console.log(currentChunk);     //       loadNext();     //     } else {     //       console.log(spark.end());     //       // 在这里请求接口     //       return spark.end();     //     }     //   };       //   function loadNext() {     //     const start = currentChunk * chunkSize;     //     const end =     //       start + chunkSize >= file.size ? file.size : start + chunkSize;     //     reader.readAsArrayBuffer(file.slice(start, end));     //   }       //   loadNext();     // },     // 大文件分块上传     splitUpload(file, onProgress) {       return new Promise(async (resolve, reject) => {         try {           const { eachSize } = this;           const chunks = Math.ceil(file.size / eachSize);           this.chunksKeep = chunks;           const fileChunks = await this.splitFile(file, eachSize, chunks);           this.fileChunksKeep = fileChunks;           console.log("fileChunks,文件数组切割后");           console.log(fileChunks);           //判断每上传一个文件,进度条涨多少,保留两位小数           this.eachProgress = parseInt(Math.floor((100 / chunks) * 100) / 100);           this.showProgress = true;           let currentChunk = 0;             for (let i = 0; i < fileChunks.length; i++) {             // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传             console.log(currentChunk, i);             // 此时需要判断进度条             if (Number(currentChunk) === i) {               // 每块上传完后则返回需要提交的下一块的index               await this.postFile(                 {                   chunked: true,                   chunk: i,                   chunks,                   eachSize,                   fileName: file.name,                   fullSize: file.size,                   uid: file.uid,                   file: fileChunks[i]                 },                 onProgress               );               currentChunk++;                 // 上传完一块后,进度条增加               this.progress += this.eachProgress;               // 不能超过100               this.progress = this.progress > 100 ? 100 : this.progress;             }           }           // this.getMd5(file, chunks);           // var spark = new SparkMD5.ArrayBuffer();           // spark.append(file);           // var md5 = spark.end();           // console.log(md5);           const spark = new SparkMD5.ArrayBuffer();           let currentChunkMd5 = 0;           const that = this;           const reader = new FileReader();           reader.onload = async function (e) {             spark.append(e.target.result);             currentChunkMd5++;               if (currentChunkMd5 < chunks) {               loadNext();             } else {               // console.log(spark.end());               var hashMd5111 = spark.end();               that.fileMd5Keep = hashMd5111;               console.log(that);               console.log(hashMd5111);               // 在这里请求接口               await that.validateFile({                 name: file.name,                 uid: file.uid,                 md5: hashMd5111,                 chunks: fileChunks.length,                 filter_type: "git_secret_file"                 // chunk: fileChunks.length,               });             }           };             async function loadNext() {             const start = currentChunkMd5 * eachSize;             const end =               start + eachSize >= file.size ? file.size : start + eachSize;             await reader.readAsArrayBuffer(file.slice(start, end));           }           this.$message({             message: "正在进行文件加密校验",             type: "info"           });           await loadNext();           // let hashMd5 = "";           // // console.log(hashMd5)           // const that = this;           // console.log("进入分片上传的getHash");           // function getHash(cb) {           //   reader.onload = function(e) {           //     console.log("进入分片上传的getHash的函数");           //     const hash = SparkMD5.ArrayBuffer.hash(e.target.result);           //     // const hash = SparkMD5.ArrayBuffer.hash(file);           //     console.log(hash);           //     that.hashMd5 = hash;           //     console.log(that.hashMd5);           //     that.fileMd5Keep = hash;           //     cb(hash);           //   };           //   reader.readAsArrayBuffer(file);           // }           // await getHash(function() {           //   console.log(that);           //   that.validateFile({           //     name: file.name,           //     uid: file.uid,           //     md5: that.hashMd5,           //     chunks: fileChunks.length           //     // chunk: fileChunks.length,           //   });           // });           // 请求接口             // console.log('fileChunks.length')           // 请求接口           // this.validateFile({           //   fileName: file.name,           //   uid: file.uid,           //   md5:md5,           //   chunks:1           // });           resolve();         } catch (error) {           reject(error);         }       });     },     // 断点续传     againSplitUpload(file, array) {       console.log("file,array");       console.log(file);       console.log(array);       return new Promise(async (resolve, reject) => {         try {           const { eachSize, fileKeep } = this;           const chunks = this.chunksKeep;           const fileChunks = this.fileChunksKeep;           this.showProgress = true;           // let currentChunk = 0;           for (let i = 0; i < array.length; i++) {             // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传             // console.log(currentChunk, i);             // 此时需要判断进度条             // 每块上传完后则返回需要提交的下一块的index             await this.postFile({               chunked: true,               chunk: array[i],               chunks,               name: file.name,               fullSize: fileKeep.size,               uid: file.uid,               file: fileChunks[array[i]]             });             // currentChunk++               // 上传完一块后,进度条增加             // this.progress += this.eachProgress;             // 不能超过100             this.progress = this.progress > 100 ? 100 : this.progress;           }           // var spark = new SparkMD5.ArrayBuffer();           // spark.append(fileKeep);           // var md5 = spark.end();           // console.log(md5);             var fileMd5KeepTwo = this.fileMd5Keep;             const isValidate = await this.validateFile({             chunks: fileChunks.length,             // chunk: fileChunks.length,             name: file.name,             uid: file.uid,             md5: fileMd5KeepTwo,             filter_type: "git_secret_file"             // task_id:file.uid           });           // if (!isValidate) {           //   throw new Error("文件校验异常");           // }           // 关闭进度条           this.showProgress = false;           // 重置进度条           this.progress = 0;           resolve();         } catch (e) {           reject(e);         }       });     },     // 文件分块,利用Array.prototype.slice方法     splitFile(file, eachSize, chunks) {       return new Promise((resolve, reject) => {         try {           setTimeout(() => {             const fileChunk = [];             for (let chunk = 0; chunks > 0; chunks--) {               fileChunk.push(file.slice(chunk, chunk + eachSize));               chunk += eachSize;             }             resolve(fileChunk);           }, 0);         } catch (e) {           console.error(e);           reject(new Error("文件切块发生错误"));         }       });     },     removeFile(file) {       this.requestCancelQueue[file.uid]();       delete this.requestCancelQueue[file.uid];       return true;     },     // 提交文件方法,将参数转换为FormData, 然后通过axios发起请求     postFile(param, onProgress) {       // console.log(param);       const formData = new FormData();       // for (let p in param) {       // formData.append(p, param[p]);       // }       formData.append("file", param.file); //  改了       formData.append("uid", param.uid);       formData.append("chunk", param.chunk);       formData.append("filter_type", "git_secret_file");       const { requestCancelQueue } = this;       const config = {         cancelToken: new axios.CancelToken(function executor(cancel) {           if (requestCancelQueue[param.uid]) {             requestCancelQueue[param.uid]();             delete requestCancelQueue[param.uid];           }           requestCancelQueue[param.uid] = cancel;         }),         onUploadProgress: e => {           if (param.chunked) {             e.percent = Number(               (                 ((param.chunk * (param.eachSize - 1) + e.loaded) /                   param.fullSize) *                 100               ).toFixed(2)             );           } else {             e.percent = Number(((e.loaded / e.total) * 100).toFixed(2));           }           onProgress(e);         }       };       // return axios.post('/api/v1/tools/upload_chunk/', formData, config).then(rs => rs.data)       return this.$http({         url: "/tools/upload_chunk/",         method: "POST",           data: formData         // config       }).then(rs => rs.data);     },     // 文件校验方法     validateFile(file) {       // return axios.post('/api/v1/tools/upload_chunk/', file).then(rs => rs.data)       return this.$http({         url: "/tools/upload_chunk/upload_success/",         method: "POST",         data: file       }).then(res => {         if (res && res.status == 1) {           this.againSplitUpload(file, res.data.error_file);           this.$message({             message: "有文件上传失败,正在重新上传",             type: "warning"           });         } else if (res && res.status == 0) {           this.$message({             message: "上传成功",             type: "success"           });           this.showProgress = false;           this.progress = 0;         } else if (res && res.status == 40008) {           this.$message.error(res.message);           this.showProgress = false;           this.progress = 0;         }       });     }   } }; </script> <style scoped> .loading {   /* 整体页面置灰 */   /* background: rgba(0, 0, 0, 0.5); */ }   .progress {   /* 在当前页面居中 */   position: absolute;   top: 50%;   left: 50%;   transform: translate(-50%, -50%);   margin-top: 40px;   /* 宽度 */ }   /deep/ .el-dialog {   position: relative;   height: 500px; } </style>  ———————————————— 版权声明:本文为CSDN博主「yjxkq99」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/yjxkq99/article/details/128942133 
  • [技术干货] Vant Uploader 上传图片功能-转载
     van-uploader参考文档 限制上传数量 通过 max-count 属性可以限制上传文件的数量,上传数量达到限制后,会自动隐藏上传区域。 限制上传大小 通过 max-size 属性可以限制上传文件的大小,超过大小的文件会被自动过滤,文件信息通过 oversize 事件获取。 文件上传前进行校验和处理 通过before-read 传入函数,可以在上传前进行校验和处理,支持返回 Promise 对 file 对象进行自定义处理。例如压缩图片:使用compressorjs压缩图片,优化功能,压缩所有格式的图片。 文件上传完毕后获取数据 文件上传后会触发 after-read 回调函数,获取到对应的 file 对象。 更多细节请参考vant官方文档 <template>   <div>     <van-uploader       v-model="fileList"       :max-count="1"       :max-size="2048 * 1024"       :after-read="OnAfterRead"       :before-read="onBeforeRead"       @oversize="onOversize"       @delete="onDelete"     />   </div> </template>  <script> import Compressor from "compressorjs"; export default {   data() {     return {       fileList: [],       IMG_LIST: "", //图片路径            };   },   methods: {     OnAfterRead(result) {       let newImage = new Image();       // 这里将src传入需要获取信息的图片地址或base64       newImage.src = result.content;        newImage.onload = () => {         // 输出图片信息 比如可以获取图片宽高         console.log("图片宽", newImage.width);         console.log("图片高", newImage.height);         this.fileList[0].url = result.content;         // 点击上传图片的结果(base64的图片字符串)IMG_LIST         this.IMG_LIST = this.fileList[0].url;         // 控制台打印         console.log(" this.IMG_LIST ", this.IMG_LIST);       };     },     //上传图片压缩,需要安装依赖: npm i compressorjs,并引入(import) 图片0.6倍压缩     onBeforeRead(file) {       return new Promise((resolve) => {         new Compressor(file, {           quality: 0.6,           success: resolve,           error(err) {             console.log('图片压缩失败---->>>>>',err.message);           },         });       });     },     //图片大小超过2M提示     onOversize(file) {       this.$toast("图片大小不能超过 2M");     },     //点击移除图片     onDelete() {       this.fileList = [];     },   }, }; </script>  <style> </style> ———————————————— 版权声明:本文为CSDN博主「前端小程」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/CSSAJBQ_/article/details/128012890 
  • [技术干货] v-viewer插件使用
    Vue.js 是一个流行的 JavaScript 框架,可以用于构建交互式的 Web 应用程序。要在 Vue.js 中使用 v-viewer 插件,您可以按照以下步骤进行操作:安装 v-viewer:您可以使用 npm 或 yarn 安装 v-viewer 插件。使用 npm 进行安装:npm install v-viewer --save或使用 yarn 进行安装:yarn add v-viewer导入和使用 v-viewer:在您的 Vue 组件中,导入 v-viewer 并使用它。// 在您的组件文件中 import Viewer from 'v-viewer'; import 'viewerjs/dist/viewer.css'; export default { // 组件的其他选项 // 在组件中注册 v-viewer mounted() { this.$nextTick(() => { this.initViewer(); }); }, methods: { initViewer() { this.$viewer = new Viewer(this.$refs.viewerWrapper); }, }, };您需要引入 viewer.css 样式文件,确保 v-viewer 的样式正确加载。在模板中使用 v-viewer:在需要展示图片的模板中,使用 v-for 循环来渲染图片,并使用 ref 添加引用以便在组件中使用。<template> <div> <div ref="viewerWrapper"> <img v-for="(image, index) in images" :src="image" :key="index" @click="$viewer.show()"> </div> </div> </template> <script> export default { // 组件的其他选项 data() { return { images: ['image1.jpg', 'image2.jpg', 'image3.jpg'], }; }, }; </script>在上述示例中,images 数组包含了要展示的图片地址。当点击图片时,使用 $viewer.show() 方法来显示图片。以上是在 Vue.js 中使用 v-viewer 的简单示例。您可以根据自己的需求进行进一步的配置和定制。请确保按照 v-viewer 的文档了解更多可用的选项。
  • [技术干货] 京东微前端和阿里微前端对比
    京东微前端和阿里微前端都是当前比较流行的微前端框架,它们有各自的优点和特点。下面是对两者进行简要对比的一些关键点:总体架构:京东微前端:京东微前端采用了基于 Web Components 的技术栈,使用自研的微前端框架来进行组织和管理微前端应用。阿里微前端:阿里微前端采用了基于微前端架构飞冰(Ice)的技术栈,使用自研的插件化中台框架来实现微前端的组织和运行。技术栈和生态系统:京东微前端:京东微前端使用了 Web Components 技术,不依赖特定的前端框架,可以与任意前端技术栈集成。它提供了一套完整的解决方案,包括构建、打包、路由、通信等。阿里微前端:阿里微前端基于飞冰(Ice)框架,使用 React 和 Vue 作为主要的前端技术栈。它也提供了一套完整的解决方案,包括构建、打包、路由、通信等,并且有丰富的生态系统和插件可用。生态和社区支持:京东微前端:虽然京东微前端是京东自研的框架,但它在社区中的影响力逐渐增强,受到一些开发者的关注和支持。但它的生态系统相对较小。阿里微前端:阿里微前端基于飞冰(Ice)框架,得到了阿里巴巴集团内众多项目的使用和支持,具有较大的社区和生态系统,有更多的插件和工具可供选择。适用场景和定位:京东微前端:京东微前端定位于构建复杂应用系统的微前端架构,适用于需要将多个独立的前端应用集成在一起的场景。阿里微前端:阿里微前端定位于构建企业级中后台应用的微前端架构,适用于大型系统和组织内部的微服务架构。需要注意的是,以上对比仅是针对一些关键点进行的简要对比,具体选型还需要根据实际需求和具体情况进行综合考虑。对于特定的项目和团队,这些微前端框架都可能是合适的选择。建议在选择之前详细了解它们的特点、文档和使用案例,并根据实际需求进行评估和测试。
  • [技术干货] 【vue3】使用canvas-转载
     canvas是什么? 一个html5支持的新标签,见名知意,canvas就是画板的意思,可以在canvas上画画。css画三角形很简单,但是要画五角星呢,不妨试试canvas。  在html中使用canvas 1、canvas是html5中的一个标签。 新建一个html。并在body中加入canvas标签。  <body>     <canvas height="600" width="600"></canvas> </body> 此时canvas已经显示在画板中,只不过因为和body的颜色一样,所以看不出来。  在head中加入css样式。  <style>     canvas {         border:1px solid;     } </style> 这时我们就可以看到canvas了。   2、获取CanvasRenderingContext2D对象用于绘图。 先给canvas一个id属性,  <canvas id='canvas' height="600" width="600"></canvas> 1 获取。  <script>   const ctx=document.querySelector('#canvas').getContext('2d'); </script> 注意,script标签应该在body标签后(至少在canvas标签后),只有在canvas渲染后才能通过JavaScript代码获取到canvas中的CanvasRenderingContext2D对象。  <body>     <canvas height="600" width="600"></canvas> </body> <script>   const ctx=document.querySelector('.canvas').getContext('2d'); </script> 3、使用JavaScript代码进行绘画。 <script>   const ctx=document.querySelector('#canvas').getContext('2d');   ctx.moveTo(100,100);   ctx.lineTo(100,400);   ctx.stroke(); </script> 该代码绘制了一条直线。   关于CanvasRenderingContext2D对象更多的绘制方法请参考官方文档。至少现在我们知道canvas是如何使用的了。(一定要注意要在渲染完成后才能通过JavaScript代码获取CanvasRenderingContext2D对象)  在vue3中使用canvas 1、创建vite+vue3项目并运行。 npm init vue@latest 1  2、创建我们的canvas。 这是我们的App.vue文件  <script setup>  </script>  <template>  </template>  <style scoped>  </style> 创建我们的canvas  <script setup>  </script>  <template>   <canvas height="600" width="600"></canvas> </template>  <style scoped> canvas {   border: 1px solid; } </style>  3、获取CanvasRenderingContext2D对象并绘制直线。 给canvas标签添加一个ref属性  <canvas ref='canvas' height="600" width="600"></canvas> 1 获取canvas对象  <script setup> import {ref} from 'vue' const canvas = ref(); </script> 渲染完成后获取CanvasRenderingContext2D对象  <script setup> import { onMounted, ref } from 'vue'  const canvas = ref();  onMounted(() => {   const ctx = canvas.value.getContext('2d');  })  </script> 画一条直线  <script setup> import { onMounted, ref } from 'vue'  const canvas = ref();  onMounted(() => {   const ctx = canvas.value.getContext('2d');   ctx.moveTo(100, 100);   ctx.lineTo(100, 400);   ctx.stroke();   })  </script>  4、模板 <script setup> import { onMounted, ref } from 'vue'  const canvas = ref(); let ctx = ref();  const drawLine = () => {   ctx.moveTo(100, 100);   ctx.lineTo(100, 400);   ctx.stroke(); }  const initContext = () => {   ctx = canvas.value.getContext('2d'); }  onMounted(() => {   initContext();   drawLine(); })  </script>  <template>   <canvas ref='canvas' height="600" width="600"></canvas> </template>  <style scoped> canvas {   border: 1px solid; } </style>  canvas快速入门 绘制折线 一个moveTo配合多个lineTo。可以通过lineWidth设置线宽,还可以设置两个端点和转折处的形状(使用lineCap和lineJoin)  // 使用moveTo,lineTo,lineWidth,lineCap,lineJoin const drawCurvedLine = () => {   ctx.moveTo(100, 100);   ctx.lineTo(400, 100);   ctx.lineTo(100, 400);   ctx.lineTo(400, 400);   ctx.lineCap = 'round';   ctx.lineJoin = 'round';   ctx.stroke(); } 绘制矩形 rect方法以及strokeRect和fillRect。效果等效:strokeRect=rect+stroke,fillRect=rect+stroke。  绘制方式:绘制边框,使用stroke,绘制填充,使用fill。strokeStyle可以设置边框颜色,fillStyle可以设置填充颜色。  // 使用rect,srokeStyle,stroke,fillStyle,fill const drawStrokeRect = () => {   ctx.rect(100, 100, 100, 100);   ctx.strokeStyle = 'green';   ctx.stroke(); }  const drawFillRect = () => {   ctx.rect(300, 100, 100, 100);   ctx.fillStyle = 'blue';   ctx.fill(); } 将绘制一个绿色边框的矩形和蓝色的矩形。然而,当一同调用时,会发现变成了两个一模一样的矩形(绿色边框或者蓝色填充)。  属性作用域:解决上述问题,使用beginPath方法即可。beginPath将后面对于属性的设置隔离开来,以避免覆盖前面的属性。  // 这里还尝试了使用strokeRect和fillRect替代了rect、stroke、fill const drawStrokeRect = () => {     ctx.beginPath();     ctx.strokeStyle='green';     ctx.strokeRect(100,100,100,100); }  const drawFillRect = () => {   ctx.beginPath();   ctx.fillStyle = 'blue';   ctx.fillRect(300, 100, 100, 100); } 绘制弧线 圆圈  ctx.beginPath(); ctx.arc(100,75,50,0,2*Math.PI); ctx.stroke(); 圆弧  ctx.beginPath(); ctx.arc(100,75,50,90/180*Math.PI,2*Math.PI); ctx.stroke(); 扇形  ctx.beginPath(); ctx.moveTo(100,75); ctx.arc(100,75,50,90/180*Math.PI,2*Math.PI); ctx.closePath(); ctx.fill(); 圆环    const RINGWIDTH = 10;    ctx.beginPath();   ctx.arc(100, 75, 90, 0, 2 * Math.PI);   ctx.fill();   ctx.beginPath();   ctx.arc(100, 75, 90-2*RINGWIDTH, 0, 2 * Math.PI);   ctx.fillStyle = 'white';   ctx.fill(); 补充:  如你所见,绘制扇形时使用了closePath,意思是将所有端点连接起来(一般是将终点和起点连接起来,形成一个闭合图形。只有图形闭合时,fill才能生效)。 所有函数的参数不需要单位。(设置字体时,ctx.font=‘40px’,需要带单位,但确实不是函数的参数) 需要角度作为参数时,都是以弧度的形式提供。计算公式,弧度=角度*Math.PI/180。90度,记为90*Math.PI/180。 更多关于画布的使用,可以查看HTML Canvas 参考手册 (w3school.com.cn) ———————————————— 版权声明:本文为CSDN博主「46590928」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/weixin_46590928/article/details/127358493 
  • [技术干货] Vue中实现自定义excel下载-转载
     最近在工作中遇到一个需求,就是需要在前端实现一个错误模板Excel的下载功能。  实现下载有两种方式,一种是后端生成一个excel,放在服务器指定目录下,然后前端直接去后端拿。第二种是后端传给前端一个json的list,前端用后端传过来的json的list直接在前端合成一个excel。  第一种:后端生成excel java后端生成excel代码  生成excel工具方法  /** * @author: wu linchun * @creat: 2021-07-29 9:49 * @desc: 将list集合转成Excel文件 * list: 内容list * path: 上传的excel路径 * fileName: 上传的excel名称 **/public static String createExcel(List<? extends Object> list, String path, String fileName) {String result = "";if (list.size() == 0 || list == null) {result = "没有对象信息";} else {Object o = list.get(0);Class<? extends Object> clazz = o.getClass();String className = clazz.getSimpleName();//这里通过反射获取字段数组Field[] fields = clazz.getDeclaredFields();File folder = new File(path);if (!folder.exists()) {folder.mkdirs();}String name = fileName.concat(".xls");WritableWorkbook book = null;File file = null;try {file = new File(path.concat(File.separator).concat(name));//创建xls文件book = jxl.Workbook.createWorkbook(file);WritableSheet sheet = book.createSheet(className, 0);//列int i = 0;//行int j = 0;for (Field f : fields) {j = 0;//这里把字段名称写入excel第一行中Label label = new Label(i, j, f.getName());sheet.addCell(label);j = 1;for (Object obj : list) {Object temp = getFieldValueByName(f.getName(), obj);String strTemp = "";if (temp != null) {strTemp = temp.toString();}//把每个对象此字段的属性写入这一列excel中sheet.addCell(new Label(i, j, strTemp));j++;}i++;}book.write();result = file.getPath();} catch (Exception e) {// TODO Auto-generated catch blockresult = "SystemException";e.printStackTrace();} finally {fileName = null;name = null;folder = null;file = null;if (book != null) {try {book.close();} catch (WriteException e) {// TODO Auto-generated catch blockresult = "WriteException";e.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blockresult = "IOException";e.printStackTrace();}}}}//最后输出文件路径return result;}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  下载文件   /** * @author: wu linchun * @creat: 2021-07-29 13:28 * @desc: 下载错误模板 **/@Overridepublic ResponseEntity<Object> downloadErrorModel() throws FileNotFoundException {// 这里的fileName是指路径+文件名String fileName="move_cms/src/main/resources/static/errorList.xls";File file = new File(fileName);InputStreamResource resource = new InputStreamResource(new FileInputStream((file)));org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();headers.add("Content-Disposition", String.format("attachment;filename="%s"", file.getName()));headers.add("Cache-Control", "no-cache,no-store,must-revalidate");headers.add("Pragma", "no-cache");headers.add("Expires", "0");ResponseEntity<Object> responseEntity = ResponseEntity.ok().headers(headers).contentLength(file.length()).contentType(MediaType.parseMediaType("application/text")).body(resource);return responseEntity;}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  下载excel的接口   /** * @author: wu linchun * @creat: 2021-07-29 11:41 * @desc: 下载错误模板 **/@ApiOperation(value = "下载错误模板")@GetMapping(value = "/downloadErrorModel")@PassTokenpublic ResponseEntity<Object> downloadErrorModel() throws FileNotFoundException {System.out.println("下载错误模板");return welfareGrantService.downloadErrorModel();}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  在掉/downloadErrorModel这个接口是要注意,只能用超链接 “<a:href />调用,不能使用axios调这个接口。这是由于axios获取的是json  而这个接口返回的是file类型,file会以byte流的形式在http上面传输,因此调/downloadErrorModel这个接口将会接收到byte流,axios默认是接收不了byte的,需要进行一些特殊的设置。可以参考一下这篇:vue使用axios接收流文件_weixin_43869439的博客-CSDN博客  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  如果使用超链接的方式访问接口进行文件下载的话,则需要获取到后端服务器的ip+port (实际中不建议这么做,因为get请求会暴露ip地址和端口,可通过浏览器f12看到ip地址和端口,不安全)  ip地址可通过 InetAddress.getLocalHost()获取到,端口号直接用@Value从配置文件中拿到。  第二种:前端合成excel 在vue工程中引入两个依赖:file-saver和xlsx  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  使用“npm install”,安装package.json中新增的依赖  npm install  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  新增 ExportExcelUtil.js,用来根据list生成excel  /* eslint-disable */ import { saveAs } from 'file-saver' import XLSX from 'xlsx'  function generateArray(table) {var out = [];var rows = table.querySelectorAll('tr');var ranges = [];for (var R = 0; R < rows.length; ++R) {var outRow = [];var row = rows[R];var columns = row.querySelectorAll('td');for (var C = 0; C < columns.length; ++C) {var cell = columns[C];var colspan = cell.getAttribute('colspan');var rowspan = cell.getAttribute('rowspan');var cellValue = cell.innerText;if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;//Skip rangesranges.forEach(function (range) {if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);}});//Handle Row Spanif (rowspan || colspan) {rowspan = rowspan || 1;colspan = colspan || 1;ranges.push({s: {r: R,c: outRow.length},e: {r: R + rowspan - 1,c: outRow.length + colspan - 1}});};//Handle ValueoutRow.push(cellValue !== "" ? cellValue : null);//Handle Colspanif (colspan)for (var k = 0; k < colspan - 1; ++k) outRow.push(null);}out.push(outRow);}return [out, ranges]; };  function datenum(v, date1904) {if (date1904) v += 1462;var epoch = Date.parse(v);return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); }  function sheet_from_array_of_arrays(data, opts) {var ws = {};var range = {s: {c: 10000000,r: 10000000},e: {c: 0,r: 0}};for (var R = 0; R != data.length; ++R) {for (var C = 0; C != data[R].length; ++C) {if (range.s.r > R) range.s.r = R;if (range.s.c > C) range.s.c = C;if (range.e.r < R) range.e.r = R;if (range.e.c < C) range.e.c = C;var cell = {v: data[R][C]};if (cell.v == null) continue;var cell_ref = XLSX.utils.encode_cell({c: C,r: R});if (typeof cell.v === 'number') cell.t = 'n';else if (typeof cell.v === 'boolean') cell.t = 'b';else if (cell.v instanceof Date) {cell.t = 'n';cell.z = XLSX.SSF._table[14];cell.v = datenum(cell.v);} else cell.t = 's';ws[cell_ref] = cell;}}if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);return ws; }  function Workbook() {if (!(this instanceof Workbook)) return new Workbook();this.SheetNames = [];this.Sheets = {}; }  function s2ab(s) {var buf = new ArrayBuffer(s.length);var view = new Uint8Array(buf);for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;return buf; }  export function export_table_to_excel(id) {var theTable = document.getElementById(id);var oo = generateArray(theTable);var ranges = oo[1];/* original data */var data = oo[0];var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);/* add ranges to worksheet */// ws['!cols'] = ['apple', 'banan'];ws['!merges'] = ranges;/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: 'xlsx',bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx") }  export function export_json_to_excel({multiHeader = [],header,data,filename,merges = [],autoWidth = true,bookType = 'xlsx' } = {}) {/* original data */filename = filename || 'excel-list'data = [...data]data.unshift(header);for (let i = multiHeader.length - 1; i > -1; i--) {data.unshift(multiHeader[i])}var ws_name = "SheetJS";var wb = new Workbook(),ws = sheet_from_array_of_arrays(data);if (merges.length > 0) {if (!ws['!merges']) ws['!merges'] = [];merges.forEach(item => {ws['!merges'].push(XLSX.utils.decode_range(item))})}if (autoWidth) {/*设置worksheet每列的最大宽度*/const colWidth = data.map(row => row.map(val => {/*先判断是否为null/undefined*/if (val == null) {return {'wch': 10};}/*再判断是否为中文*/else if (val.toString().charCodeAt(0) > 255) {return {'wch': val.toString().length * 2};} else {return {'wch': val.toString().length};}}))/*以第一行为初始值*/let result = colWidth[0];for (let i = 1; i < colWidth.length; i++) {for (let j = 0; j < colWidth[i].length; j++) {if (result[j]['wch'] < colWidth[i][j]['wch']) {result[j]['wch'] = colWidth[i][j]['wch'];}}}ws['!cols'] = result;}/* add worksheet to workbook */wb.SheetNames.push(ws_name);wb.Sheets[ws_name] = ws;var wbout = XLSX.write(wb, {bookType: bookType,bookSST: false,type: 'binary'});saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), `${filename}.${bookType}`); }   <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  在.vue中添加下载excel方法  // 下载ExceldownloadExcel () {import('@/utils/ExportExcelUtil').then(excel => {const filterVal = ['item', 'error']const tHeader = ['item', 'error']const data = this.formatModelJson(filterVal, this.errList)console.log(data)console.info(data)excel.export_json_to_excel({header: tHeader,data,filename: 'errorList'})})},// 格式化JSONformatModelJson (filterVal, list) {return list.map(v => filterVal.map(j => {return v[j]}))},  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  errorList是从后端传过来的  exportExcel () {getErrorListExcel().then(response => {this.errList = response.data})}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  使用created方法,在加载.vue时,就调用/getErrorListExcel接口,获取errorList的值。  created () {this.exportExcel()},  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  调用后端接口的api  import axios from 'axios'  export function getErrorListExcel () {return axios.get('http://127.0.0.1:8082/login/getErrorListExcel', {}).then(response => response).catch(error => error) }  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  后端接口设置errorList中的值   @GetMapping("/getErrorListExcel")public List<ErrorItemBO> getErrorListExcel() {List<ErrorItemBO> list = new ArrayList<>();for (int i = 0; i < 10; i++) {ErrorItemBO errorItemBO = new ErrorItemBO();errorItemBO.setItem("item" + i);errorItemBO.setError("error" + i);list.add(errorItemBO);}return list;}  1 <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  <img src=“https://juejin.cn/ “点击并拖拽以移动”” style=“margin: auto” />  总结 如果涉及到文件下载,尽量后端传一个list,然后都在前端合成相应的文件以到达减轻服务器负担的作用。 ​  最后 最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。   ———————————————— 版权声明:本文为CSDN博主「梅花十三儿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Android062005/article/details/127207166 
  • [技术干货] vue-day01 使用cdn引入使用-转载
     vue? Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。  vue简明例子 首先使用cdn引入vue.js文件,<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>。 vue.js?声明式渲染,条件循环指令,处理用户表单的输入,以及自定义组件,模板语法…  <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8">     <title>vue-01</title>  </head> <body>      <div id="app">         {{message}}     </div>     <div id="app2" style="border: 1px solid red;">         <span v-bind:title="message">             鼠标悬停几秒查看信息         </span>     </div>     <div id="app3">         <p v-if="seen">现在你看到我了</p>     </div>     <div id="app4">         <ol>             <li v-for="todo in todos">                 {{todo.text}}             </li>         </ol>     </div>      <div id="app5">         <p>{{message}}</p>         <button v-on:click="reverseStr">点击反转</button>     </div>     <!-- 开发环境版本,包含了有帮助的命令行警告 -->     <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>      <script>         /*简单数据绑定,使用mustache语法将数据与app对象绑定*/         let app = new Vue({             el: '#app',             data: {                 message: 'Hello Vue!'             }         });            /*使用v-bind指令,响应式的更新html属性*/         let app2 = new Vue({             el:'#app2',             data: {                 message:"哈哈哈,我成功了!vue开始"             }         });         /*使用v-if指令,根据值来判断对应元素是否显示*/         let app3 = new Vue({             el: '#app3',             data:{                 seen:true             }         });         /*使用v-for指令,将todos中的内容遍历到html上*/         let app4 = new Vue({             el:'#app4',             data:{                 todos:[                     {text:"学习"},                     {text:"真的"},                     {text:"需要坚持"}                 ]             }         });         /*利用v-on:click来监听点击事件,然后对原本的字符串进行反转*/         let app5 = new Vue({             el:"#app5",             data:{                 message:"反转!"             },             methods:{                 reverseStr:function () {                     this.message=this.message.split('').reverse().join('')                 }             }         });     </script>  </body> </html> 
  • [技术干货] 使用Vue实现轮播图组件
    在本篇博客中,我们将使用Vue来实现一个简单的轮播图组件。我们将利用Vue的生命周期钩子、数据绑定和计算属性等特性来完成这个功能。准备工作首先,我们需要创建一个Vue组件,用于展示轮播图。在这个例子中,我们将创建一个名为Carousel的组件。模板我们首先来定义组件的模板,即HTML结构。我们将使用一个ul元素来包含轮播图的图片,以及一个指示器来显示当前显示的图片。<template> <div class="carousel"> <ul class="slides"> <li v-for="(slide, index) in slides" :key="index" :class="{ active: currentIndex === index }"> <img :src="slide.image" alt="Slide Image"> </li> </ul> <div class="indicators"> <span v-for="(slide, index) in slides" :key="index" :class="{ active: currentIndex === index }" @click="goToSlide(index)" ></span> </div> </div> </template>在上面的代码中,我们使用了Vue的指令v-for来循环渲染轮播图的每个图片。我们还根据当前索引currentIndex来设置每个轮播图项的active类,以及根据点击事件切换到对应的轮播图。数据和计算属性接下来,我们需要在组件的data中定义轮播图的数据和当前索引。我们使用一个数组slides来存储每个轮播图项的信息,以及一个整数currentIndex来表示当前显示的轮播图索引。<script> export default { data() { return { slides: [ { image: 'slide1.jpg' }, { image: 'slide2.jpg' }, { image: 'slide3.jpg' } ], currentIndex: 0 }; }, // ... }; </script>生命周期钩子我们需要在组件挂载后启动轮播功能。为此,我们可以使用Vue提供的mounted生命周期钩子。<script> export default { // ... mounted() { this.startCarousel(); }, // ... }; </script>在mounted钩子中,我们调用startCarousel方法来启动自动轮播功能。方法接下来,我们需要定义一些方法来实现轮播图的切换。<script> export default { // ... methods: { startCarousel() { setInterval(this.nextSlide, 3000); }, nextSlide() { this.currentIndex = (this.currentIndex + 1) % this.slides.length; }, goToSlide(index) { this.currentIndex = index; } } }; </script>在上面的代码中,我们使用setInterval函数来定时调用nextSlide方法,以实现自动切换轮播图。nextSlide方法将当前索引递增,并使用取余运算符确保索引在轮播图数组范围内循环。goToSlide方法用于切换到指定的轮播图。样式最后,我们需要为组件添加一些样式,以使其呈现为一个轮播图。<style scoped> .carousel { position: relative; width: 100%; height: 300px; } .slides { list-style: none; position: relative; width: 100%; height: 100%; } .slides li { position: absolute; /* 设置轮播图项的样式 */ } .indicators { /* 设置指示器的样式 */ } </style>在上述代码中,我们为组件容器.carousel设置了宽度和高度,并使用绝对定位来布局轮播图项。你可以根据需要自定义轮播图项和指示器的样式。运行代码现在,我们已经完成了轮播图组件的实现。你可以在Vue应用中使用<carousel></carousel>标签来引入这个组件。<template> <div> <!-- 其他内容 --> <carousel></carousel> <!-- 其他内容 --> </div> </template> <script> import Carousel from './Carousel.vue'; export default { components: { Carousel }, // ... }; </script>运行代码后,你将看到轮播图按照指定的时间间隔自动切换,并且你也可以点击指示器切换到对应的轮播图。总结通过使用Vue的特性,我们可以很方便地实现一个轮播图组件。我们利用了生命周期钩子、数据绑定和计算属性等功能,使得轮播图的切换和渲染变得简单而灵活。希望本篇博客能对你理解Vue实现轮播图组件有所帮助!如果你有任何问题或疑问,欢迎提出。
  • [技术干货] vue如何使用print-nb库来实现打印功能
    步骤1:安装print-nb首先,我们需要安装print-nb库。我们可以使用npm包管理器执行以下命令来安装:npm install print-nb --save步骤2:引入print-nb接下来,我们需要在Vue组件中引入print-nb库。以下是一种方法:import Print from 'print-nb';步骤3:创建打印功能现在,我们将创建一个Vue方法来处理打印功能。以下是示例代码:print() { let options = { // 打印样式表 styles: [ 'path/to/styles.css' ], // 要打印的元素的id id: 'printable' }; Print(options);}在这里,我们首先定义打印选项。我们指定要在打印中使用的样式表和要打印的元素的id。然后,我们使用Print()函数来处理打印。Print()函数将弹出一个打印对话框,并启动打印功能。步骤4:调用打印功能最后,我们需要在Vue组件中调用打印功能。以下是示例代码:<template> <div> <!-- 要打印的内容 --> <div id="printable"> ... </div> <!-- 触发打印功能 --> <button @click="print">打印</button> </div></template><script>import Print from 'print-nb';export default { methods: { print() { let options = { // 打印样式表 styles: [ 'path/to/styles.css' ], // 要打印的元素的id id: 'printable' }; Print(options); } }}</script>在这里,我们指定要打印的内容,并定义一个按钮来触发打印功能。当用户点击该按钮时,将调用print()方法并执行打印功能。结论到此为止,我们已经学会了如何使用print-nb库在Vue.js应用程序中实现打印功能。print-nb库提供了一个简单而强大的方式来实现打印功能,并可以轻松集成到Vue.js应用程序中。