☰
Flask+ECharts数据可视化大屏:多页面切换与实时渲染实战
2026/9/26 20:27:42 网站建设 项目流程

简介:这是一套基于Python与Flask框架构建的轻量级数据可视化大屏展示系统,面向企业数据监控、业务分析场景,也适合作为前端工程化实战练手项目。系统支持多页面切换与实时数据渲染,内置数据看板、空气质量监测、计算机性能指标等模块,可满足不同层级用户的展示需求。压缩包共24个文件,约1.02MB,包含6个png与1个jpg图片、4个py后端脚本、3个js与1个css前端资源、2个html页面、1个ttf字体、1个gif动图,以及说明文档、README、LICENSE等,目录结构清晰,便于按模块查阅。目前已有103人学习下载。读者可从中获取完整的Flask后端与前端页面代码、图表渲染逻辑、实时数据推送思路,以及附赠的前端工程化配置与样式库资源,快速搭建并定制企业级可视化大屏。

1. 从一份 Flask 大屏源码说起:多页面切换与实时渲染到底怎么落地

很多做企业数据监控的团队都遇到过这种尴尬:前端用 ECharts 拼了几张图,页面一刷新数据就断,多页面之间跳转还得重新拉一遍接口,最后交付时只能截图糊弄。这份基于 Python 和 Flask 的轻量级数据可视化大屏系统,解决的正是这个场景——它把数据看板、空气质量监测、计算机性能指标拆成独立模块,用 Flask 做后端路由与数据接口,前端负责多页面切换和实时数据渲染,整套代码可以直接本地跑起来,也能部署到内网服务器做业务分析。它适合两类人:一类是想找一个能改、能扩的数据可视化大屏底座的前端或全栈工程师,另一类是需要快速搭出企业数据监控原型、又不想从零写 WebSocket 和定时器的开发者。下面我按「资源是什么、怎么跑、坑在哪」的顺序,把这份源码拆开讲一遍。

2. Flask 后端与多页面路由:接口怎么设计才不返工

2.1 为什么用 Flask 而不是直接静态 HTML

数据可视化大屏如果只做静态展示,纯 HTML 加 ECharts 就够了。但这份资源的核心价值在于「实时数据渲染」和「多页面切换」,这两件事都绕不开后端。Flask 在这里承担三个角色:一是提供页面路由,让数据看板、空气质量监测、计算机性能指标各自有独立 URL;二是提供 JSON 数据接口,前端定时或按需拉取;三是作为后续接入真实数据源的入口,比如把模拟数据换成数据库查询或第三方 API。

常见做法是每个模块一个蓝图(Blueprint),这样路由不打架,后期加模块也不用动主文件。我一般会把数据接口统一放在/api/前缀下,页面路由放在根路径,前端通过fetch或axios调用。这样做的好处是,当你把模拟数据换成真实数据时,只需要改接口内部实现,前端一行都不用动。

2.2 多页面切换的路由组织方式

多页面切换有两种实现路径:一种是 Flask 渲染多个模板,每个模板一个独立页面,跳转时整页刷新;另一种是单页应用(SPA)思路,Flask 只提供一个入口模板,前端用 JavaScript 控制视图切换。这份资源更偏向第一种,因为它的模块划分清晰,每个模块的图表配置差异较大,独立页面反而更好维护。

下面是一个典型的 Flask 路由组织方式,我按这份资源的模块结构做了简化:

# app.py from flask import Flask, render_template, jsonify import psutil import random from datetime import datetime app = Flask(__name__) # 页面路由:三个模块各自独立 @app.route('/') def index(): return render_template('dashboard.html') # 数据看板 @app.route('/air') def air(): return render_template('air_quality.html') # 空气质量监测 @app.route('/performance') def performance(): return render_template('performance.html') # 计算机性能指标 # 数据接口:统一 /api/ 前缀 @app.route('/api/dashboard') def api_dashboard(): # 模拟业务数据,实际项目替换为数据库查询 return jsonify({ 'sales': [120, 200, 150, 80, 70, 110], 'visits': [300, 420, 380, 500, 460, 390], 'timestamp': datetime.now().strftime('%H:%M:%S') }) @app.route('/api/air') def api_air(): # 模拟空气质量数据 return jsonify({ 'pm25': random.randint(10, 80), 'pm10': random.randint(20, 120), 'co2': random.randint(400, 900), 'timestamp': datetime.now().strftime('%H:%M:%S') }) @app.route('/api/performance') def api_performance(): # 用 psutil 读取真实计算机性能指标 return jsonify({ 'cpu': psutil.cpu_percent(interval=0.5), 'memory': psutil.virtual_memory().percent, 'disk': psutil.disk_usage('/').percent, 'timestamp': datetime.now().strftime('%H:%M:%S') }) if __name__ == '__main__': app.run(debug=True, host='0.0.0.0', port=5000)

