最近在个人的项目中,想对页面之间跳转的过程进行优化,想到了很多文档或 npm 等都用到的页面跳转进度条,于是便想自己去实现一个,特此记录。

来看下 npm 搜索组件时候的效果:

so 下面咱们一起动手实现一下呗。

定义使用方式

想实现一个组件的前提,一定要想好你的需求是什么,还要自己去定义一个舒服的使用方法,这其中也是有原则的,对使用者来说,使用方式越简单越好。那么对应的代价就是写这个组件的复杂度会变高。

我想要的使用方式是这样的:可以在任意的地方去调用到这个方法,可以随时控制其状态。

看下对应的代码:

async someFunction () {

this.$progress.start()

try {

const ret = await axios.get('/xxx')

// some code ...

this.$progress.finish()

} catch (err) {

// cache err ...

this.$progress.fail()

}

}

当然,想在任意地方使用,少不了一个步骤,就是在全局注册这个组件:

import progressBar from 'xxx/progress-bar'

Vue.use(progressBar, { // some config ... })

如果不想全局注册,你也可以在某个组件内单独使用,只要你开心就好。

实现过程 

先来建立一个文件夹和其中两个文件:

progress-bar

- 01 progress-bar.vue

- 02 index.js

打开progress-bar.vue,先来写结构和样式,这里很简单:

<template>

<div :style='style'></div>

</template>

<style scoped>

.bar {

position: fixed;

z-index: 99999;

opacity: 1;

}

</style>

在注册组件的时候,我想可以自定义一些功能,比如

  • 成功的颜色

  • 失败的颜色

  • 进度条的位置

  • 动画过渡时间

  • 加载方向

  • 高度

  • 是否可以自动完成

当然只要你想到的都可以添加,那么这些可以定制的属性,自然而然就成为了组件的 props:

export default {

name: 'progressBar',

props: {

options: {

type: Object,

default () {

return {

succColor: 'rgb(76, 164, 214)',

failColor: 'rgb(218, 26, 101)',

position: 'top',

transition: {

widthSpeed: 200,

opacitySpeed: 400,

duration: 300  // 定义消失时间 ms

},

inverse: false,  // 进度条的加载方向

thickness: 2  // 进度条的高度

}

}

}

}

}

</script>

除了要定义的属性以外,那么组件本身总要有一些自己的属性,用来控制自己的状态,比如这个组件,你要控制进度条的长度、显示和隐藏等状态。

添加 vue 的 data 属性:

data () {

return {

percent: 0,  // 进度条长度

show: false, // 显示和隐藏

canSuccess: true  // 是否是成功的状态

}

}

有了这些属性,这个进度条就要根据这些属性的变化来“自己动”。所以这个时候首先想到的当然就是 Vue 的计算属性:

computed: {

style () {

// 先拿到乱七八糟的属性

const { succColor, failColor, location, transition, inverse, thickness } = this.options

const { widthSpeed, opacitySpeed } = transition

const { canSuccess, preset, show } = this

// 定义 css 样式

const style = {

backgroundColor: canSuccess ? succColor : failColor,

opacity: show ? 1 : 0

}

if (position !== 'top' || position !== 'bottom') {

throw new Error('The wrong config of position!')

}

style[position] = 0

if (inverse) {

style.left = 0

} else {

style.right = 0

}

style.width = preset + '%'  // 设置进度条长度

style.height = thickness + 'px'  // 设置显示高度

style.transition = `width ${widthSpeed}ms, opacity ${opacitySpeed}ms`  // 设置过度样式

return style

}

}

到这里这个 vue 组件其实就完成了,接下来就是如何去控制它。打开index.js,先来写一个标准的组件格式:

import progressBar from './progress-bar.vue'

export default {

install (Vue, options = {}) {

// 注册组件

Vue.component(progressBar.name, progressBar)

}

}

之后咱们要用到 Vue 提供的扩展方法,来完成咱们的需求。

第一步,添加 autoFinish 属性,用来设定动画是否可以自动完成,默认是 true,当然如果某个路由或请求一直处于 pending 状态,你可以可以设置让其永远不完成动画的动作。

第二步,来写一个对象,其中包含 start 、 finish 、 fail 方法以及动画代码。

第三步,将这个对象挂在到 Vue 的原型

 完整的代码和说明如下:

import progressBar from './progress-bar.vue'

export default {

install (Vue, options = {}) {

// 注册组件

Vue.component(progressBar.name, progressBar)

// 创建一个 Vue 子类

const Component = Vue.extend(progressBar)

// 拿到自定义的属性

const { autoFinish, ...res } = options

// 创建组件实例

const vm = new Component({

data: {

autoFinish: typeof autoFinish === 'boolean' ? autoFinish : true

}

})

// 将 progressBar 的默认 options 与 自定义的 options 合并

options = Object.assign(vm.$props.options, { ...res })

//合并新的 props

vm.$propsData = options

vm.$mount()

// 如果是服务端渲染那么不继续执行

if (!vm.$isServer) {

document.body.appendChild(vm.$el)

}

let timer = null

const progress = {

start () {

if (Vue.$isServer) return

// 每次调用 start 都重新初始化一次,比如多次点击某个按钮连续请求,那么每次都从0开始

vm.percent = 0

vm.show = true

vm.canSuccess = true

// 定一个增量,这个值可以改成参数,也可以按照使用经验来设定

const CUT_SCALE = 5

// 定义每 100 秒来执行一次动画

timer = setInterval(() => {

// 每次执行增量动画

this.increase((CUT_SCALE - 1) * Math.random() + 1)

// 如果进度大于 95%,并且设置了自动完成,那么执行结束动作

if (vm.percent > 95 && vm.autoFinish) {

this.finish()

}

}, 100)

},

increase (cut) {

vm.percent = Math.min(99, vm.percent + cut)

},

hide () {

clearInterval(timer)

// 这里面有2个定时器,外层定时器是让用户可以看到这个 进度已经完成啦

// 内层定时器,由于 opacity 消失需要一定的过渡时间,所以要等待它消失以后

// 在将其进度设置为0,等待下次调用,如果不延迟,那么会看到进度到100后又回到0的动画

setTimeout(() => {

vm.show = false

setTimeout(() => {

vm.percent = 0

timer = null

}, vm.options.transition.opacitySpeed)

}, vm.options.transition.duration)

},

// 下面这2个方法就很简单了,只需要完成进度,然后执行隐藏即可

finish () {

if (Vue.$isServer) return

vm.percent = 100

this.hide()

},

fail () {

if (Vue.$isServer) return

// 修改未成功的状态,实际效果就是改变最后的颜色

vm.canSuccess = false

vm.percent = 100

this.hide()

}

}

// 最后挂在到全局

Vue.prototype.$progress = progress

}

}

到这里,一个进度条组件就完成了。大家可以自己动手实践一下,起一个项目,使用 vue-router 的 beforeResolve 声明周期钩子,或者写一个定时器模拟异步来测试一下。

以上是笔者归纳总结,如有误之处,欢迎指出。

往期文章推荐:

觉得本文对你有帮助?请分享给更多人

关注【妙味前端】加星标,关注更多内容

订阅号ID:Miaovclass

关注妙味订阅号:“妙味前端”,为您带来优质前端技术干货;

手把手教你实现一个 Vue 进度条组件!的更多相关文章

  1. 手把手教你实现一个Vue无限级联树形表格(增删改)

    前言平时我们可能在做项目时,会遇到一个业务逻辑.实现一个无限级联树形表格,什么叫做无限级联树形表格呢?就是下图所展示的内容,有一个祖元素,然后下面可能有很多子孙元素,你可以实现添加.编辑.删除这样几个 ...

  2. 基于Vue的事件响应式进度条组件

    写在前面 找了很多Vue 进度条组件!,都不包含拖拽和点击事件,input range倒是原生包含input和change事件,但是直接基于input range做进度条的话,样式部分需要做大量调整和 ...

  3. 手把手从零开始---封装一个vue视频播放器组件

    现在,在网页上播放视频已经越来越流行,但是网上的资料鱼龙混杂,很难找到自己想要的,今天小编就自己的亲身开发体验,手把手从零开始---封装一个vue视频播放器组件. 作为一个老道的前端搬砖师,怎么可能会 ...

  4. 只有20行Javascript代码!手把手教你写一个页面模板引擎

    http://www.toobug.net/article/how_to_design_front_end_template_engine.html http://barretlee.com/webs ...

  5. vue 的进度条组件

    先看效果: 要想实现如上图的,进度跳效果,有两种方式,首先介绍第一种: 1.自己用 div 写一个,代码如下 <template> <div class="mfc-slid ...

  6. vue+element UI + axios封装文件上传及进度条组件

    1.前言 之前在做项目的时候,需要实现一个文件上传组件并且需要有文件上传进度条,现将之前的实现过程简单记录一下,希望可以帮助到有需要的人. 项目用的是Vue框架,UI库使用的是element UI,前 ...

  7. PWA入门:手把手教你制作一个PWA应用

    摘要: PWA图文教程 原文:PWA入门:手把手教你制作一个PWA应用 作者:MudOnTire Fundebug经授权转载,版权归原作者所有. 简介 Web前端的同学是否想过学习app开发,以弥补自 ...

  8. 手把手教你用JS/Vue/React实现幸运水果机(80后情怀之作)

    项目体验地址 免费视频教程 分别使用原生JS,Vue和React,手把手教你开发一个H5小游戏,快速上手Vue和React框架的使用. 项目截图 在线体验 在线体验 游戏介绍 幸运水果机是一款街机游戏 ...

  9. R数据分析:跟随top期刊手把手教你做一个临床预测模型

    临床预测模型也是大家比较感兴趣的,今天就带着大家看一篇临床预测模型的文章,并且用一个例子给大家过一遍做法. 这篇文章来自护理领域顶级期刊的文章,文章名在下面 Ballesta-Castillejos ...

随机推荐

  1. The Code analysis of the FFDNet model

    1. 读取图像并判断是否为灰度图,如为RGB图转化为灰度图,并读取图像的w.h 2.数据格式转换:将uint8表示的读取图像矩阵变为double表示. 3.加入噪声,如果噪声水平$\sigma = 5 ...

  2. URI/URL/URN的联系和区别

    下面是我整理的一些关于他们的描述. URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源. 而URL是uniform resource locato ...

  3. 什么是DNS攻击?它是如何工作的?

    什么是DNS攻击?它是如何工作的? DNS攻击是一种利用域名系统中的弱点或漏洞的网络攻击.今天,互联网已成为我们生活中不可或缺的一部分.从社交到金融.购物再到旅游,我们生活的方方面面都是互联网.由于互 ...

  4. shell爬虫--抓取某在线文档所有页面

    在线教程一般像流水线一样,页面有上一页下一页的按钮,因此,可以利用shell写一个爬虫读取下一页链接地址,配合wget将教程所有内容抓取. 以postgresql中文网为例.下面是实例代码 #!/bi ...

  5. [c/c++] programming之路(21)、字符串(二)

    一.for /l %i in (1,1,5) do calc 等命令行参数 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #inclu ...

  6. conda环境py36 requirements.txt

    生成: conda list -e > requirements.txt 使用: conda install --yes --file requirements.txt # This file ...

  7. vue 文件目录结构详解

    vue 文件目录结构详解 本篇文章主要介绍了vue 文件目录结构详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 项目简介 基于 vue.js 的前端开发环境,用于前后 ...

  8. 启动xampp出错,Port 80 in use by "Unable to open process" with PID 4!

    启动xampp出错,Port 80 in use by "Unable to open process" with PID 4! 环境:windows10 80端口被PID为4的应 ...

  9. itsdangerous

    将字典进行加密.解密 通过私钥保证安全性 serializer=TimedJSONWebSignatureSerializer(私钥,过期时间) dumps(字典)=====>返回加密字符串 l ...

  10. 整理this笔记

    1.在浏览器全局环境中this指向的是Window console.log(this); //Window 2.在事件处理函数中的this,这个事件是由谁触发,this就指向谁 3.直接执行一个函数的 ...