拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于SpringBoot+Vue+MyBatis的医院网站前后端分离实战详解

基于SpringBoot+Vue+MyBatis的医院网站前后端分离实战详解

前后端分离、SpringBoot、Vue、MyBatis、MySQL,这几个关键词组合在一起,就是当前中小型医院网站项目里最实用的一套技术栈。我最近把一个医院门户网站完整跑通,从数据库建模、接口开发、Vue页面搭建到服务器部署,整个过程踩了不少坑,也沉淀出一套能直接复用的方案。这篇文章按实际开发顺序,把项目拆分、表设计、核心接口、前后端联调、部署流程和常见问题完整复盘一遍。无论你是准备做毕业设计,还是刚入职想独立负责一个JavaWeb项目,这篇内容都能帮你少走弯路。

1. 项目整体设计与技术选型

1.1 项目定位:中小型医院需要什么样的网站系统

中小型医院需要的是“对外信息门户加基础预约服务”,而不是大型HIS。它要解决的核心痛点是患者能查科室、查医生、看排班、提交在线预约,管理员能维护内容。我在立项时没有盲目堆功能,而是把主流程收敛为:医院介绍、科室导航、专家团队、排班预约、新闻公告、留言反馈。主要页面和接口都围绕这几条线展开。

技术选型最终锁定SpringBoot加Vue加MyBatis加MySQL,原因很直接:Java后端生态成熟,Vue做单页应用体验好,MyBatis对复杂查询可控,MySQL完全能覆盖中小型医院的数据规模。这套组合在中小团队里也比较好招人、好维护。真正做下来,我发现它最大的优势是职责清晰:前端只负责页面和交互,后端只负责提供接口和业务校验,数据库只负责存储。排查问题时,顺着“页面→接口→SQL”这条线走,很快就能定位到问题在哪一层。

1.2 为什么不用Thymeleaf或若依这类脚手架

有人会问,SpringBoot直接用Thymeleaf写页面不是更简单?确实,不分离的模式开发门槛低,部署一个包就能跑。但真正遇到医院网站这种需要频繁调整首页样式、增加交互组件、未来可能再做小程序端或App端的场景,Thymeleaf就会越改越累。前端页面每动一处,都得重新编译重启后端,接口联调效率也很低。Vue负责前端路由和组件化之后,页面改动不再影响后端服务,两边并行开发互不阻塞,这是前后端分离最大的价值。

我没用若依这类前后端分离脚手架,不是说它不好,而是它的代码生成器和内置权限模块对新手理解核心链路有干扰。写这个项目时,我更希望让大家看清SpringBoot和Vue之间一条干净完整的通路,所以去掉了一切多余的东西。如果你已经熟练掌握了这套核心链路,再去用若依提升开发效率不迟;但如果一开始就停留在脚手架生成代码,很多底层问题反而会被掩盖,遇到版本冲突或部署问题无从下手。

1.3 适合谁参考

这个项目适合三类人。第一类是正在选毕业设计题目的学生,前后端分离、SpringBoot、Vue、MyBatis、MySQL,这个技术组合本身就是高频评分点,做出来的系统也容易演示;第二类是初级Java开发,想完整经历一遍从建表到上线的流程,补上部署运维层面的经验;第三类是医院信息科或集成商,需要快速搭建一套门户网站给领导演示或内部试用。

需要说明的是,这个项目不涉及微服务、高并发、大数据,它更强调业务闭环和工程规范。如果你想学分布式架构,它不是合适的起点。但如果你想把一个中小型业务系统的“增删改查、登录鉴权、预约事务、部署发布”全链路吃透,这套源码和教程的匹配度很高。

2. 系统功能与数据库设计

2.1 前台页面按患者动线拆解

前台页面我按患者访问动线来拆。首页是门面,放了轮播图、医院简介、重点科室导航、最新公告和出诊动态;科室列表支持按分类筛选,进入科室详情页能看到科室介绍、擅长领域和医生列表;专家团队页展示医生头像、职称、擅长方向和出诊安排;预约挂号页是核心,用户先选科室,再选医生,再选日期和时段,然后填写就诊人姓名、身份证号、手机号并提交,系统返回一个预约编号。

