6.6 Element UI 加载指示器
Element UI 提供了两种主要的加载指示器:
Loading
服务 (Loading Service): 全屏或针对某个 DOM 元素的遮罩层加载。- 组件的
loading
属性: 直接在按钮 (<el-button>
) 等组件上设置loading
属性。
方法一:使用 Loading
服务 (推荐用于全屏或区域加载)
Loading.service(options)
可以创建一个全屏或针对特定元素的加载遮罩。
1. 基本用法 (全屏)
<template><div><el-button type="primary" @click="fetchData">获取数据</el-button><!-- 其他内容 --></div>
</template><script>
import axios from 'axios';
// 引入 Element UI 的 Loading 服务
import { Loading } from 'element-ui';export default {methods: {async fetchData() {let loadingInstance = null;try {// 1. 显示加载loadingInstance = Loading.service({fullscreen: true, // 全屏text: '拼命加载中...', // 加载文本spinner: 'el-icon-loading', // 加载图标background: 'rgba(0, 0, 0, 0.7)' // 遮罩背景色});// 2. 发起 axios 请求const response = await axios.get('/api/some-data');console.log('数据:', response.data);// 3. 请求成功后,可以显示成功消息this.$message.success('数据获取成功!');} catch (error) {// 4. 请求失败,显示错误消息this.$message.error('数据获取失败: ' + (error.message || '未知错误'));} finally {// 5. 无论成功或失败,都必须关闭加载// 使用 $nextTick 确保 DOM 更新后再关闭,避免闪烁this.$nextTick(() => {if (loadingInstance) {loadingInstance.close();}});}}}
}
</script>
2. 针对特定元素的加载 (区域加载)
<template><!-- 给需要加载遮罩的容器添加一个 ref --><div ref="loadingTarget" style="height: 300px; border: 1px solid #ddd; padding: 10px;"><h3>数据列表</h3><el-table :data="tableData" style="width: 100%"><el-table-column prop="name" label="姓名"></el-table-column><el-table-column prop="age" label="年龄"></el-table-column></el-table></div><el-button type="primary" @click="loadTableData">刷新表格</el-button>
</template><script>
import axios from 'axios';
import { Loading } from 'element-ui';export default {data() {return {tableData: []}},methods: {async loadTableData() {let loadingInstance = null;try {// 1. 显示加载,目标是 ref 为 loadingTarget 的元素loadingInstance = Loading.service({target: this.$refs.loadingTarget, // 指定目标 DOM 元素text: '加载中...',spinner: 'el-icon-loading'// fullscreen 为 false 或不设置});// 2. 发起请求const response = await axios.get('/api/table-data');this.tableData = response.data; // 更新数据} catch (error) {this.$message.error('加载表格数据失败');} finally {// 3. 关闭加载this.$nextTick(() => {if (loadingInstance) {loadingInstance.close();}});}}}
}
</script>
3. 封装成可复用的函数
为了减少重复代码,可以将加载逻辑封装起来:
// utils/loading.js
import { Loading } from 'element-ui';// 封装一个带加载的请求函数
export async function requestWithLoading(requestPromise, loadingOptions = {}) {let loadingInstance = null;try {// 合并默认选项const options = {fullscreen: true,text: '加载中...',spinner: 'el-icon-loading',background: 'rgba(0, 0, 0, 0.7)',...loadingOptions};loadingInstance = Loading.service(options);const result = await requestPromise;return result;} catch (error) {// 这里可以根据需要处理错误,或者让调用者处理throw error;} finally {// 确保加载被关闭if (loadingInstance) {// 使用 setTimeout 确保关闭发生在下一个事件循环,避免与组件更新冲突setTimeout(() => {loadingInstance.close();}, 0);}}
}// 在组件中使用
// import { requestWithLoading } from '@/utils/loading';
// import axios from 'axios';// async fetchData() {
// try {
// const response = await requestWithLoading(
// axios.get('/api/data'),
// { text: '正在查询...', target: this.$refs.someElement } // 可选的自定义 loading 选项
// );
// console.log(response.data);
// } catch (error) {
// this.$message.error('请求失败');
// }
// }
方法二:使用组件的 loading
属性 (推荐用于按钮)
当点击按钮发起请求时,直接让按钮进入加载状态是最直观的方式。
<template><div><!-- loading 属性绑定到 data 中的变量 --><el-button type="primary" :loading="loading" @click="handleSubmit">{{ loading ? '提交中...' : '提交' }}</el-button><!-- 对话框中的按钮 --><el-dialog title="编辑" :visible.sync="dialogVisible"><el-form :model="form"><el-form-item label="名称"><el-input v-model="form.name"></el-input></el-form-item></el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取 消</el-button><el-button type="primary" :loading="dialogLoading" @click="handleDialogSubmit">{{ dialogLoading ? '保存中...' : '确 定' }}</el-button></span></el-dialog></div>
</template><script>
import axios from 'axios';export default {data() {return {loading: false, // 控制主按钮加载状态dialogVisible: false,dialogLoading: false, // 控制对话框内按钮加载状态form: { name: '' }}},methods: {async handleSubmit() {// 1. 设置加载状态this.loading = true;try {// 2. 发起请求await axios.post('/api/submit', this.form);this.$message.success('提交成功!');} catch (error) {this.$message.error('提交失败');} finally {// 3. 无论成功失败,关闭加载this.loading = false;}},async handleDialogSubmit() {this.dialogLoading = true;try {await axios.put(`/api/users/${this.form.id}`, this.form);this.$message.success('保存成功!');this.dialogVisible = false; // 保存成功后关闭对话框} catch (error) {this.$message.error('保存失败');} finally {this.dialogLoading = false;}}}
}
</script>
方法三:结合 axios
拦截器 (全局加载)
如果希望在所有 axios
请求时都显示一个全局加载(比如在页面顶部显示一个进度条或在某个固定区域显示加载),可以使用 axios
的拦截器。
// utils/request.js 或 main.js 中
import axios from 'axios';
import { Loading } from 'element-ui';let loadingInstance = null;
let requestCount = 0; // 请求计数器,防止多个请求时 loading 被过早关闭// 请求拦截器
axios.interceptors.request.use(config => {// 如果是第一个请求,显示加载if (requestCount === 0) {loadingInstance = Loading.service({fullscreen: true,text: '加载中...',spinner: 'el-icon-loading'});}requestCount++;return config;},error => {return Promise.reject(error);}
);// 响应拦截器
axios.interceptors.response.use(response => {requestCount--;// 所有请求都完成时,关闭加载if (requestCount === 0 && loadingInstance) {// 使用 $nextTick 或 setTimeout 确保 DOM 更新setTimeout(() => {loadingInstance.close();loadingInstance = null;}, 0);}return response;},error => {requestCount--;if (requestCount === 0 && loadingInstance) {setTimeout(() => {loadingInstance.close();loadingInstance = null;}, 0);}return Promise.reject(error);}
);// 导出配置好的 axios 实例
export default axios;
注意:
- 这种方式会影响所有使用这个
axios
实例的请求。 - 使用
requestCount
计数器是为了处理并发请求。如果第一个请求很快完成而第二个还没完成,不应该关闭加载。 - 全局拦截器可能不适合所有场景(比如某些请求不需要显示加载),可以在
config
中添加一个自定义标志(如config.showLoading = false
)来控制。
总结与最佳实践
- 选择合适的方法:
- 按钮点击: 优先使用按钮的
loading
属性,用户体验最直接。 - 页面/区域刷新: 使用
Loading
服务针对特定元素 (target
)。 - 全站通用加载: 考虑使用
axios
拦截器 +Loading
服务(注意并发和可配置性)。
- 按钮点击: 优先使用按钮的
try...catch...finally
: 务必在finally
块中关闭加载,确保无论请求成功或失败,加载状态都能被正确清除。$nextTick
或setTimeout
: 在关闭Loading
服务时,使用this.$nextTick
或setTimeout(..., 0)
可以避免因 Vue 的异步更新机制导致的加载状态未及时关闭或闪烁的问题。- 用户体验: 设置有意义的加载文本 (
text
),使用合适的加载图标 (spinner
)。 - 错误处理: 在
catch
块中处理错误,并给出用户友好的提示(如this.$message.error()
)。
通过合理运用这些方法,你可以轻松地在 axios
请求时为用户提供清晰的加载反馈,显著提升应用的交互体验。