首页 / 资讯中心 / 文章详情

软件工程实习复盘:Java Web开发从环境配置到部署排错

软件工程实习复盘:Java Web开发从环境配置到部署排错 ★ FEATURED ARTICLE
简介这份《软件工程实习记录》面向本科软件工程及相关专业学生尤其适合需要撰写实习周记、整理项目实践材料或准备毕业实习归档的读者参考与改写。压缩包共1个doc文件约40KB以按周记录的Word文档形式呈现内容从实习动员、分组开发延伸到具体任务实现可按时间线直接借鉴结构与表述。文档结合真实项目场景梳理软件开发六步流程、需求与设计要点、项目经理职责、常见岗位及前后端技术栈并记录Swing文件查找与排序、HTML/CSS/JavaScript页面、Node.js接口、MySQL增删改查、Linux与ARM环境搭建等实践片段便于读者理解开发流程、补充实习记录细节并对照修改。目前已有2924人学习适合作为实习记录模板与软件工程实践复盘参考。1. 写代码只占 15%一份四周实习记录里最反直觉的一句话四周实习结束的那天晚上我把这份按日期一条条记下来的软件工程实习记录从头翻了一遍。第一天的笔记里写着一句当时自己都不太信的话编码在整个开发过程里大概只占百分之十五。这是大学毕业前那段实习留下的原始材料从可行性分析、需求分析一路记到最后一晚的 Tomcat 部署中间夹着一堆当天就出问题、当场修改的操作。它能帮到三种人正在补实习记录、需要一份能照着改的骨架的人刚上手 Java Web、天天在环境和编码上卡住的初学者以及想知道企业里一个功能从需求到上线要经过几道处理环节的人。我按时间线拆开讲哪些是当天就有结论的哪些是半年后才想明白的哪些问题其实到现在还有人天天踩。能复现的步骤命令和参数我都写出来。2. 桌面工具与静态页第一周的功能为什么都卡在「地址写死」上第一周的任务看着很小做个文件查找、做个文件排序、做个教师信息展示页。三件事没有一个用到框架全是 JavaSE 加 HTML/CSS/JS。但正是这一周让我第一次意识到能跑起来和能用之间隔着的不是语法而是「变量从哪来」这件事。当时的代码里到处是写死的路径、写死的数组、写死的字段名功能演示没问题换个目录就报错换个数据源就得重写。回过头看这一周的价值不在于我写了几个控件而在于它把三类最基础的问题摊开了文件系统怎么访问、排序按什么规则算「有序」、页面数据从哪来。这三件事在后面 Node.js 单页和 JSP 阶段会以另一种形式重新出现只是那时候你会用框架把它包起来看不见底层。2.1 文件查找功能从写死路径到让用户选目录当年的第一版是这样的在面板上放一个输入框、一个按钮、一个表格点击后去某个固定盘符下扫一遍把包含关键字的文件名回显到表格里。演示能过但只要换台机器就完蛋因为路径写在了代码里。我拿着这个问题去问驻场的指导工程师他改了两处路径改成由文件选择器返回回显前先清空表格。改动很小但把「参数」这个概念讲透了——凡是会变的东西都不该硬编码在方法体里。复现的核心代码大概长这样// 查找面板关键字来自输入框根目录来自文件选择器两者都不写死 JFileChooser chooser new JFileChooser(); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); // 只允许选目录避免用户选到文件 if (chooser.showOpenDialog(panel) JFileChooser.APPROVE_OPTION) { File dir chooser.getSelectedFile(); String keyword keywordField.getText().trim().toLowerCase(); // listFiles 只扫一层模糊匹配用 contains忽略大小写 File[] hits dir.listFiles((d, name) - name.toLowerCase().contains(keyword)); tableModel.setRowCount(0); // 关键先清空否则每次查找结果会叠加 if (hits ! null) { for (File f : hits) { tableModel.addRow(new Object[]{f.getName(), f.length(), new Date(f.lastModified())}); } } }setFileSelectionMode决定用户能选什么这是最容易漏的一行漏了用户就会选到一个文件listFiles返回 null界面直接空白看起来像「查找没生效」。listFiles传的是过滤器只做一层扫描要递归得自己写FileVisitor而且必须考虑权限不足的目录会抛异常。还有一个当时完全没意识到的点扫描放在事件线程里目录一大界面就假死正确做法是丢进SwingWorker边扫边往表格里加行。提示文件名模糊匹配不等于数据库里的 like。后者的大小写敏感性取决于字段的排序规则同一个关键字在两边可能给出不同结果测试时别只测小写目录。这一小节真正该带走的是判断依据什么时候用文件选择器、什么时候用配置项。选目录这种由使用者每次决定的东西必须走交互而像根目录这种部署环境固定的东西更适合放配置文件。当年我把两者混在一起所以每次换机器都得改代码重新编译这就是典型的把配置写进了逻辑里。2.2 冒泡排序给文件名排序当年的选择和今天的替代方案第二天的任务是「把磁盘里现存的文件按文件名排序」。记录里写的是冒泡排序说实话当年选它不是因为合适而是因为它是唯一一个我能不看资料默写出来的。冒泡在几十条数据上完全够用但文件列表动辄上千条O(n²)的比较次数会让界面明显卡顿而且它对字符串的处理默认是字典序file10会排在file2前面用户一眼就觉得「排错了」。三种常见做法的对比我一般这么比方案时间复杂度稳定性适用场景主要问题冒泡排序O(n²)稳定教学、几十条以内数据一多就卡工程上基本不用快速排序平均 O(n log n)不稳定大批量、不在乎相同元素顺序实现要处理基准值选择归并排序 / 库函数排序O(n log n)稳定绝大多数业务场景需要自定义比较器实际工程里没人手写排序直接用库函数加比较器// 用库函数替掉手写冒泡比较器决定「什么算有序」 Arrays.sort(files, (a, b) - a.getName().compareToIgnoreCase(b.getName())); // 想要 file2 排在 file10 前面就得抽数字做自然序比较而不是纯字典序 ComparatorFile natural (a, b) - { String x a.getName().replaceAll(\\d, m - String.format(%010d, Long.parseLong(m))); String y b.getName().replaceAll(\\d, m - String.format(%010d, Long.parseLong(m))); return x.compareToIgnoreCase(y); };第一段是字典序第二段把名字里的数字补零到固定宽度再比这样2会被补成0000000002自然就排在10前面。参数上唯一要盯的是大小写策略Windows 的文件系统不区分大小写Linux 区分同一份排序代码在两个平台上结果可能不同跨平台工具必须显式指定compareToIgnoreCase或者统一转小写。注意排序结果依赖比较器不依赖排序算法本身。换个算法但不换比较器用户看到的顺序不会变所以排查「排序不对」永远先看比较器。这一段判断依据很清楚数据结构小、只求正确手写没问题一旦数据量上来或者要和界面的其它逻辑配合就用库函数加比较器把「有序的定义」抽出来单独维护。后来做报表导出时我用的是同一个思路排序规则抽成配置前端后端共用一套省掉了很多「为什么两边顺序不一样」的争论。2.3 数组渲染表格前端第一步不是样式是数据从哪来第一周最后一天是用 HTML 标签加 CSS 加 JavaScript 做一个教师信息展示页数据先放在数组里用表格渲染出来。当时我以为难点在样式实际卡住的地方是「页面怎么知道数组里有什么」。写完才发现这套东西只能演示静态数据真实项目里数据在数据库里页面得先发请求、拿到响应、再拼 DOM这三步任何一步错了页面上就是一片空白。这就是为什么后面必然要走到 Node.js 单页和 JSP不是技术栈升级是数据源从内存换成了数据库之后请求、响应、渲染这条链必须显式建立起来。很多人在这一步以为是前端框架的问题其实是没搞清楚请求发给了谁、返回了什么格式。判断依据很简单——如果刷新页面数据就没了那数据在内存里如果换了浏览器数据还在才说明数据落了库。3. 从静态数组到 MySQLNode.js 单页这条最朴素的链路第二周开始接触 Node.js。目标写得很清楚实现一个单页把数据库里的教师信息读出来显示再支持增删改查。听上去是一个完整的 CRUD实际上被拆成了三步先让页面能发请求、再让服务端能连数据库、最后让按钮触发不同的 SQL。这个拆分方式后来我一直在用因为它把「玄学问题」变成了三段可单独验证的链路——前端错了就查请求服务端错了就查连接数据错了就查语句。这段经历里最值钱的不是 Express而是「先用静态数组跑通再换真实数据源」这个顺序。当年我不理解为什么不能直接连库多绕了一圈现在带人时我会强制要求这么干因为一旦请求链路没通你就分不清是接口没写对还是数据库连不上两个变量同时存在排查成本翻倍。3.1 npm init 之后的两小时Express 的 get 与 post 最小闭环搭建过程很朴素装 Node.js桌面建一个目录当根目录npm init初始化生成package.json然后装 Express。当年为了搞懂 get 和 post 花了小半天现在回头看只需要记住一点get 的参数在 URL 上post 的参数在请求体里而请求体要能被解析出来中间必须挂一个解析中间件。const express require(express); const app express(); app.use(express.urlencoded({ extended: true })); // 解析表单体没有这行 post 拿到的 req.body 是 undefined // 过渡阶段先用内存数组验证「请求-响应」这条链路是通的 let teachers [{ id: 1, name: A, course: 数据结构 }]; app.get(/api/teachers, (req, res) { res.json(teachers); }); app.post(/api/teachers, (req, res) { const { id, name, course } req.body; // 字段名必须和页面 input 的 name 完全一致 teachers.push({ id, name, course }); res.json({ ok: true, count: teachers.length }); }); app.listen(3000, () console.log(listening on 3000));express.urlencoded的extended决定嵌套对象怎么解析表单场景用 true 兼容性更好。字段名对齐是最常见的出错点页面写teacherName后端解构name请求能通、响应 200但数据库里全是空值这种问题不看请求体根本查不出来。至于app.listen的端口选 3000 只是习惯被占用时 Node 会直接抛EADDRINUSE改成 3001 就行不用改其它任何东西。提示用浏览器地址栏直接敲接口地址只能测 get测 post 得用curl或者页面表单很多人以为接口挂了其实是没发对方法。判断依据在这里也很明确如果只是取数据用 get参数少且不敏感一旦涉及写入、删改一律 post。当年我把删除做成了 get 链接刷新页面就重复删一次这个坑在第 5 章会再提。3.2 Excel 进 MySQL建库、建表、字符集三件事数据从 Excel 导进数据库听上去是体力活实际上是整条链路上最容易埋雷的一步。当年我按顺序做了三件事装数据库、建库建表、把 Excel 里的数据导进去。中间卡住的地方全在字符集上Excel 默认的编码、建表时指定的字符集、连接时声明的字符集这三个只要有一个对不上导进去的中文就是问号或者乱码。-- 建表时显式指定字符集别依赖服务器默认值 CREATE TABLE teacher ( id INT PRIMARY KEY, name VARCHAR(32) NOT NULL, course VARCHAR(64) NOT NULL ) DEFAULT CHARSET utf8mb4;utf8mb4和utf8的区别在于能不能存四字节字符业务上只要有可能出现特殊符号就选前者。建表不写字符集时表会继承库的默认值库又继承服务器的默认值一路继承下来不同机器上装出来的结果可能不一样这就是为什么同样的建表语句在同事电脑上正常、在我电脑上乱码。数据导入之后用 Node 读出来还得过一道 JSON 转换。这里的参数是连接配置四个字段一个都不能错const mysql require(mysql); const pool mysql.createPool({ host: 127.0.0.1, user: app_user, // 别用 root 跑业务代码权限太大 password: ******, database: school, charset: utf8mb4 // 连接字符集缺了这一步前面建表的字符集白设 });用连接池而不是单连接是因为每次请求新建连接的开销在并发下很明显。charset这一项最容易漏漏了之后表里的数据是对的查出来的 JSON 却是乱码排查时容易误判成前端问题。判断依据如果数据库客户端里查出来正常、接口返回乱码问题一定在连接层如果客户端里就是乱码问题在写入或建表。3.3 增删改查的真实结构四个按钮背后是四条函数链页面上的增删改查看着是四个按钮做起来是四条链路按钮点击 → 触发某个 JS 函数 → 发请求到对应路由 → 执行某条 SQL → 返回结果 → 重新渲染。当年我做完发现「添加」有个毛病同一条信息点两次就出现两条页面越点越乱。解决方式是在添加前先清空原来的回显再重新拉一遍列表。这个改法很土但当时的场景下够用——它至少让页面状态和服务端状态重新对齐了。真正值得记下来的是这四条链路的分工。查询是只读的失败了不影响数据新增和修改要先校验再写库删除是最危险的必须有二次确认。当年删除做成了 get 超链接浏览器预加载或者用户刷新都会再发一次请求重复删除不会报错但会让人以为「数据自己没了」。正确做法是删除走 post 或 delete且服务端做幂等处理——删不存在的 id 返回成功而不是 500。判断依据任何会改数据的操作都不能用 get。这条到现在我 review 代码时还在查看到a href/delete?id3一律打回。至于渲染方式当年是手动拼 DOM现在更常见的是拿到 JSON 后交给模板或者前端框架本质没变数据流是单向的服务端返回什么页面就显示什么不要在页面里自己造数据。4. 环境与部署这两关Linux、服务器、Tomcat 全踩一遍第三周开始事情变得不友好了。前面写的都是应用层的代码错了看日志能猜个八九不离十这一周碰的是操作系统、编译工具链、Web 服务器出问题时现象和原因之间隔着好几层属于典型的黑匣子。我那几天最大的感受是环境问题的排查顺序比技术细节重要得多顺序对了能少走一半弯路。这一周的四块内容是连着的——先在一台企业版 Linux 上跑通编译和部署流程再回头搞懂网络服务器在局域网里扮演什么角色然后是本地网站的权限配置最后是 Tomcat 上的端口和依赖。当年我是按任务顺序硬着头皮做完的现在回看其实应该反过来先建立「请求从浏览器到进程经过哪些层」的模型再去动具体的配置文件。4.1 ARM-Linux 环境验证hello.c 加 Makefile 到底在验证什么第一个任务是熟悉 Linux 环境建目录、写hello.c、写Makefile、编译、把编好的文件放到开发板上跑。当年只觉得这是走流程后来才明白这几步验证的是四件不同的事交叉编译工具链是否装对、头文件路径是否正确、构建脚本能否复现、产物能否在目标架构上执行。# 最简构建脚本目标、依赖、命令三段式 hello: hello.c gcc hello.c -o hello clean: rm -f helloMakefile 的规则是「目标 : 依赖」下一行必须是 Tab 缩进而不是空格这是新手第一个高频翻车点报错信息是missing separator看不出哪里错。clean目标不是给构建用的是给「重来一次」用的删掉产物再编一遍能排除掉「改了源码但跑的还是旧二进制」这种低级问题。如果目标是 ARM 开发板编译器就不是gcc而是带前缀的交叉编译器产物用file hello一看架构就知道对不对——这一步比在板子上跑起来更早发现问题。判断依据编译通过只能说明语法和头文件没问题file看架构、ldd看动态库依赖两个都过了才算环境真的通了。注意交叉编译出来的程序在开发机上直接执行会报「无法执行二进制文件」这是正常的不是编译失败。这一段后来对我的影响很大任何新环境我都先跑一个最小可执行程序而不是直接上项目。最小程序能通说明工具链、路径、权限这几层没问题剩下的错误就只可能出在业务代码里。这个习惯省下的时间比任何调试技巧都多。4.2 本机网站打不开IIS 来宾账户权限与 NTFS 格式参与公司网站制作时我的任务是在本地装环境、配 IIS、设默认站点的主目录。系统装完了IIS 组件装了主目录也指过去了浏览器一访问就是打不开。查了半天资料才发现是两个原因叠在一起本地虚拟访问需要给 Internet 来宾账户配读取权限而权限选项卡只有在 NTFS 格式的磁盘上才显示。这件事的教训不是「记住勾哪个框」而是理解 IIS 的工作方式它用一个低权限账户去读站点目录这个账户没有权限页面就返回 401 或者 403。很多人看到「无法查看网页」第一反应是站点没启动或者端口不对实际上状态码已经区分开了——401/403 是权限404 是路径500 是应用内部错误。当年我连状态码都没看直接去改配置纯属乱试。第二个坑更隐蔽磁盘格式决定你能不能看到安全选项卡。FAT 格式没有 ACL 概念自然也没有权限设置入口。判断依据很直接——如果「属性」里根本找不到「安全」页签先看磁盘格式别去翻 IIS 配置。这条经验后来在服务器上复用给应用分配目录时第一件事是确认文件系统支持权限模型否则后面所有的权限设计都是空谈。权限给到「读取」还是「修改」取决于这个目录要不要被程序写入。当时因为后续要做后台管理需要上传文件所以勾了修改权限。这个决定有代价上传目录可写就意味着如果有人传了可执行文件并想办法访问到风险很高。正确做法是把上传目录单独拎出来禁掉脚本执行权限这是后来做安全评审时才补上的认知。4.3 端口占用与 jar 版本不匹配两封日志的读法最后一周做部署遇到了两个经典问题它们共同的特点是现象和原因隔得很远不看日志永远猜不到。第一个是 Tomcat 起不来访问项目主页失败。翻日志发现配置的端口已经被别的进程占用。端口冲突的表现有时候不是启动报错而是启动成功但访问不通因为请求被另一个进程接走了。查占用的命令两个平台不一样netstat -ano | findstr :8080 # Windows最后一列是进程号拿去任务管理器对 ss -lntp | grep 8080 # Linux直接能看到进程名和 pid改端口只要动server.xml里的 Connector 配置把 8080 换成 89 之类没被占用的即可。但要注意改完端口后项目里如果写死了回调地址或者前端请求地址也得跟着改否则页面能打开、接口全 404。判断依据能打开静态页但接口报错先查地址里的端口别急着怀疑后端。第二个是 Java 网站在浏览器里报 500服务端日志里有一句「数据加载失败」。当时第一反应是 Tomcat 配置有问题改了一堆连接池参数都没用最后发现是驱动 jar 包和数据库版本不匹配连接根本建立不起来。这个问题的排查顺序应该是先看日志里最内层的那条异常ClassNotFound是驱动没引入No suitable driver是驱动和连接串协议不匹配通信异常才是网络或版本兼容问题。提示日志要从最下面往上读最内层的Caused by才是根因上面那些只是层层包装。这两个问题让我形成了一个固定动作任何环境问题先确认「进程在不在、端口通不通、依赖版本对不对」三样都过了再去改配置。这个顺序看起来笨但它把不确定性一层层剥掉了比漫无目的地改参数快得多。4.4 分层框架与 JSP为什么学完还是不会独立做项目第三、四周穿插着学了 Struts、Spring、Hibernate 和 JSP。当时的理解是三个框架分别管三层表示层、业务逻辑层、持久层这样分工是为了让不同岗位的人写出来的代码风格一致同时把一些重复代码自动生成省掉手写 JDBC 和 SQL 的活。这个理解不算错但不够用因为它只回答了「它们是什么」没回答「什么时候该用哪一层」。真正卡住我的是另一件事学完这些概念我依然没法独立做一个项目。原因现在看很清楚——我学的是分层的结果没学分层的动机。比如为什么要用 ORM是因为手写 JDBC 时「打开连接、拼 SQL、处理结果集、关资源」这四步每次都要重复且关资源的顺序错了会泄漏连接ORM 把这些封装掉代价是你要理解它生成的 SQL 长什么样否则慢查询查不出来源。JSP 也是同理。当时的理解是「在页面里嵌 Java 代码片段就能动态生成内容还降低了对浏览器的要求」。这个说法本身没错但它掩盖了真正的问题逻辑写在页面上改一个判断就要动模板测试也没法单独测。后来所谓的分层本质是把「取数据」「算逻辑」「渲染」拆开让每一层能单独改、单独测。判断依据如果一个功能改起来要同时动三个文件说明分层没做对如果改一个展示文案都要重新编译说明逻辑和视图耦合了。当年我以「能跑」为标准所以永远停留在能跑的水平。这个认知转变发生得很晚但它比任何一个框架的用法都重要。5. 踩坑排查四周里最耗时间的五个问题下面这五个问题是那四周里真正吃掉时间的。共同点是现象都具有误导性第一反应基本都是错的。我按「现象 → 原因 → 解决」的格式记下来顺序按当时踩到的先后排。5.1 数据库装不上安装向导里被跳过的「开发者」选项现象安装程序跑到一半就退出没有任何明确报错重装两遍结果一样换安装包也不行。原因安装向导里有一步是选安装类型默认选项装出来的是一套精简配置缺了开发需要的组件和管理工具。我一路点「下一步」根本没看那一页写的是什么。解决卸载残留、清掉数据目录重新跑安装向导在选择安装类型那一步显式选开发者模式后面的组件全装上。判断依据是如果安装过程「太顺」反而要警惕是不是用了默认精简配置。后来我装任何开发环境都习惯先看安装向导有几步、每步选了什么这个动作花不了一分钟但能省掉两小时。5.2 中文乱码三个字符集只要有一个不一致就出问题现象往数据库里插中文用客户端查是问号从接口读出来是乱码页面显示又是一堆方块。原因Excel 文件的编码、建表时表用的字符集、连接字符串里声明的字符集三处不一致。当年只改了页面编码所以页面上的乱码没了库里的数据还是错的。解决三处统一到utf8mb4——建表时显式写DEFAULT CHARSET连接配置里写charset导入数据时确认源文件编码。排查顺序是从后往前先在数据库客户端里直接查客户端里就乱说明写入环节错了客户端正常但接口乱说明是连接层两者都正常只有页面乱才是页面渲染的问题。这个顺序能把范围一次缩到三分之一。5.3 添加的信息重复显示页面状态没和服务端对齐现象同一个教师信息点两次添加页面上出现两条刷新后又变回一条。原因添加成功后前端没重新拉列表而是直接在本地数组里又 push 了一条页面显示的是本地累积的结果服务端其实只有一条。当年的土办法是「添加前先清空原来的回显」本质就是用重新渲染代替增量更新。解决写操作之后一律重新拉一次列表用服务端返回的数据覆盖本地状态。代价是多一次请求好处是永远不会出现「页面和服务端不一致」。判断依据如果刷新页面数据就正常了那问题一定在前端状态管理不在后端。5.4 Tomcat 启动后访问失败端口被占用现象启动脚本没报错浏览器访问就是打不开换浏览器、清缓存都没用。原因配置的端口已经被另一个进程占用请求被那个进程接走了。这种冲突有时不体现在启动日志里所以看起来像「服务起来了但没响应」。解决用netstat或ss查端口占用改server.xml里的 Connector 端口同时把前端请求地址、回调地址里的端口一起改掉。改完先用curl直接请求一个静态资源验证服务器本身通了再去测接口。判断依据静态资源能拿到、接口 404就是应用内路径问题静态资源也拿不到才是服务器层问题。5.5 500 错误与「数据加载失败」驱动 jar 包版本不匹配现象页面报 500日志里只有一句数据加载失败改连接池参数、改 Tomcat 配置都没有效果。原因数据库驱动 jar 包和数据库版本不匹配连接压根没建立起来。日志里真正的根因藏在最内层的Caused by里被外层异常盖住了。解决从日志最底部往上读找到最内层异常。驱动类找不到就补 jar 包驱动不适用就换对应版本的驱动通信异常才去看网络和端口。判断依据所有「连不上数据库」的问题先分清楚是「找不到驱动」「驱动不认协议」还是「网络不通」这三类的修法完全不同混在一起改只会越改越乱。6. 把四周记录整理成能复用的模板一张表加一个排查顺序这份记录最后能直接用靠的不是文笔是结构。我当时是按天流水账记的回头整理时才发现真正有价值的信息只有四列今天做了什么、卡在哪、根因是什么、对应哪个知识点。按这个结构重排之后它既是一份实习记录也是一份自己的问题库。字段写什么为什么值得留任务一句话说清当天交付物面试时能说清「我做过什么」卡点具体现象带报错原文半年后还能被关键词搜到根因一句话写清真正原因区分「现象」和「原因」的能力就看这列知识点对应的技术名词把零散操作挂到知识树上填这张表时有个技巧卡点那列一定要抄原始报错不要自己转述成「数据库连不上」。原始报错是唯一能搜到答案的东西转述过的描述搜索命中率极低。根因那列如果当时没搞清楚就写「未确认」别硬编一个编出来的错误结论比空白危害大得多。排查顺序也固定下来了进程在不在 → 端口通不通 → 字符集对不对 → 依赖版本匹不匹配 → 权限够不够。这五步覆盖了那四周里八成的问题而且顺序是从外到内每一步都能把范围砍掉一块。看起来是笨办法但它不依赖灵感谁都能照着走一遍。从那以后我每接一个新环境都强制自己先跑一遍这个顺序哪怕一切正常也跑因为「正常」本身就是一条要记录下来的基线。有了基线下次出问题时才知道是哪一步变的。这份记录里那些当天看起来很蠢的操作恰恰是后来最常被翻回去看的部分。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站