从"一个人写前后端"到"工程化协作":JavaWeb前端工程化(上)的完整实践笔记
很多刚开始学JavaWeb的人会有一个疑惑:前端工程化不是前端岗位才需要学的东西吗?我一个写Servlet、JSP、对接MySQL的后端,为什么要关心CSS和JavaScript怎么组织?
我以前也是这么想的,直到自己做了一个带完整前后端的JavaWeb项目,被静态资源404、浏览器缓存、部署后路径全失效这几个问题连续折腾了好几天才明白:在真实的JavaWeb开发里,前端资源从来不是"放到webapp目录下就完事"那么简单。尤其是当你做的项目要交给别人运行、或者打包部署到服务器上时,前端资源的组织方式会直接决定项目能不能正常跑起来。
这篇笔记是"前端工程化"章节的(上)篇,主要梳理静态资源管理、模块化思想、构建工具的基本逻辑,以及如何在IDEA里把传统JavaWeb项目和前端资源整合起来跑通。内容基于我跟着黑马JavaWeb课程做笔记、同时自己动手复现一个完整项目(含MySQL数据库)的实战过程,适合正在学JavaWeb、准备从单页面走向完整项目的同学参考——不管你是打算纯手写JSP,还是已经开始用Vue这类框架,前半部分的基础逻辑都是通用的。
1. JavaWeb开发者为什么要碰前端工程化:三次真实翻车现场
先说结论:前端工程化对于JavaWeb开发者的核心价值不是"追赶时髦",而是解决传统开发方式下一定会遇到的资源组织问题。我用三个亲身经历的翻车现场来说明。
1.1 第一次翻车:CSS文件复制到webapp,页面还是裸奔
第一次写JavaWeb项目时,我把整个前端页面的CSS文件夹直接拖进webapp目录就启动了Tomcat,结果页面打开只有最原始的HTML文本,没有任何样式。
排查了很久才发现问题不在Tomcat配置,而在资源路径。我的页面放在webapp/pages/index.html,CSS放在webapp/css/style.css,HTML里引用写的是css/style.css。如果是直接双击打开这个HTML文件,这个相对路径是能用的;但通过Tomcat访问时,URL变成了http://localhost:8080/项目名/pages/index.html,浏览器解析相对路径会以/项目名/pages/为基础去拼,最终请求的却是/项目名/pages/css/style.css——这个目录根本不存在,自然就404了。
这个问题的本质是:文件系统的相对路径和URL路径根本不是一回事。你的项目一旦通过服务器对外提供服务,所有资源引用都必须以"当前应用的上下文路径"为基准来规划。
1.2 第二次翻车:改了JS,用户那边怎么都是旧版本
还有一个让人抓狂的问题:本地调试时,明明修改了JS文件,刷新页面后浏览器跑的还是旧逻辑。Chrome的缓存机制会把JS、CSS这类静态资源缓存下来,只要文件名不变、服务器没有返回新的缓存头,浏览器就默认用本地缓存。
这类问题在前后端不分家的JavaWeb项目里特别容易踩。最简单的解决思路是"改文件名"——把app.js改成app-20240610.js,浏览器一看名字变了,自然就会重新请求。但问题来了:如果你的项目里有十几个页面都在引用app.js,每次改动都要把所有页面的引用路径同步改一遍,工作量不小还容易漏。
而这正是"前端工程化"里构建工具要解决的典型问题——许多工具支持自动生成带哈希值的文件名,并在打包时把HTML里的引用路径一并替换掉。这个机制你现在不用工具实现一遍可能体会不到它的价值,后面讲到模块化和构建时会展开。
1.3 第三次翻车:本地跑得好好的,部署到服务器就废了
这个坑最经典。本地开发时一切都正常,页面能打开、接口能请求、数据库能连上,但打包成war包部署到Linux服务器后,页面能打开,所有静态资源却全部失效,接口请求也全部404。
排查到最后发现是路径写死的问题。我在前端代码里把请求路径写成了http://localhost:8080/user/list,本地倒是没问题,但部署到服务器上后项目名变了、端口也可能不同,localhost更是直接指向服务器本机而非用户浏览器所在的机器。对于JavaWeb项目,最稳妥的做法是使用相对路径配合上下文路径,或者通过后端模板引擎动态拼接。自那以后我写前端请求路径再也不敢写死任何一种绝对地址了。
这三个问题本质上都指向同一件事:JavaWeb项目里的前端资源必须被系统性地组织、管理和规划,而不是"能用就行"。这就是前端工程化在JavaWeb课程里出现的原因。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 没有构建工具的日子:手动维护css/js的混乱成本
要理解为什么需要工程化,得先回到最原始的手工管理模式,看看它的成本到底有多高。这里我用自己整理过的webapp目录结构来举例。
2.1 一个"原始"但真实存在的webapp目录长什么样
典型的传统JavaWeb项目,前端资源全部塞在src/main/webapp下面:
text复制webapp/
├─ index.jsp
├─ pages/
│ ├─ login.html
│ ├─ register.html
│ └─ user/
│ └─ list.html
├─ css/
│ ├─ common.css
│ ├─ login.css
│ └─ list.css
├─ js/
│ ├─ jquery-3.6.0.min.js
│ ├─ common.js
│ ├─ login.js
│ └─ list.js
└─ images/
└─ logo.png
这种目录结构很直观,资源按类型分文件夹放,用的时候在HTML里挨个引入就行。对于只有两三个页面的学习项目,完全没问题。但当项目页面增多、多个页面共用部分JavaScript模块、组件化需求出现时,问题就来了。
2.2 一个JS文件的"小版本升级"引发的连锁反应
假设common.js里封装了一个校验用户权限的函数,所有页面都要引用。某天这个函数接口升级了(比如返回值从布尔值改成了对象),你需要:
- 修改
common.js本体; - 找到所有引用它的HTML/JSP页面;
- 逐个确认调用方式的兼容性;
- 为了避开缓存问题,还可能要把文件名改成
common-v2.js; - 把所有页面里的引入路径全部替换一遍。
这种操作只要漏了一个页面,就会出现"有的页面功能正常,有的页面报错"的诡异局面。排查的时候你会怀疑是不是后端接口出问题了,结果查半天发现只是一个页面的JS引用没改。
用Maven的人到这一步应该会有一种熟悉感——这就像你用mvn install手动管理jar包,却不用依赖管理工具一样。每个依赖都要自己下载、拷进lib目录、手动维护版本关系,最终必然会出现版本冲突或缺失。
2.3 无关紧要的"请求数量"在真实网络环境里一点都不无关紧要
还有一个经常被忽略的问题:性能。一个页面如果引入了5个CSS文件、8个JS文件,再加5张图片,浏览器就得发起18个HTTP请求(未计接口请求)。每一个请求都有网络开销,尤其是用户访问来自不同网络环境时,这个开销会被显著放大。
构建工具做的事情之一就是把多个JS文件合并成一个,多个CSS合并成一个,再压缩体积。一次请求能拿回来的资源,就不需要十几次。对于JavaWeb项目来说,这种优化在开发和部署阶段都能明显感受到——部署后的首屏加载速度会比手工模式快不少。
如果你没有真实在慢速网络环境里访问过这种未压缩的多资源页面,可能很难直观感受这一点的价值——大概的理解方式是:每多一个请求,就多一次从用户电脑到服务器之间的往返;页面加载时间受制于往返次数和单次传输量,减小请求数量与压缩资源体积正是构建工具最直观的收益。
3. 模块化、编译构建、依赖管理:前端工程化的三大基石
在真正上手工具之前,最需要建立的是三个底层概念。这三个概念对Java后端来说其实一点都不陌生,只是换了一套名词。
3.1 模块化:把"引用关系"显式化
手工模式下,HTML页面里引入多个JS文件,文件之间是"隐式依赖"——page.js用到common.js里的函数,但两者之间没有任何显式的声明关系。你只能靠"记住"来维护,页面HTML里的引入顺序一旦写错,函数就找不到了。
模块化的核心思路是:每个JS文件显式声明自己依赖谁、导出什么。ES Module语法大概长这样:
javascript复制// utils.js
export function formatDate(date) {
return date.toLocaleDateString();
}
// user.js
import { formatDate } from './utils.js';
document.getElementById('time').textContent = formatDate(new Date());
这样代码的依赖关系一目了然。哪个页面用到哪个模块,直接在代码里写清楚,不需要靠人为约定的"引入顺序"来维持正确性。
用Maven类比很好理解:你在pom.xml里显式声明依赖了哪个jar包的哪个版本,Maven会负责把依赖链拉齐并放到classpath里。前端模块化也是这个思路,只是针对的对象从Java类变成了JS模块。
3.2 构建:开发态到生产态的转换
Java后端有两个状态:源码(.java文件)和编译产物(.class文件)。Maven的核心工作之一,就是把你的源代码编译成可执行的产物。前端工程化里的"构建"也是同样的逻辑:把开发态的源代码(可能是几十个分散的JS/CSS文件)转换成生产态的产物(一个合并压缩过的bundle.js和一个合并压缩过的bundle.css)。
构建过程中做的事情通常包括:
- 合并:把多个文件合并成更少的文件;
- 压缩:去掉空格、换行、注释,缩短变量名,减小体积;
- 转译:把ES6+等较新的语法转成兼容更多浏览器的ES5语法;
- 指纹化:在文件名上生成内容哈希,方便浏览器缓存管理。
理解了这个逻辑,就理解了为什么"前端工程化"对JavaWeb开发者是有实际价值的——它相当于给前端资源也加了一道"编译"环节,这道工序在只用原始资源文件时是不存在的。
3.3 依赖管理:npm机制其实和Maven仓库同构
Java用Maven从中央仓库拉取jar包到本地~/.m2/repository,前端用npm从npm仓库拉取依赖到项目的node_modules目录。两者唯一本质区别只是语言生态不同。
一条典型的npm命令:
bash复制npm init -y
npm install jquery
执行完后项目里会出现一个node_modules目录,里面的jquery就是构建时准备打包进产物的代码。
这里有一个Java开发者容易产生的疑问:直接用原始形式引入jquery,然后通过构建工具打包,不也是可以的?答案是可以,npm依赖管理和直接拷贝文件的本质区别在于:npm能解析"依赖树的传递关系"。比如你引入了A库,A库内部依赖B库,npm会自动把B库也装好;手工拷贝则要自己检查某个库的依赖清单,逐一手动下载,管理成本会随库的数量增加而迅速上升。
3.4 三个概念的关系串起来
很多新手会在"模块化""构建""依赖管理"这三个词之间绕晕。我常用一个做饭的类比:
- 模块化 = 食材分门别类放好,每种食材都有固定的存储位置,做菜时按需取用;
- 依赖管理 = 按菜谱批量采购,需要的食材和调料由清单自动搞定,不会哪个材料漏买;
- 构建工具 = 把准备好的食材按顺序处理成盘菜——该切的切、该炒的炒、该摆盘的摆盘,端上桌(浏览器)时已经是可以直接吃的状态。
三个步骤贯穿的就是"前端工程化"上半部分的核心骨架。至于具体用哪个构建工具(Webpack、Vite、Gulp),下半部分讲框架集成时再展开,上半部分最紧要的是先把逻辑框架建立起来。
4. IDEA里跑通JavaWeb项目:从Artifact配置到页面显示的完整链路
概念讲完,落到实操。很多人在"idea运行javaweb项目配置"上卡住过——不是代码有问题,而是IDEA、Tomcat、Artifact三者之间的配置文件关系没弄明白。我在这里把完整链路拆开讲。
4.1 新建项目和配置Tomcat:两个关键入口
用IDEA新建一个普通的JavaWeb项目时,推荐先建好纯Java项目,再手动加上Web支持,而不是直接点"Java Enterprise"菜单里的模板。
第一步,File -> New -> Project,选Maven Archetype,Archetype选maven-archetype-webapp。它会自动生成一个带有src/main/webapp和web.xml的标准Web工程骨架。
第二步,配置Tomcat运行环境。在IDEA右上角打开Run/Debug Configurations,点击加号,选择Tomcat Server -> Local。关键是Deployment标签页,把项目添加进去,这里选war exploded而不是war。两者的核心区别是:
war:把整个项目打包成一个war文件再部署,适合生产发布;war exploded:直接把项目目录映射到Tomcat作为Web应用,适合开发调试。修改代码后热部署更快,也能直接在IDEA里查看out目录中的实际产物。
我建议开发阶段始终用war exploded。等最终要部署上线了,再通过Maven执行mvn clean package打出war包。
提示:IDEA里Application context的值决定了访问路径的前缀,比如默认填
/webapp,那么所有资源都要通过http://localhost:8080/webapp/来访问。本地开发时可以改成一个简短的名字,避免URL过长。
4.2 构建产物到底放在哪里:看out目录就懂了
点击Run,IDEA会做这几件事:
- 编译
src/main/java下的所有类,输出到WEB-INF/classes; - 把webapp下面的所有静态资源(HTML、CSS、JS、图片),原样拷贝到Web应用目录的根路径下;
- 把
src/main/resources下的配置文件也拷贝到类路径下,也就是WEB-INF/classes里; - 整个结果被映射到Tomcat配置的
webapps目录中。
运行模式下,你能在IDEA的out/artifacts目录里看到完整的Web应用结构。以我之前整理过的输出目录为例:
text复制out/artifacts/javaweb_demo_war_exploded/
├─ index.jsp
├─ css/
│ └─ style.css
├─ js/
│ └─ app.js
├─ WEB-INF/
│ ├─ web.xml
│ ├─ classes/
│ │ └─ com/example/servlet/UserServlet.class
│ └─ lib/
└─ META-INF/
有一个容易踩的认知误区是:以为IDEA会把源代码目录webapp直接映射给Tomcat,修改了源码就自动生效。实际上Tomcat运行的是out里的那一份拷贝。如果改的代码没生效,第一反应应该去out目录下看看对应文件到底更新了没有——这个排查看不到,后面很容易原地转圈。
4.3 一个请求从浏览器到页面的完整生命周期
以一个带MySQL的登录页面为例,跑通一条"前端页面 -> 后端Servlet -> 数据库验证 -> 页面跳转"的链路需要哪几环:
- 浏览器输入
http://localhost:8080/javaweb_demo/login.html; - Tomcat根据URL找到webapp目录下的
login.html,返回页面; - 用户在页面输入账号密码后提交到
/login这个URL; - Tomcat依据
web.xml或@WebServlet注解找到对应的LoginServlet; LoginServlet通过JDBC连接MySQL,执行SELECT * FROM user WHERE username = ? AND password = ?;- 验证通过后,转发或重定向到
index.jsp;失败则返回错误信息。
在这条链路上,"前端工程化"发挥作用的主要是第2步和第3步——静态资源能否被正确返回、请求路径是否与后端Servlet注解匹配。很多初学者项目里前端点击提交后控制台报一堆JS错误,就是因为前端资源本身没有被正确组织。
4.4 Servlet路径与前端请求路径的匹配规则
JavaWeb里@WebServlet注解里写的路径,和前端请求的路径必须严格一致。
比如后端这么写:
java复制@WebServlet("/user/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 处理登录逻辑
}
}
前端页面用axios或fetch请求时就要用对应路径:
javascript复制fetch('user/login', {
method: 'POST',
body: new URLSearchParams({
username: document.getElementById('username').value,
password: document.getElementById('password').value
})
});
注意这里的user/login是相对路径——它在当前页面URL基础上自动拼上上下文路径。在http://localhost:8080/javaweb_demo/login.html这个页面上,实际请求就会变成http://localhost:8080/javaweb_demo/user/login。这就是前面说的"不要在代码里写死绝对路径",相对路径方式可以自动适配上下文路径的变化。
注意:如果后端用的
@WebServlet(urlPatterns = "*.do")这类后缀形式的映射,前端请求路径也要对应带.do后缀。这是JavaWeb里一个比较容易出错的细节,很多人前端fetch写了/user/login,后端注解却是/user/login.do,结果返回404,查半天对不上。
5. 一个带MySQL的完整案例:前端表单数据是怎么流进数据库的
这节我用自己整理过的一个用户注册案例,把"前端工程化"在整条数据链路里的实际位置讲清楚。很多JavaWeb学习者会对"前端工程化"和"完整项目案例"之间的关系感到模糊——两者不是割裂的:前端资源如何组织,会直接影响整个功能链路能否正常跑通。
5.1 完整的项目结构设计
先看项目总览:
text复制javaweb-demo/
├─ pom.xml
├─ src/
│ ├─ main/
│ │ ├─ java/
│ │ │ └─ com/example/
│ │ │ ├─ entity/User.java
│ │ │ ├─ util/DBUtil.java
│ │ │ └─ servlet/RegisterServlet.java
│ │ ├─ resources/
│ │ │ └─ db.properties
│ │ └─ webapp/
│ │ ├─ register.html
│ │ ├─ css/style.css
│ │ └─ js/register.js
│ └─ test/java/
└─ sql/
└─ init.sql
MySQL侧建一张简单的用户表:
sql复制CREATE DATABASE javaweb_demo CHARACTER SET utf8mb4;
USE javaweb_demo;
CREATE TABLE `user` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) NOT NULL UNIQUE,
`password` VARCHAR(100) NOT NULL,
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5.2 前端页面和JS逻辑的组织方式
register.html里放一个表单,register.js里负责收集数据并请求后端接口。用原生fetch,不依赖其他库,这样能更清楚地看到"前端资源组织"和"后端数据链路"之间的独立边界:
html复制<form id="registerForm">
<label>用户名:<input type="text" name="username" id="username"></label>
<label>密码:<input type="password" name="password" id="password"></label>
<button type="submit">注册</button>
</form>
javascript复制document.getElementById('registerForm').addEventListener('submit', async (e) => {
e.preventDefault();
const resp = await fetch('user/register', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
username: document.getElementById('username').value,
password: document.getElementById('password').value
})
});
const result = await resp.json();
if (result.code === 1) {
alert('注册成功');
} else {
alert(result.msg);
}
});
注意fetch里用的是相对路径user/register,这正是前面讲的"不写死绝对路径"实践。
5.3 后端接收数据并写入MySQL
RegisterServlet负责接收前端传来的表单字段,通过JDBC执行INSERT插入数据库:
java复制@WebServlet("/user/register")
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("application/json");
String username = req.getParameter("username");
String password = req.getParameter("password");
try (Connection conn = DBUtil.getConnection()) {
String sql = "INSERT INTO `user` (username, password) VALUES (?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
int rows = ps.executeUpdate();
if (rows > 0) {
resp.getWriter().write("{\"code\":1,\"msg\":\"注册成功\"}");
} else {
resp.getWriter().write("{\"code\":0,\"msg\":\"注册失败\"}");
}
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("{\"code\":0,\"msg\":\"服务器异常\"}");
}
}
}
数据链路整理成表格就是这样的关系:
| 环节 | 技术组件 | 首要从"工程化"中获得的帮助 |
|---|---|---|
| 页面展示 | HTML + CSS | 静态资源组织和构建 |
| 页面逻辑 | JavaScript | 模块化拆分和依赖管理 |
| 前后端通信 | fetch / ajax | 请求路径规划,避免写死 |
| 后端接口 | Servlet | 路径注解与前端请求一致 |
| 数据库访问 | JDBC / MyBatis | 无关,但依赖管理保障jar包可用 |
| 数据库 | MySQL | 初始化脚本与字符集配置 |
5.4 返回JSON给前端渲染的关键细节
JavaWeb里Servlet返回JSON给前端,有两个极其容易踩的坑:
第一个是resp.setCharacterEncoding("UTF-8")必须在getWriter()之前调用,否则返回的JSON里中文会乱码。第二个是req.setCharacterEncoding("UTF-8")必须放在读取请求参数之前,否则表单提交的中文会乱码。这两个"必须先于某操作"的顺序问题,几乎每次跑通案例都会遇到。
解决之后,前端才能稳定拿到JSON数据并做渲染。整条链路里,"前端工程化"真正参与的部分集中在静态资源组织和请求路径规划,但前端资源组织不好、路径规划不对,后面的后端逻辑再好,页面也跑不通——这就是它在这个章节里被重视的原因。
6. 前端工程化(上)最容易踩的四个坑:带完整排查链路
这一节我整理了自己跟着课程做笔记、反复复现项目时遇到的高频问题,每个坑都给出完整的排查思路,而不是直接给结论。你得知道这个坑"是怎么从现象一步步定位到根因的",才能在下一次遇到时快速反应。
6.1 坑一:idea运行javaweb项目时静态资源404
现象:页面能打开,但CSS、JS、图片全部404。
排查链路:
- 先从浏览器开发者工具Network面板看请求的URL,确认路径前缀;
- 对比URL和实际文件在out目录中的位置。比如请求
/javaweb_demo/css/style.css,却在out/javaweb_demo_war_exploded/css/下找不到style.css; - 看IDEA里Artifact配置的Output Layout,确认webapp下的资源有没有被真正拷进artifact;
- 检查
Application context是否和你访问时用的前缀一致; - 确认页面文件的位置是否正确(例如页面在
pages/子目录下,引用css/style.css时,请求路径会相对pages目录解析,这时应该改成../css/style.css)。
多数情况下,404的根因就出在第4步或第5步——上下文路径不匹配,或相对路径层级算错。
排查小技巧:直接在浏览器地址栏手动访问静态资源URL,比在页面里刷新更直观。如果手动访问也404,问题在资源组织;如果手动访问能打开但页面里不行,多半是相对路径引用问题。
6.2 坑二:改完前端代码刷新后不生效
现象:改了CSS或JS,页面显示毫无变化。
排查链路:
- 按F12打开Network面板,看加载的资源请求;
- 查看响应头里的
Cache-Control和ETag字段,确认是否命中缓存; - 在Network面板勾选
Disable cache再刷新一次,缓存问题可以先排除; - 如果禁用缓存后还是没变化,去
out目录下看文件内容是否真的更新了——IDEA有时热部署不会自动同步外部修改的文件,需要手动Build -> Rebuild Project; - 确认Tomcat的
On frame deactivation和On update选项是否选择了Update resources或Redeploy。
实操中最推荐的组合是:IDEA里把On frame deactivation设为Update resources,并且每次修改代码后主动按Ctrl+F9重新编译。这样既不频繁重启Tomcat,又能保证代码同步。
6.3 坑三:Tomcat端口被占用导致无法启动
现象:启动Tomcat时控制台报Port 8080 was already in use。
排查链路:
- 用命令查谁占用了端口:
bash复制netstat -ano | findstr 8080
- 记下占用进程的PID;
- 用任务管理器找到对应进程并结束,或者直接在IDEA的Tomcat配置里换一个端口,比如8081;
- 改端口时要同步更新前端代码里所有的请求路径(如果使用了绝对端口),否则接口会请求到旧端口上。
这个坑本身不复杂,但它和"前端工程化"有一个共同教训:端口、路径这类环境信息一旦出现在代码的多个位置,维护起来就非常痛苦。更合理的做法是把这类信息统一放到配置文件中,前端通过一个公共变量来引用,而不是散落各处。
6.4 坑四:表单提交中文乱码,从页面到数据库全乱
现象:前端正常输入中文,存入MySQL后变成???。
排查链路:
- 先看后端接收到的参数是否乱码:在Servlet里打印
req.getParameter("username"),如果是???,说明请求解析阶段出了问题——req.setCharacterEncoding("UTF-8")没在读取参数之前调用; - 如果Servlet打印正常但数据库存进去是乱码,检查MySQL连接的URL参数:
text复制jdbc:mysql://localhost:3306/javaweb_demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
- 确认数据库和表的字符集是utf8mb4,而不是latin1;
- 看JSP页面(如果有)是否在顶部声明了
<%@ page contentType="text/html;charset=UTF-8" language="java" %>。
这个坑跨越了前端、后端和数据库三层,也是"完整案例"里最容易卡壳的地方。建议在一开始建库建表时就把字符集统一设定为utf8mb4,连接URL带上编码参数,前端页面统一用UTF-8声明,三层一致后基本不会再犯。
前端工程化(下)会往下讲框架集成、SPA开发模式、打包部署流水线,到那个时候,前端代码就不会再以"手写的若干个文件"这种方式存在于webapp目录里,而是先经过构建工具打包成产物,再拷贝到JavaWeb项目的webapp中被服务器直接使用。现在先把(上)篇的基础逻辑、IDEA配置、完整链路和三四个高频坑消化掉后,再往下走会更顺——那些看起来"高级"的工具,本质上就是为了解决我们前面遇到的这些手忙脚乱的问题而诞生的。
我自己在整理这个章节笔记时最大的体会是:不要急于上工具,先把"为什么需要工具"弄清楚。当你在手工模式下亲身体会到修改一个JS文件名要连带改十几个页面的痛苦时,再回头去看构建工具,一切都顺理成章。这一章的课后练习建议是:在IDEA里手动建一个带MySQL的注册登录案例,故意用最原始的方式组织前端资源,跑通之后再思考"哪些环节如果有工具介入会更爽",然后带着这份需求去接触下半部分的内容。