新闻公告页展示医院新闻和健康宣教内容;最后是一个留言反馈入口,方便患者提交建议。这些前台页面和后台管理共用一套登录用户体系,只是在权限上做区分。这一点很多医院网站容易忽略,前后台各自维护一套账号,导致患者登录状态和管理员登录状态互相冲突。共用一套用户表,用role字段区分角色,逻辑最简单,也最符合中小型医院的实际使用习惯。

2.2 后台管理模块拆解

后台管理按管理员工作习惯分模块。首页仪表盘统计今日预约量、新增留言数、公告数量,让管理员一眼看到核心数据;科室管理支持增删改查,上传科室图片并设置排序;医生管理录入医生姓名、职称、头像、擅长方向、简介,同时关联所在科室;排班管理非常关键,可以按周设置每个医生的出诊时段,设置号源总数;预约管理查看所有预约,支持确认、取消操作,只有状态为“已确认”的预约才能进入线下就诊流程。

公告管理发布新闻公告,设置发布时间、发布状态和置顶;留言管理负责回复患者留言。权限上,后台模块需要管理员角色,普通患者角色只能访问前台接口。后台功能不用做得很花哨,但流程必须完整,尤其是预约状态流转。我在项目里把预约状态定义为待确认、已确认、已完成、已取消,后台任何操作都不允许跳状态,比如已完成不能直接取消,已取消不能改成已确认。这样哪怕操作失误,数据也不会乱。

2.3 核心表设计与字段说明

数据库是整套系统的地基,我总共建了八张核心表:用户表、科室表、医生表、排班表、预约表、公告表、轮播图表、留言表。用户表存账号、密码、姓名、角色、手机号;科室表存科室名称、简介、图片、排序;医生表存姓名、职称、头像、擅长、简介和科室外键;排班表存医生外键、星期、时段、号源总数、已约数量;预约表存预约编号、用户外键、排班外键、就诊人信息、预约日期时段、状态。

下面是一个带注释的医生表建表SQL,其他表结构类似,核心是我统一了字段命名风格:主键id、外键xxx_id、时间字段create_time或update_time、状态字段status。

