如何实现vue加载指令 v-loading

 更新时间:2024年01月15日 10:53:59   作者:劫辞  
在日常的开发中,加载效果是非常常见的,但是怎么才能方便的使用,本文介绍如何实现vue加载指令 v-loading,感兴趣的朋友一起看看吧

本文不会详细的说明 vue 中指令这些知识点,如果存在疑问,请自行查阅文档或者其他资料

为什么使用指令实现

  • 在日常的开发中,加载效果是非常常见的,但是怎么才能方便的使用,那就还是值得思考一番的,
  • 比如在 vue 中,最简单的方式就是封装为一个组件了,但是如果封装为组件的话,在不想注册为全局组件的时候,每次都需要引入、注册、使用;如果注册为全局组件,你也往往需要分析结构在合适的位置插入组件,貌似使用起来都会麻烦一点,loading 这种使用频率高的效果,使用一次麻烦一点,使用100次就会觉得更加麻烦
  • 而使用指令只需要在需要的位置像使用属性一样即可;封装可以麻烦,但是使用越简单越好

具体实现

封装准备

1.首先需要一个 js 文件,因为指令实际上就是一个对象,通过在不同的钩子函数中执行对应的逻辑,在本文中,需要使用的钩子函数是 inserted 和 update,因此可以写一个基础的结构,如下:

export default {
    inserted(el, binding){
    },
    update(el, binding){
    }
}

2.然后将这个指令在入口文件 main.js 内进行全局注册,如下:

import vLoading from '你封装指令js文件的路径'
// 注册指令
Vue.directive('jc-loading', vLoading)

3.创建一个 vue 文件来使用这个指令,如下:

<template>
	<div class="container">
		<button
			style="margin-bottom: 20px"
			@click="handleClick">
			开关
		</button>
		<div
			class="box"
			v-jcLoading="isLoading">
			Lorem ipsum dolor sit amet consectetur adipisicing elit. Libero, temporibus veniam! Totam temporibus ipsam, atque
			amet aliquid corporis molestiae, perspiciatis asperiores doloremque enim explicabo aperiam. Vel doloremque
			voluptatibus incidunt quae suscipit cupiditate. Obcaecati sunt, consectetur voluptas sequi aliquam omnis, rem non
			molestiae assumenda illum quasi excepturi error voluptatibus pariatur nulla.
		</div>
	</div>
</template>
<script>
export default {
	data() {
		return {
			isLoading: false
		}
	},
	methods: {
		handleClick() {
			this.isLoading = !this.isLoading
		}
	}
}
</script>
<style lang="less" scoped>
.container {
	width: 100vw;
	height: 100vh;
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: center;
	.box {
		width: 500px;
		height: 300px;
		padding: 20px;
		border: 1px solid #999;
		color: #f40;
	}
}
</style>

4.查看一下指令内的输出语句是否正常执行,如图:

5.正常进行了打印,现在我们进行正式的编写

实现 loading 效果

1.实现这一步其实也非常的简单,找一个你觉得好看或者合适的加载效果,按照正常的 html+css+js 进行实现就好,当你实现好之后,需要做的就是使用 js 进行动态的创建这些元素,所以我们需要有一个函数帮助我们完成这一步,如下:

// 导入模块化的 less 文件
import styles from './loading.module.less'
// 创建 loading 元素
function createLoading() {
	// 创建 load 遮罩
	const loadingMask = document.createElement('div')
	loadingMask.dataset.role = 'jc-loading'
	loadingMask.classList.add(styles['jc-loading-mask'])
	// 创建 loading 旋转容器元素
	const loadingSpinner = document.createElement('div')
	loadingSpinner.classList.add(styles['jc-loading-spinner'])
	loadingMask.appendChild(loadingSpinner)
	// 创建文本片段
	const fragment = document.createDocumentFragment()
	// 创建子元素进行旋转缩放
	for (let i = 0; i < 12; i++) {
		const div = document.createElement('div')
		div.style = `--i:${i}`
		div.classList.add(styles['jc-loading-spinner__circle'])
		fragment.appendChild(div)
	}
	loadingSpinner.appendChild(fragment)
	return loadingMask
}

2.代码还是非常简单的,具体取决于你本身实现的 loading 效果,我这个是一个比较简单的动效,上面这个地方如果有疑问那应该就是证据导入语句,在 vue 中,如果希望一个 less 文件作为一个模块导入和使用,需要将文件命名改为 文件名.module.less 这种格式,即文件后缀为 .module.less,我们在 inserted 钩子函数中打印一下这个导入的 styles,如下:

export default {
    inserted(el, binding){
        console.log(styles)
    },
    update(el, binding){
    }
}

3.结果如图:

4.k 为 less 文件中开发时书写的类名,而后面的 v 表示实际的类名,本案例中 less 文件代码如下:

.jc-loading-mask {
	position: absolute;
	inset: 0;
	background-color: rgba(0, 0, 0, 0.7);
}
.jc-loading-spinner {
	position: absolute;
	left: calc(50% - 25px);
	top: calc(50% - 25px);
	width: 50px;
	height: 50px;
	animation: sp 4s linear infinite;
}
.jc-loading-spinner__circle {
	position: absolute;
	top: 0;
	left: calc(50% - 3px);
	width: 6px;
	height: 6px;
	transform: rotate(calc(var(--i) * (360deg / 12)));
	transform-origin: center 25px;
}
.jc-loading-spinner__circle::before {
	content: '';
	inset: 0;
	border-radius: 50%;
	position: absolute;
	background-color: #ff6348;
	animation: zoom 2.5s linear infinite;
	animation-delay: calc(var(--i) * 0.2s);
}
@keyframes sp {
	to {
		transform: rotate(360deg);
	}
}
@keyframes zoom {
	0% {
		transform: scale(1.2);
	}
	50% {
		transform: scale(0.5);
	}
	100% {
		transform: scale(1.2);
	}
}

5.这些 css 样式我就不再赘述了,先不进行其他逻辑判断,只展示到页面上,看看效果,代码如下:

export default {
    inserted(el, binding){
      	el.appendChild(createLoading())
    },
    update(el, binding){
    }
}

6.效果如图:

7.其实也不难对吧,这个效果你可以根据自己的需求来进行更换,但是实现方法都是可以套用的

loading 显示与隐藏

1.把这个需求梳理清楚之后,后面的就呼之欲出了,什么时候显示,必然是指令上的值为 true 的时候,隐藏则相反,这是一个先决条件

2.在这个条件之后呢?还需要考虑什么呢?是不是需要创建这个 loading 效果的元素啊,当指令的值为 true 且不存在当前的 loading 元素的时候,才需要创建,而指令的值为 false ,则是当前的 loading 元素存在的话,就需要移除啊

3.基于上面的条件,我们需要一个辅助函数,来帮助我们查找当前 loading 效果的元素是否存在,如下:

function getLoading(container) {
	return container.querySelector(`[data-role="jc-loading"]`)
}

4.所以我们在 inserted 钩子函数中,应该进行判断,当指令的值为 true 且元素不存在时,就创建元素并添加,如下:

inserted(el, binding){
	// 如果为 true 且不存在加载元素就创建元素添加加载效果
	if (!getLoading(el)) {
		const loading = createLoading()
		el.appendChild(loading)
	}
}

5.而 update 函数中的代码是不是也可以写出来了,进行条件判断来执行逻辑,而且不难发现其实这个条件与 inserted 中的条件是重合的,所以我们可以封装为一个函数,如下:

// 开启加载效果
function openLoading(el, binding) {
	// 如果为 false 且存在加载元素就移除加载元素
	if (!binding.value) {
		const dom = getLoading(el)
		dom && dom.remove()
	} else {
		// 如果为 true 且不存在加载元素就创建元素添加加载效果
		if (!getLoading(el)) {
			const loading = createLoading()
			el.appendChild(loading)
		}
	}
}

6.当然,还需要考虑当前显示加载元素的 dom 是不是存在相对定位,如果不存在则改为相对定位,最后指令对象的实际代码如下:

export default {
    inserted(el, binding){
        // 检测绑定的元素的 position 属性是否为 static
		if (window.getComputedStyle(el).position === 'static') {
			// 如果是则改为相对定位
			el.style.position = 'relative'
		}
      	openLoading(el, binding)
    },
    update(el, binding){
        openLoading(el, binding)
    }
}

7.我们看一下实际的效果,如图:

使用修饰符扩展

1.通过 modifiers(修饰符) 进行一个扩展,当指令了添加了修饰符 body 的时候,loading 就会插入到 body 里面,填充 body,所以我们还需要进行一些额外的判断,如下:

function getContainer(el, binding) {
	return binding.modifiers.body ? document.body : el
}
export default {
	inserted(el, binding) {
		if (window.getComputedStyle(el).position === 'static') {
			el.style.position = 'relative'
		}
		openLoading(getContainer(el, binding), binding)
	},
	update(el, binding) {
		openLoading(getContainer(el, binding), binding)
	}
}

2.此时在组件中使用添加修饰符 body 即可,如下:

<!-- 添加修饰符.body -->
<div class="box" v-jcLoading.body="isLoading">
...
</div>

3.查看效果,如图:

4.元素实际插入的位置,如图:

完整代码与结语

1.现在已经具备了一个 loading 指令基本的效果,如果还需要进行其他扩展,比如传递给 loading 指令的值不是一个单纯的布尔值,而是一个对象,如下:

{ loading:true, color: 'blue', text: '拼命加载中...' ... }

2.通过这些配置来增强指令的效果,有兴趣的可以自己试试

3.完整指令代码:

import styles from './loading.module.less'
function getLoading(container) {
	return container.querySelector(`[data-role="jc-loading"]`)
}
function createLoading() {
	const loadingMask = document.createElement('div')
	loadingMask.dataset.role = 'jc-loading'
	loadingMask.classList.add(styles['jc-loading-mask'])
	const loadingSpinner = document.createElement('div')
	loadingSpinner.classList.add(styles['jc-loading-spinner'])
	loadingMask.appendChild(loadingSpinner)
	const fragment = document.createDocumentFragment()
	for (let i = 0; i < 12; i++) {
		const div = document.createElement('div')
		div.style = `--i:${i}`
		div.classList.add(styles['jc-loading-spinner__circle'])
		fragment.appendChild(div)
	}
	loadingSpinner.appendChild(fragment)
	return loadingMask
}
function openLoading(el, binding) {
	if (!binding.value) {
		const dom = getLoading(el)
		dom && dom.remove()
	} else {
		if (!getLoading(el)) {
			const loading = createLoading()
			el.appendChild(loading)
		}
	}
}
function getContainer(el, binding) {
	return binding.modifiers.body ? document.body : el
}
export default {
	inserted(el, binding) {
		if (window.getComputedStyle(el).position === 'static') {
			el.style.position = 'relative'
		}
		openLoading(getContainer(el, binding), binding)
	},
	update(el, binding) {
		openLoading(getContainer(el, binding), binding)
	}
}

4.less 样式代码:

.jc-loading-mask {
	position: absolute;
	inset: 0;
	background-color: rgba(0, 0, 0, 0.7);
}
.jc-loading-spinner {
	position: absolute;
	left: calc(50% - 25px);
	top: calc(50% - 25px);
	width: 50px;
	height: 50px;
	animation: sp 4s linear infinite;
}
.jc-loading-spinner__circle {
	position: absolute;
	top: 0;
	left: calc(50% - 3px);
	width: 6px;
	height: 6px;
	transform: rotate(calc(var(--i) * (360deg / 12)));
	transform-origin: center 25px;
}
.jc-loading-spinner__circle::before {
	content: '';
	inset: 0;
	border-radius: 50%;
	position: absolute;
	background-color: #ff6348;
	animation: zoom 2.5s linear infinite;
	animation-delay: calc(var(--i) * 0.2s);
}
@keyframes sp {
	to {
		transform: rotate(360deg);
	}
}
@keyframes zoom {
	0% {
		transform: scale(1.2);
	}
	50% {
		transform: scale(0.5);
	}
	100% {
		transform: scale(1.2);
	}
}

到此这篇关于如何实现vue加载指令 v-loading的文章就介绍到这了,更多相关vue加载指令 v-loading内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于vue配置axios的方法步骤

    基于vue配置axios的方法步骤

    这篇文章主要介绍了基于vue配置axios的方法步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • 详解nuxt sass全局变量(公共scss解决方案)

    详解nuxt sass全局变量(公共scss解决方案)

    这篇文章主要介绍了详解nuxt sass全局变量(公共scss解决方案),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • 详解vue+vueRouter+webpack的简单实例

    详解vue+vueRouter+webpack的简单实例

    这篇文章主要介绍了详解vue+vueRouter+webpack的简单实例,非常具有实用价值,需要的朋友可以参考下
    2017-06-06
  • 详解Vue中数据可视化词云展示与词云的生成

    详解Vue中数据可视化词云展示与词云的生成

    数据可视化是现代Web应用程序中的一个重要组成部分,词云是一种非常流行的数据可视化形式,可以用来展示文本数据中的主题和关键字,本文我们将介绍如何在Vue中使用词云库进行数据可视化词云展示和词云生成,需要的可以参考一下
    2023-06-06
  • vue2实现pdf电子签章问题记录

    vue2实现pdf电子签章问题记录

    仿照e签宝,实现pdf电子签章 => 拿到pdf链接,移动章的位置,获取章的坐标,怎么实现呢,下面小编给大家介绍vue2实现pdf电子签章问题记录,感兴趣的朋友一起看看吧
    2023-12-12
  • 一文详解Vue的响应式原则与双向数据绑定

    一文详解Vue的响应式原则与双向数据绑定

    使用 Vue.js 久了,还是不明白响应式原理和双向数据绑定的区别?今天,我们就一起来学习一下,将解释它们的区别,快跟随小编一起学习学习吧
    2022-08-08
  • Vue实现登录保存token并校验实现保存登录状态的操作代码

    Vue实现登录保存token并校验实现保存登录状态的操作代码

    这篇文章主要介绍了Vue实现登录保存token并校验实现保存登录状态,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-02-02
  • vue新vue-cli3环境配置和模拟json数据的实例

    vue新vue-cli3环境配置和模拟json数据的实例

    今天小编就为大家分享一篇vue新vue-cli3环境配置和模拟json数据的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-09-09
  • vue+element-ui实现表格编辑的三种实现方式

    vue+element-ui实现表格编辑的三种实现方式

    这篇文章主要介绍了vue+element-ui实现表格编辑的三种实现方式,主要有表格内部显示和编辑切换,通过弹出另外一个表格编辑和直接通过样式控制三种方式,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • 解决Vue在Tomcat8下部署页面不加载的问题

    解决Vue在Tomcat8下部署页面不加载的问题

    今天小编就为大家分享一篇解决Vue在Tomcat8下部署页面不加载的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11

最新评论