1. 项目概述
在Vue.js项目中,加载状态指示器(loading)是提升用户体验的重要组件。Element UI等主流UI库虽然提供了默认的loading样式,但在实际项目中,我们经常需要根据设计规范调整其外观,特别是颜色和大小,以保持与整体UI风格的一致性。
本文将详细介绍如何在Vue.js项目中自定义loading组件的样式,包括:
- 通过customClass属性添加自定义类名
- 精准控制loading文字和旋转图标的样式
- 使用SCSS实现局部作用域样式
- 避免全局样式污染的最佳实践
2. 核心实现原理
2.1 Element UI Loading组件基础
Element UI的Loading服务通过this.$loading()方法调用,返回一个Loading实例。主要配置参数包括:
javascript复制const loadingInstance = this.$loading({
lock: true, // 是否锁定屏幕滚动
text: '加载中...', // 提示文字
spinner: 'el-icon-loading', // 旋转图标类名
background: 'rgba(0, 0, 0, 0.7)', // 背景色
customClass: 'my-custom-class' // 自定义类名
})
2.2 样式覆盖机制
Element UI的Loading组件DOM结构通常如下:
html复制<div class="el-loading-mask custom-loading-class">
<div class="el-loading-spinner">
<i class="el-icon-loading"></i>
<p class="el-loading-text">正在加载...</p>
</div>
</div>
通过添加customClass,我们可以精准定位到特定实例的loading元素,避免全局样式污染。
3. 完整实现步骤
3.1 添加自定义类名
在Vue组件中调用Loading服务时,添加customClass属性:
javascript复制methods: {
showLoading() {
this.loadingInstance = this.$loading({
lock: true,
text: '正在运行品质评价,请稍后...',
background: 'rgba(0, 0, 0, 0.7)',
spinner: 'el-icon-loading',
customClass: 'custom-loading-class' // 关键配置
})
},
hideLoading() {
this.loadingInstance.close()
}
}
3.2 编写局部样式
在Vue单文件组件的<style>标签中编写SCSS样式:
scss复制<style lang="scss">
// 使用深度选择器确保样式穿透
.custom-loading-class {
// 修改文字样式
.el-loading-text {
color: #d7d7d7 !important;
font-size: 16px !important;
letter-spacing: 0.5px; // 增加可读性
margin-top: 12px; // 调整文字与图标的间距
}
// 修改旋转图标样式
.el-loading-spinner i {
font-size: 24px !important; // 增大图标尺寸
color: #d7d7d7 !important;
animation-duration: 1.2s; // 调整旋转速度
}
// 可选:修改背景遮罩
&.el-loading-mask {
background-color: rgba(0, 0, 0, 0.75);
}
}
</style>
3.3 样式作用域控制
为确保样式只影响当前组件,可以使用以下两种方式:
- Scoped CSS:
html复制<style lang="scss" scoped>
/* 样式自动添加data-v属性选择器 */
</style>
- CSS Modules:
html复制<style lang="scss" module>
/* 生成唯一类名 */
</style>
提示:使用scoped时,如需深度选择器,可以使用
::v-deep或/deep/(取决于Vue版本)
4. 高级定制技巧
4.1 动态主题切换
通过计算属性动态生成样式类:
javascript复制computed: {
loadingTheme() {
return this.darkMode ? 'dark-loading' : 'light-loading'
}
}
然后在样式中定义两套主题:
scss复制.dark-loading {
.el-loading-text {
color: #e0e0e0 !important;
}
/* 其他暗色样式... */
}
.light-loading {
.el-loading-text {
color: #333 !important;
}
/* 其他亮色样式... */
}
4.2 自定义旋转动画
替换默认的旋转图标:
scss复制.custom-loading-class {
.el-loading-spinner {
i {
display: none; // 隐藏默认图标
}
&::before {
content: '';
display: inline-block;
width: 24px;
height: 24px;
background: url('@/assets/custom-spinner.svg') no-repeat;
animation: custom-spin 1.5s linear infinite;
}
}
}
@keyframes custom-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
4.3 响应式尺寸调整
根据屏幕尺寸调整loading大小:
scss复制.custom-loading-class {
.el-loading-spinner i {
font-size: 16px;
@media (min-width: 768px) {
font-size: 20px;
}
@media (min-width: 992px) {
font-size: 24px;
}
}
.el-loading-text {
font-size: 14px;
@media (min-width: 768px) {
font-size: 16px;
}
}
}
5. 常见问题与解决方案
5.1 样式不生效排查清单
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 样式完全没效果 | 选择器优先级不够 | 检查是否需要用!important或更具体的选择器 |
| 部分样式生效 | scoped样式限制 | 使用::v-deep深度选择器 |
| 文字样式生效但图标没变化 | 图标使用了字体颜色 | 确保同时设置了color和font-size |
| 生产环境样式丢失 | 样式被tree-shaking移除 | 检查webpack配置的sideEffects |
5.2 性能优化建议
- 避免频繁创建/销毁:
javascript复制// 不好的做法
methods: {
submitForm() {
const loading = this.$loading()
await apiCall()
loading.close()
}
}
// 推荐做法
data() {
return {
loadingInstance: null
}
},
methods: {
async submitForm() {
if (!this.loadingInstance) {
this.loadingInstance = this.$loading()
}
await apiCall()
this.loadingInstance.close()
this.loadingInstance = null
}
}
- 防抖处理:
javascript复制import { debounce } from 'lodash'
export default {
methods: {
showLoading: debounce(function() {
this.loadingInstance = this.$loading()
}, 300),
hideLoading() {
this.loadingInstance?.close()
}
}
}
5.3 多实例管理
当需要同时管理多个loading实例时:
javascript复制export default {
data() {
return {
loadingInstances: new Map()
}
},
methods: {
showLoading(key, options) {
if (this.loadingInstances.has(key)) {
return
}
const instance = this.$loading(options)
this.loadingInstances.set(key, instance)
},
hideLoading(key) {
if (this.loadingInstances.has(key)) {
this.loadingInstances.get(key).close()
this.loadingInstances.delete(key)
}
},
hideAllLoading() {
this.loadingInstances.forEach(instance => instance.close())
this.loadingInstances.clear()
}
}
}
6. 最佳实践总结
-
样式隔离原则:
- 始终通过customClass添加自定义类名
- 避免直接修改.el-loading-*全局样式
- 使用scoped或CSS Modules限制作用域
-
设计一致性:
- 文字颜色应与背景有足够对比度(建议≥4.5:1)
- 图标大小与文字大小保持视觉平衡
- 动画速度保持在0.8-1.5秒/圈之间
-
性能考量:
- 复用loading实例而非频繁创建
- 复杂动画考虑使用硬件加速
- 移动端注意控制遮罩层的层级(z-index)
-
可访问性增强:
javascript复制this.$loading({ text: '正在加载,请稍候...', ariaLive: 'assertive', ariaAtomic: 'true' })
在实际项目中,我通常会创建一个loadingMixin.js,封装常用的loading操作和样式,然后在各个组件中混入使用。这样可以确保整个项目的loading体验一致,也便于后期统一调整。