CREATE TABLE `doctor` ( `id` int NOT NULL AUTO_INCREMENT, `department_id` int NOT NULL COMMENT '所属科室', `name` varchar(50) NOT NULL COMMENT '姓名', `title` varchar(30) DEFAULT NULL COMMENT '职称', `avatar` varchar(255) DEFAULT NULL COMMENT '头像地址', `good_at` varchar(500) DEFAULT NULL COMMENT '擅长领域', `introduce` text COMMENT '个人简介', `sort_order` int DEFAULT 0 COMMENT '排序', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_dept` (`department_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='医生表';

所有表的字符集统一使用utf8mb4,时间字段统一用datetime,状态字段统一用tinyint。这样可以避免中文乱码和状态判断混乱。很多新人喜欢在表里直接存中文状态,比如“已确认”“已取消”,看起来直观,但统计和接口判断都很痛苦,改状态时要精确匹配字符串,一旦有人多打了个空格就查不到数据。用数字状态配合代码里的枚举常量,才是稳妥做法。

2.4 关联关系与索引设计要点

表关系上,科室和医生是一对多,医生和排班是一对多,预约既关联排班也关联用户。设计时注意不要冗余科室名称到医生表,否则后续改科室名称要连带改医生数据。预约表上我建了两类索引:一是排班ID加预约日期的联合索引,用来支撑时段号源查询和防止重复预约;二是预约编号的唯一索引,方便用户凭编号查询订单。

排班表上建了医生ID和星期几的联合索引,支撑后台按医生排班查询。公告表的发布时间索引也很重要,列表页默认按发布时间倒序,没有索引时数据量到几万条就会开始变慢。虽然这些表数据量不大,但规范索引能让系统在数据逐步增长时保持稳定。索引不是越多越好,每张表保持在两到三个核心索引就够,太多反而会拖慢插入和更新速度。

3. SpringBoot后端实现与关键细节

3.1 Maven工程结构和数据源配置

后端工程用Maven搭建标准SpringBoot结构,分为controller、service、mapper、entity、config、common几个包。所有Mapper接口和XML文件在mapper目录下,启动类加@MapperScan扫描。pom.xml里核心依赖是spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-j、lombok。

这里必须先说一个最容易踩的坑:SpringBoot版本和MyBatis启动器版本必须匹配。SpringBoot 2.7.x配mybatis-spring-boot-starter 2.x,SpringBoot 3.x必须用3.x的starter,同时javax包要换成jakarta。我见过很多人启动时报“Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required”,十有八九就是版本错位。数据源配置用application.yml,url里必须带serverTimezone、characterEncoding和useSSL=false,否则容易报时区或SSL错误。

server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/hospital_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.hospital.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

MyBatis的map-underscore-to-camel-case一定要开,数据库的下划线字段能自动映射到Java驼峰属性,省掉大量ResultMap配置。不过连表查询出现字段名冲突时,还是要显式写ResultMap,别偷懒。Mapper XML文件要确保能被打包进classpath,如果pom里没有配置resources,默认是可以打包的;但如果你在src/main/java下放了XML文件,就必须额外配置。

3.2 统一返回结果与全局异常处理

前后端分离开发时,前端需要明确知道请求成功还是失败。我定义了一个Result类,包含code、message、data。Controller层不直接返回裸对象或Entity,而是包装成Result返回。前端axios拦截器拿到响应后先判断code,code为200才处理数据,code为401跳转登录,code为500统一弹错误提示。这样页面代码非常干净,不会到处判断一堆状态码。

配合@RestControllerAdvice全局异常处理器,在Service里直接抛业务异常,由全局处理器转换成Result返回。写这个项目时,我一开始在Controller里try catch,到处重复,后来改成全局异常后,Controller代码变得非常简洁,只保留参数接收和调用Service。建议你也这么做,后期加参数校验、权限校验都会轻松很多。全局异常处理器里至少要处理三种异常:参数校验异常、业务异常、未知异常。未知异常不仅要返回统一错误信息,还应该把详细堆栈打印到日志,方便排查。

3.3 登录鉴权与接口权限控制

用户登录我使用JWT方案。密码用BCrypt加密存储,数据库里不保存明文密码。登录成功后,后端生成一个token返回给前端,前端存在localStorage里,之后每次请求在Authorization头里带上。后端用一个拦截器解析token,判断当前用户是否登录。如果是后台管理接口,还要额外判断role是否为管理员。

拦截器的放行名单需要仔细配置。登录接口、科室列表、医生列表、预约提交等公开接口要放行,后台管理接口全部拦截。这里有一个隐蔽坑:跨域预检请求是OPTIONS方法,如果不放行OPTIONS,前端请求会因为CORS失败。很多前后端分离项目联调时遇到“接口报403”但接口本身没问题,就是拦截器把OPTIONS请求也拦截了,我后来在拦截器里统一放行OPTIONS方法,问题才消失。

3.4 预约挂号业务的核心实现

预约挂号是核心,重点在防止号源超卖。实现思路是:前端提交科室、医生、日期、时段和就诊人信息后,后端先根据排班ID,用select ... for update锁住该排班记录,检查已约数量是否小于号源总数,如果够则插入预约记录并把已约数量加1,整个过程放在一个事务里。这里我用悲观锁没有用乐观锁,因为中小型医院同时间段预约并发量并不大,行锁足够,逻辑也最简单。

取消预约时,先校验预约状态必须是待确认或已确认,然后更新排班表的已约数量减1。这个状态校验很重要,如果不加,已完成的历史预约也可能被取消,号源会越减越乱。我在测试阶段就遇到过:测试数据里有一条已完成预约,点取消后号源数量加了回去,导致当天号源超过总数。后来加上状态限定,再也没出现过。

3.5 MyBatis动态SQL、缓存和TypeHandler

MyBatis部分要想用得好,动态SQL是必学技能。后台列表查询时,科室、职称、状态都可能为空,直接用<where>加<if>拼接条件,避免出现多余的AND。批量插入排班可以用<foreach>,一次插入一周的数据。持久层我在简单查询上使用注解,复杂统计和动态SQL用XML,配合map-underscore-to-camel-case自动转换下划线字段。

关于缓存,项目里我关闭了MyBatis二级缓存,原因是医院数据要求实时准确,二级缓存会让后台改完数据前台不能立刻看到,排查还麻烦。MyBatis默认开启一级缓存,SqlSession级别,这个基本够用,不用特意关。TypeHandler作为扩展点,可以用来自定义特殊类型转换,比如把List转成逗号分隔字符串、日期字符串和LocalDateTime互转,面试和实际开发都能加分。知道这几个场景,处理项目里的特殊字段类型就不会手足无措。

4. Vue前端实现与联调要点

4.1 项目搭建与路由设计

前端使用Vue 3加Vite搭建,目录划分成api、router、views、components、utils。Vite比Vue CLI启动快,配置简单,开发体验好。路由采用history模式,页面包括首页、科室列表、科室详情、医生列表、预约挂号、新闻列表、留言反馈和后台管理。后台管理需要登录后进入,通过路由守卫判断本地token是否存在,不存在则跳转登录页。

静态路由对这个项目就够用了,我不建议一开始就上动态路由,因为角色只有管理员和普通用户,强制用动态路由只会增加维护成本。等以后要扩展多个角色和菜单权限,再重构也不迟。页面UI用了Element Plus,组件复用度高,例如科室卡片、医生卡片、分页组件都在components目录里。组件拆分的原则是:只要在两个以上页面出现的东西,就抽成公共组件,不让代码重复。

4.2 axios封装与请求拦截

axios封装是前端联调的基础。我在utils/request.js里创建axios实例,设置baseURL为/api,超时时间10秒,请求拦截器从localStorage里取token放到Authorization头,响应拦截器统一处理Result对象。code为200时直接返回data,其余code抛异常给页面提示。这样前端业务代码不用关心token和响应结构,只需写具体请求函数。

例如预约提交接口是这样封装的:

export function submitAppointment(data) { return request({ url: '/appointment/submit', method: 'post', data: data }) }

页面里调用函数,拿到预约编号后跳转成功页。表单校验用Element Plus的rules,手机号和身份证号用正则校验,能在前端拦住很多无效提交,减少后端压力。特别是身份证号,如果只靠前端提示而不做幂等处理,用户重复点击提交会生成多条预约记录。我在提交按钮上加了loading状态,提交中禁用按钮,同时后端做防重复校验,双保险才靠谱。

4.3 跨域处理与代理配置

跨域问题在开发阶段通过Vite的proxy解决。devServer里把/api代理到http://localhost:8080,浏览器看到的请求是同源的,不会触发CORS。但在发布阶段,Nginx同样需要配置/api反向代理,如果直接用前后端分离地址而没有代理,跨域很难避免。

后端我还是配了CorsFilter作为兜底,但生产环境不要用*放行所有来源,尤其涉及患者实名信息和预约数据,来源白名单越严越好。还有一个细节:请求头里自定义了Authorization,后端跨域配置必须允许这个头,否则预检请求会失败,前端在Network里看到一堆OPTIONS请求报错。我查过几次这类问题,每次都发现是没有把Authorization头加入allowed headers导致的。

4.4 视频宣教与文件上传扩展

医院网站的“健康宣教”板块经常需要放视频。Vue播放m3u8视频并不难,引入video.js和videojs-contrib-hls插件,就能在浏览器里直接播放HLS流,不需要额外安装桌面播放器。视频文件建议用MinIO做对象存储,SpringBoot集成MinIO只需要引入依赖、创建客户端、封装上传下载方法,前端拿到返回的URL后直接给video标签。

如果只是为了演示,也可以把视频放到后端静态目录,通过Nginx映射访问。不管用哪种方案,都要注意Bucket的CORS配置,否则浏览器播放视频时跨域取不到分片,表现就是视频一直转圈或者只有声音没有画面。我在项目文档里专门把这一条写进去,后来部署时没有再踩坑。图片资源也是同样思路,不要塞进数据库,数据库只存URL,文件交给对象存储或静态目录。

5. 项目部署实操与避坑指南

5.1 本地环境准备:JDK、Maven、MySQL、Node

本地环境先准备好:JDK 1.8或11,Maven 3.6以上,MySQL 5.7或8.0,Node 14以上。MySQL安装是很多人第一道坎,Windows下安装时选择utf8mb4字符集,Linux下用RPM包安装后注意初始化密码和root远程访问配置。连接数据库时如果看到SSL连接相关报错,优先在JDBC URL里加上useSSL=false,测试环境可以这样快速绕过;生产环境如果安全要求高,再研究证书配置。

Node方面建议直接装Long Term Support版本,避免版本过高导致前端依赖安装失败。Maven配置国内镜像,能显著减少依赖下载时间;下载依赖报错时先看本地仓库里有没有损坏的.lastUpdated文件,把它删掉重新下载。很多Maven问题不是依赖真的下载不了,而是本地仓库残留了失败记录,Maven会认为这个依赖已经尝试过,不会重新下载。

5.2 后端打包启动脚本

后端部署时,我习惯在项目根目录执行mvn clean package -DskipTests,生成jar包后放到服务器某个固定目录。用nohup java -jar hospital-server.jar > app.log 2>&1 &启动,日志写到app.log。为了重启方便,我写了start.sh和stop.sh两个脚本,stop.sh用jps获取进程ID后kill,比直接找端口占用再kill省事。

生产环境的数据库密码不要在jar里写死,启动命令改用--spring.datasource.password=xxxx传参,或者放到环境变量里。如果服务器上8080端口被占,修改application.yml的server.port就行。还要记得防火墙或云安全组放行对应端口,否则服务起来外部照样访问不了。我踩过一次:服务已经在8080监听,但浏览器就是访问不了,排查半天发现云安全组没放行。这种环境问题最浪费时间,所以部署前把端口放行检查一下,能省很多事。

5.3 前端构建与两种部署方式

前端构建执行npm install和npm run build,生成dist目录。部署方式有两种。第一种是标准前后端分离:dist给Nginx托管,Nginx配置location /指向dist,location /api/反向代理到SpringBoot的8080端口。第二种是把dist目录复制到SpringBoot项目的resources/static下,直接把前端页面打进jar包,访问后端端口就能同时看到页面和接口。

第二种方式最简单,适合毕设演示或小带宽服务器,但前后端耦合在一个进程里,扩展不太灵活。我做项目演示时经常用第二种,正规部署还是推荐第一种。无论用哪种,都需要注意history模式刷新404的问题,Nginx配置里必须加try_files $uri $uri/ /index.html;。否则首页能打开,点路由内部跳转没问题,一旦按F5刷新,子路由就会404。

server { listen 80; server_name hospital.example.com; root /opt/hospital/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }

5.4 生产环境初始化与备份

首次上线前,先执行项目里的init.sql脚本,导入表结构和初始管理员账号。导入后不要急着完事,我习惯逐页点一遍:首页轮播图是否正常显示,科室列表图片是否可访问,医生排班是否覆盖本周,新闻公告是否按时间倒序,预约提交后后台管理能否看到。任何一个环节断掉,都要回到底层排查。

数据备份我用crontab每天凌晨备份一次MySQL,备份文件保留最近7天。医院网站涉及预约和患者信息,备份是底线。建议备份脚本用mysqldump加--single-transaction参数,保证备份期间不锁表;备份文件放到独立目录,再异地同步一份,双保险。

6. 常见问题与排查记录

6.1 启动时报Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required

这个问题几乎都出在MyBatis和SpringBoot版本不匹配,或者Mapper接口没有被正确扫描。检查两步:第一步确认pom里mybatis-spring-boot-starter版本和SpringBoot版本配套,SpringBoot 3.x必须用MyBatis starter 3.x;第二步确认启动类加了@MapperScan,或Mapper接口上有@Mapper注解。

还有一个隐蔽原因:application.yml里的mapper-locations路径写错,XML文件没被加载,也会出现类似Mapper找不到的报错。如果XML文件放在src/main/java下面,默认打包可能不会带上,需要额外在pom里配置resources,否则运行时就是找不到。这个问题报错不一定直接说XML,而是Mapper方法找不到statement,排查时方向要广一点。

6.2 数据库中文乱码

中文乱码问题,优先检查三处:数据库表字符集、JDBC连接URL参数、前端页面编码。我建议建表脚本里每张表都指定DEFAULT CHARSET=utf8mb4,数据库连接URL带characterEncoding=utf8,前端页面通过Vite默认输出UTF-8,一般就没事了。

如果已经出现乱码数据,需要把数据和表结构一起迁移,比较麻烦,所以最好新建表时就把字符集写清楚。MySQL 8.0下如果直接用工具建库没指定字符集,很可能会继承服务器的latin1默认值,这是很多乱码问题的根源。我遇到过的情况是,开发环境一切正常,生产环境连的是旧的MySQL 5.5,默认字符集不对,页面中文全变成问号。后来在生产库执行了alter table改字符集,才算彻底解决。

6.3 接口返回404或403

接口返回404或403,先判断是哪个层面拦住的。404如果是后端接口,检查Controller的@RequestMapper路径和前端请求URL是否完全一致;如果是Nginx层的404,看静态文件和代理配置是否正确。403大多是权限拦截器或跨域配置问题。我在项目里加入了JWT拦截器,放行名单里必须包含登录接口、科室列表、医生列表、预约提交等公开接口,否则前端访问这些接口就会403。

另外,跨域预检请求是OPTIONS方法,拦截器要放行OPTIONS,否则浏览器能看到请求发出去了,但CORS一直报错。排查403时我有个习惯:直接拿postman请求一下后端接口,如果postman正常但浏览器报错,九成是跨域或预检拦截问题;如果postman也一样403,再去查拦截器逻辑。

6.4 预约提交成功但号源数量没变

这个坑我印象很深。原因是Service方法没加@Transactional,或者加了但方法被同类内部调用,导致事务不生效。解决办法是事务注解加到Service入口方法,并且保证该方法是由Controller调用的外部入口进入。排查时打开后端日志,看预约插入和排班更新是否在一个事务里,如果中间有异常回滚,号源数量就不会变。

还有一种情况是事务已经生效,但排班表的主键查错了,更新的不是同一个排班记录。比如前端传了医生ID和日期,后台去更新医生表而不是排班表,自然不会影响号源。解决方法是前端提交时把排班ID一起带上,后端直接用排班ID定位记录,避免二次查询时的脏数据问题。

6.5 Vue打包后页面刷新404

Vue项目打包部署后,首页能打开,点刷新或直接访问某个子路由却404。原因是history模式的路由交给前端,但服务器不知道这个规则,直接按路径找文件找不到。Nginx需要加try_files $uri $uri/ /index.html;,让所有未命中静态文件的请求都回到index.html,由前端路由接管。

如果前端部署在Tomcat,可以改成hash模式,URL会带#号,没那么好看但省配置。我在项目README里把Nginx配置示例、history模式坑写成单独一节,后来项目交接给同事时省了很多沟通成本。这个问题几乎每个用history模式的前端项目都会遇到,提前在部署文档里写明,能避免上线当天手忙脚乱。

6.6 播放m3u8视频只有声音没有画面

健康宣教视频播放m3u8时只有声音没有画面,第一排查video.js的hls插件是否引入,第二看视频源和分片是否有跨域CORS配置。前端引入videojs-contrib-hls后,设置autoplay和controls,一般能正常播放。如果视频本身是多码率HLS,可在播放器初始化时指定resolution,不让它默认加载音频流。

对象存储的CORS配置也很重要,否则浏览器会拦截m3u8的分片请求,播放器进度条一直转却出不来画面。这个问题在本地开发时很少遇到,部署到线上才会暴露,所以上线前一定要测一遍视频播放。我之前的做法是本地放一个测试m3u8文件,先确认前端播放器没问题,再切换到对象存储的线上地址。

6.7 图片加载慢怎么优化

图片加载慢,尤其是科室图和医生头像,不要把图片放进数据库。数据库里只存URL,图片交给MinIO或Nginx静态目录。我在几个版本迭代里都遇到过图片加载慢的问题,后来定了一个规范:新增图片资源统一走MinIO,数据库只保留对象地址,页面通过对象存储或CDN访问。

如果暂时没有条件部署MinIO,也可以用Nginx的alias映射本地目录,效果差别不大。医院网站图片数量不算特别大,重点是把文件存储和数据库分离,维护起来才不别扭。同时,前端图片要做懒加载,列表页几十张图一次性全加载,首屏会非常慢。Element Plus提供了懒加载组件,配合统一缩略图尺寸,页面加载速度提升很明显。

最后说一点个人经验:做这类中小型医院网站,技术点并不难,难的是把业务链路走通,尤其是排班、预约、确认这条主线。源码和部署教程再完整,也不如你自己动手跑一遍、改一个接口、加一个字段来得实在。如果你正在拿这个项目做毕业设计或者练手,建议第一天就把数据库表和预约流程画明白,再动手写代码。遇到上面列的问题时,不要急着搜一堆资料,先按数据库、后端、前端、部署四个层面一层层排查,大多数问题都是版本、路径、事务这三种原因引起的。这个项目后续还可以扩展体检预约、在线问诊、患者反馈统计等模块,骨架搭好了,扩展起来会顺手很多。

返回列表