这段代码的逻辑很直白:页面路由返回 HTML 模板,数据接口返回 JSON。psutil是 Python 的系统信息库,cpu_percent(interval=0.5)表示采样 0.5 秒内的 CPU 使用率,这个参数不能设太小,否则第一次调用会返回 0.0,这是新手最容易翻车的地方。host='0.0.0.0'让服务监听所有网卡,方便内网其他机器访问;如果只想本机调试,改成127.0.0.1即可。debug=True仅用于开发,生产环境必须关掉,否则会暴露调试器。

2.3 前端如何消费接口并做实时刷新

前端部分的核心是「定时拉取 + 图表更新」。ECharts 实例创建后,通过setOption更新数据,而不是销毁重建。下面是一个数据看板页面的简化实现:

// static/js/dashboard.js // 初始化 ECharts 实例 const salesChart = echarts.init(document.getElementById('sales-chart')); const visitsChart = echarts.init(document.getElementById('visits-chart')); // 首次渲染 function fetchDashboard() { fetch('/api/dashboard') .then(res => res.json()) .then(data => { salesChart.setOption({ xAxis: { data: ['周一', '周二', '周三', '周四', '周五', '周六'] }, series: [{ data: data.sales }] }); visitsChart.setOption({ xAxis: { data: ['周一', '周二', '周三', '周四', '周五', '周六'] }, series: [{ data: data.visits }] }); // 更新时间戳显示 document.getElementById('update-time').innerText = data.timestamp; }) .catch(err => console.error('数据拉取失败:', err)); } // 每 3 秒刷新一次 fetchDashboard(); setInterval(fetchDashboard, 3000); // 窗口大小变化时重绘,避免图表变形 window.addEventListener('resize', () => { salesChart.resize(); visitsChart.resize(); });

这里有几个参数值得注意:setInterval的 3000 毫秒是刷新间隔,实际项目要根据数据更新频率调整,太短会给后端压力,太长实时性不够。resize()必须绑定,否则浏览器窗口缩放后图表会错位,这是大屏项目的高频问题。fetch的.catch不能省,接口挂了至少能在控制台看到原因,而不是页面一片空白。

3. 实时数据渲染的三种方案:轮询、SSE 和 WebSocket 怎么选

3.1 轮询方案的适用边界

这份资源默认用的是轮询,也就是前端定时调接口。轮询的优点是实现简单、兼容性好、后端不需要维护长连接;缺点是实时性受间隔限制,且会产生大量无效请求。对于计算机性能指标这种每秒都在变的数据,3 秒轮询其实已经够用;但对于空气质量监测这种分钟级变化的数据,轮询反而浪费资源。

我一般会按数据变化频率来选:变化慢的用轮询,变化快的用 SSE 或 WebSocket。轮询的另一个坑是「请求堆积」——如果接口响应时间超过轮询间隔,会出现请求叠加,页面数据乱序。解决办法是用setTimeout递归代替setInterval,确保上一次请求完成后再发起下一次:

// 用递归 setTimeout 避免请求堆积 function pollDashboard() { fetch('/api/dashboard') .then(res => res.json()) .then(data => { // 更新图表逻辑同上 updateCharts(data); }) .catch(err => console.error(err)) .finally(() => { setTimeout(pollDashboard, 3000); // 上一次完成后才排下一次 }); } pollDashboard();

这个改动的关键在finally:无论请求成功还是失败,都在完成后才安排下一次,这样就不会出现多个请求同时在飞的情况。

