最近在个人的项目中,想对页面之间跳转的过程进行优化,想到了很多文档或 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

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

最新文章

  1. NodeJs之OS
  2. String、StringBuffer与StringBuilder之间区别
  3. jstl foreach标签
  4. c#操作mysql积累
  5. iniutils for Golang
  6. 2017 New Year’s Greetings from Sun Yat-sen University
  7. log4net--帮助程序员将日志信息输出到各种目标(控制台、文件、数据库等)的工具
  8. TCL_事务控制语言
  9. [置顶] 图书推荐:SQL Server 2012 T-SQL基础 Itzik Ben-Gan
  10. Codeforces Round #271 (Div. 2) E题 Pillars(线段树维护DP)
  11. 三、ASP.NET MVC Controller 控制器(二:IController控制器的创建过程)
  12. gulp自动化压缩合并、加版本号解决方案
  13. KMP算法(研究总结,字符串)
  14. python堆栈实现
  15. shell队列实现线程并发控制(转)
  16. 3.HttpSession
  17. python语言学习--2
  18. 把当前ubuntu系统做成镜像
  19. grep匹配字符串
  20. mysql date

热门文章

  1. 51Nod 1174 区间中最大的数
  2. JavaScript 示例
  3. PHP快速排序(递归)
  4. onclick 事件
  5. loj2353. 「NOI2007」 货币兑换
  6. A4988和CNC SHIELD使用方法 步进电机
  7. Object.assign 的问题
  8. JS 单线程
  9. ehcache 简介和基本api使用
  10. [Windows端口占用] 找到占用端口的进程并杀死