3.2 SSE 做单向实时推送

如果数据只需要服务端推给前端,SSE(Server-Sent Events)比 WebSocket 更轻。Flask 实现 SSE 需要返回一个生成器,并设置正确的 MIME 类型:

# SSE 接口示例 from flask import Response import time, json, random @app.route('/api/stream/performance') def stream_performance(): def generate(): while True: data = { 'cpu': psutil.cpu_percent(interval=1), 'memory': psutil.virtual_memory().percent, 'timestamp': time.strftime('%H:%M:%S') } # SSE 格式:data: 内容\n\n yield f"data: {json.dumps(data)}\n\n" time.sleep(2) return Response(generate(), mimetype='text/event-stream')

前端用EventSource接收:

const source = new EventSource('/api/stream/performance'); source.onmessage = function(event) { const data = JSON.parse(event.data); // 更新性能指标图表 updatePerformanceChart(data); }; source.onerror = function() { console.error('SSE 连接断开,浏览器会自动重连'); };

SSE 的坑在于:Flask 开发服务器默认是单线程的,一个 SSE 连接就会占住一个 worker,多个页面同时打开会卡死。解决办法是用threaded=True启动,或者上生产级 WSGI 服务器。另外,SSE 不支持跨域携带自定义头,如果前后端分离部署,需要在 Flask 侧加 CORS 头。

3.3 WebSocket 与 Flask-SocketIO 的取舍

WebSocket 是全双工,适合需要前端向后端发指令的场景,比如切换数据源、调整刷新频率。这份资源没有强制用 WebSocket,但如果要扩展成「可交互大屏」,Flask-SocketIO 是常见选择。它的坑在于版本兼容:Flask-SocketIO 对 Flask 版本和前端 socket.io 客户端版本都有要求,版本对不上会一直报 400 错误。我一般会锁定版本,比如 Flask-SocketIO 5.x 配 socket.io 4.x,不要混用。

三种方案的对比:

方案实时性实现复杂度后端压力适用场景
轮询取决于间隔低中数据变化慢、兼容性要求高
SSE高中低单向推送、性能指标流
WebSocket最高高低双向交互、多客户端同步

选型建议:如果只是展示,轮询和 SSE 足够;如果要交互,再上 WebSocket。不要为了技术而技术,大屏项目稳定比炫技重要。

4. 避坑与排查:这份源码跑不起来时先看这几条

4.1 端口被占用导致启动失败

现象:运行python app.py后报OSError: [Errno 98] Address already in use。原因是 5000 端口被其他进程占用,常见于之前启动的 Flask 实例没关干净,或者系统里有其他服务在用。解决办法:先查占用进程,Linux 用lsof -i:5000,Windows 用netstat -ano | findstr :5000,找到 PID 后杀掉;或者直接改端口,app.run(port=5001)。我一般会在开发时固定用一个不常用的端口,避免和系统服务冲突。

4.2 psutil 读取指标返回 0 或报权限错误

现象:计算机性能指标页面 CPU 一直是 0,或者内存读取报AccessDenied。原因是psutil.cpu_percent()第一次调用如果没有传interval,会返回 0.0,因为它需要两次采样才能计算差值。解决办法:要么传interval=0.5,要么在程序启动时先调一次「预热」。权限问题多出现在 Linux 上读取磁盘或网络信息时,需要用有权限的用户运行,或者只读取当前用户可访问的指标。

4.3 前端图表不显示或显示错位

现象:页面打开了,但 ECharts 图表区域一片空白,或者图表挤在左上角。原因通常是容器没有设置宽高。ECharts 初始化时需要容器有明确的width和height,如果父元素是display: none或者高度为 0,图表就渲染不出来。解决办法:给图表容器设固定高度,比如style="height: 400px;",或者在切换页面后手动调用chart.resize()。另一个常见原因是 JS 文件路径不对,Flask 的静态文件默认在static/目录,模板里要用url_for('static', filename='js/dashboard.js')引用,不要写死相对路径。

4.4 多页面切换后定时器重复创建

现象:在数据看板和空气质量页面之间来回切换几次后,接口请求频率越来越高,浏览器越来越卡。原因是每个页面加载时都执行了setInterval,但页面跳转时没有清除旧的定时器。如果是整页刷新,旧页面的定时器会随页面销毁;但如果是 SPA 式切换,定时器会一直留在内存里。解决办法:在页面卸载或视图切换时调用clearInterval,或者把定时器统一管理,切换时先清后建。我一般会在beforeunload事件里做清理,虽然整页刷新时浏览器会自动回收,但养成习惯能避免很多玄学问题。

4.5 部署到服务器后接口 404 或静态资源加载失败

现象:本地跑得好好的,放到服务器上页面能打开但图表没数据,控制台报 404。原因通常是反向代理配置问题,比如 Nginx 把/api/路径转发到了错误的后端端口,或者 Flask 的APPLICATION_ROOT没设置对。解决办法:先确认 Flask 服务本身能通过curl http://127.0.0.1:5000/api/dashboard返回数据,再检查 Nginx 的location配置。静态资源 404 多半是url_for生成的路径带了前缀,而 Nginx 没做对应转发。这类问题没有后悔药,只能一层层排查,从后端到代理到浏览器控制台。

5. 进阶技巧:把模拟数据换成真实数据源并做接口缓存

5.1 接入真实数据源的两种方式

这份资源的默认数据是模拟的,实际项目里数据来源无非两类:数据库和外部 API。数据库场景下,我一般用 SQLAlchemy 做 ORM,把查询结果转成 JSON 返回。外部 API 场景下,用requests拉取后做字段映射。无论哪种,都建议在接口层加一层缓存,避免每次请求都打数据库或外部服务。

# 带缓存的接口示例 from flask import jsonify import requests import time _cache = {'data': None, 'expire': 0} @app.route('/api/air/real') def api_air_real(): now = time.time() # 缓存 30 秒 if _cache['data'] and now < _cache['expire']: return jsonify(_cache['data']) try: # 假设从某个环境监测 API 拉取 resp = requests.get('https://api.example.com/air', timeout=5) data = resp.json() _cache['data'] = data _cache['expire'] = now + 30 return jsonify(data) except requests.RequestException as e: # 外部接口挂了,返回缓存旧数据或默认值 if _cache['data']: return jsonify(_cache['data']) return jsonify({'error': str(e)}), 503

这段代码的关键是「缓存 + 降级」:30 秒内重复请求直接返回缓存,外部接口超时或报错时返回上一次的数据,而不是让页面空白。timeout=5必须设,否则外部接口卡住会拖死整个 Flask 进程。缓存时间根据数据更新频率调整,空气质量这类数据 30 秒到 1 分钟都合理。

5.2 用 Flask-Caching 做更规范的缓存

手写字典缓存适合小项目,模块多了会乱。Flask-Caching 是更规范的选择,支持内存、Redis 等多种后端:

from flask_caching import Cache cache = Cache(app, config={'CACHE_TYPE': 'simple', 'CACHE_DEFAULT_TIMEOUT': 30}) @app.route('/api/dashboard/cached') @cache.cached(timeout=30) def api_dashboard_cached(): # 耗时查询逻辑 return jsonify(query_database())

CACHE_TYPE='simple'是内存缓存,适合单进程开发;生产环境多 worker 时要用 Redis,否则每个 worker 各存一份,缓存命中率上不去。timeout参数和手写方案里的过期时间是一个意思,按数据特性设。

5.3 验证实时渲染是否真的生效

改完代码后怎么确认实时渲染在工作?我一般用三步验证:第一步,打开浏览器开发者工具的 Network 面板,看接口是不是按设定间隔在请求;第二步,手动改一下后端返回的数据(比如把 CPU 值写死成 99),看页面是否在下一个刷新周期更新;第三步,用curl直接调接口,确认返回的 JSON 结构和前端解析逻辑对得上。这三步走完,基本能排除「看起来在动,其实是静态图」的假象。

从那以后我每次拿到一份大屏源码,都强制先跑通「接口返回 → 前端渲染 → 定时刷新」这条链路,再去看样式和布局。因为样式可以慢慢调,数据链路断了,整个项目就是一张会动的壁纸。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询