很好,你已经学会了 Vue、Rust、Actix、GitHub Actions、pnpm、Vitest、Caddy
现在你可以使用 React、Python、Django、GitLab CI、yarn、Jest、Nginx 来完成软工作业了(
(其实这里面比较有问题的就只有 React,其他都还好,只是数量多凑一点 meme 效果更好)
小作业
Django 后端小作业
每一处需要修改的代码都列出来了,还有详细的注释告诉你要干什么。Django 完全没学,就根据提示查了一处文档,代码的整体逻辑也没搞清楚。
感觉写完之后理解了 ChatGPT,看着注释和示例补全代码根本就不需要学会如何写代码嘛(
学 React
小作业应该不需要怎么学 React,但还是把 Learn React (Beta) 完整过了一遍。
之前都在 Twitter 上云 React,学了之后终于有喜欢 Vue 的自信了(所以说 X 自信就是要允许 Y 才能有,以 X 自信为名打压 Y 到底是什么操作
React (Next.js) 前端小作业
上来就看到 ESLint 是 4 空格缩进、优先双引号,这能忍吗(。特意去确认了一下,除了前端助教参与的几个项目,随便找一个项目都是 2 空格缩进、优先单引号(
再一写代码,发现不对劲,这个 ESLint 有啥 rule 啊,怎么怎么写都没 lint error,给我整不会了(
于是直接上了 @
,理论上来说我应该整一个给 tsx 用的 config,但 ts 的 config 配合上小作业模板原有的应该也凑合着能用吧(
写完后的感觉和 Django 小作业差不多,React 都白学了,啥都不会就能写完(
这是你前端小作业的最后一个 Step 了,如果你已经完成了,我相信你已经充分掌握了 Next.js 的入门知识。
事实上这份前端小作业几乎可以作为大作业的起始框架。除了 Next.js 框架本身,其包含了:
- 代码风格检查与修正插件 ESLint,配置文件为
.
eslintrc . json - 单元测试框架 Jest
不是,你是认真的吗,先不谈学会多少能算是《充分掌握了入门知识》,这个 eslintrc 真的能用吗(
第二天早上起来再看了下代码,发现组件 unmount 时没有 clearInterval
,而这需要在允许(推荐)的代码范围之外进行修改,怎么回事(
网络请求也是,unmount 时没有 abort / ignore,但要改的话交互逻辑也会有些变化,就懒得修了,一个简陋的小作业而已(
CI/CD 小作业
前一天的 Power TUNA 上还有人问为什么 TUNA 有很多海外流量,第二天就看到了在 Dockerfile 里硬编码 TUNA(
为子目录设置 commit 邮箱
作业经常会需要(或者说最好需要)使用一些与平时不同的 commit 邮箱,而 SECoder 的邮箱验证似乎还坏了,就更需要了。
以前碰到这种需求我都在每个 repo 设 local git config,但在软工可能有好几个 repo,不仅麻烦而且可能忘记,就需要更好的解决方案。
其实简单搜一下就很容易搜到,毕竟这是个很常见的需求:
-
在
~/.
中添加gitconfig [includeIf "gitdir:~/some/path/**"] path = ~/some/path/.gitconfig
-
在
~/
中添加some / path /. gitconfig [user] email = [email protected]
小作业的一些锅
deploy 之后发现一堆锅,它们不影响我的小作业得分,只会影响我能不能交上数学课作业,但我还是修了,数学课作业则是没写完就交了(
说到这就得怀念程设训练了,还有 5 分的“课堂参与”,额外加在 100 分之外,回答几个同学的提问、开一些 issue 帮忙修锅就拿满了,虽然最后这 5 分对 A+ 毫无帮助,要是能移到软工就好了。
Next.js static HTML export 有 dynamic route 时报错
在 Next.js 13.0.7 之前,具体来说是 13.0.7-canary.5 之前,通过 next export
得到 static HTML export 后,如果访问一个能匹配上 dynamic route 的 static route,会报“Application error: a client-side exception has occurred”。例如,有 pages
和 pages
时,访问 /list.html
就会报错。
简单看了一下这个版本的 changelog,有几个疑似和这个 bug 相关的,但我没能确认具体是哪个。本来想在本地 build Next.js 来进一步确认,但我一 build 就报一万个类型错误,搞了半天也没搞好,就放弃了。反正 bump 一下就修好了,开课前为啥不 bump 一下啊。
Next.js static HTML export 的 Nginx 路由配置
感觉网上搜到的一些 Nginx 路由配置都很迷惑:$uri
是匹配不到 HTML 文件的,需要 $uri.html
;$uri/
则可能匹配到目录,导致 403;动态路由如果不专门写路由配置的话,就不能直接访问到正确的 HTML,static HTML export 的作用就没有了,而且,fallback 到 /
可能导致加载一些不需要的页面内容和 JS,实际上比直接 SPA 更劣。
用户名格式检查
说来惭愧,我一开始也没意识到用户名没校验格式的问题,还是配路由的时候才想到用户名里会不会有 slash。
现在要修的话确实麻烦,所以只要归结为《由于小作业的简化》就好了(
忘记关测试用 Docker container
第二天早上起来一看,Plausible 特别卡,再一看,load avg 都快两位数了,原来是在 VPS 上测试后端部署忘关了 🌚 不要随便在生产环境顺便测小作业(
玩小作业
把 game of life 当成冬日绘板画画,动态更新时钟,从 LifeWiki 获取随机 pattern,这是一个作业写不完的人能干的出来的吗,共享后端是这样用的吗(
第二天早上起来一看(怎么每段都有个第二天早上起来),大家都在画些啥,我还是太保守了(
回答同学提问
软工的公开提问貌似只能在微信群,而且助教有十几个,回复速度实在是太快了。程设训练的时候我还能偶尔抢在助教之前回答问题,现在根本做不到(
偷窥同学代码
偶然间发现 SonarQube 上可以看到同学的代码,因为我的小作业已经交了而且分都给了,所以我就放心大胆地偷窥了(
本来想给助教报告一下问题等权限修复,结果说是懒得修了,导致我甚至不能在迟交 ddl 之前在博客吐槽(要是下学期还不修,大家都学会了这个操作,那只能说不是我的锅了),那我只好趁机来认真看一看同学们都写了一些什么奇妙代码(
-
冗余的类型标注(看到文档里讲 TS 的时候举例全是冗余类型标注时,我就知道同学们肯定会写一万个
let count: number = 0
) -
只需条件修改 prop 时,条件渲染 prop 不同的两个组件
-
手写八遍而不是使用
for
来遍历网格的邻居 -
更新 React state 时即使结果是常量依然使用回调函数
-
回调函数写成
() => { foo(); }
而不是foo
-
冗余的
flex
- direction : row -
复制二维数组,有看到用
for
一个个push
的,也有看到直接JSON
的;正确写法应该是. parse ( JSON . stringify ) old
。数组大小为 1000x1000 时,在 Firefox 中,这两种写法的用时都大约是. map (( row ) => row . slice ()) map
&slice
的 10 倍,在 Chrome 中,一个个push
的用时是map
&slice
的 3 倍,JSON
的用时是 40 倍。而且,由于小作业中需要的是复制其他所有,但修改一个,很多同学在. parse ( JSON . stringify ) for
里面写if
,这样效率更低,应该把if
写在for
外面。但是还看到一种写法,只深复制修改了的那一行,其他行浅复制。草,这样确实性能更好。
-
依赖于效果为 toggle 的按钮不会在 toggle 完成前被点击两次:
- 问题背景:一个 start 按钮和一个 stop 按钮,修改一个
playing
的 state,处于 playing 状态时需要一个setInterval
- 问题 1:在 start / stop 时将
playing
修改为!playing
而非true
/false
- 问题 2:只在 stop 时
clearInterval
,没有在 start 时clearInterval
正常的交互很难做到 toggle 完成前被点击两次,基本上只能通过在 console 连续调用两次
click
实现,但最好还是不要依赖于这一点。 - 问题背景:一个 start 按钮和一个 stop 按钮,修改一个
Node.js 和浏览器的不同 timer
队友问了一个神奇的 bug,说是 clearInterval
clear 不掉,我看了半天都没发现问题,最后发现开头有个 import
。
之前还真没注意过,timer 有 Node.js 和浏览器两个版本,Node.js 的 setInterval
返回的是一个 object,浏览器返回的是一个 number
。
- 直接写
setInterval
时,在 TypeScript 中会被认为返回NodeJS
,而实际运行时在哪运行就用哪个版本的函数。. Timer - 如果写的是
window
,在 TypeScript 中会被认为返回. setInterval number
,在 Node.js 中运行会报错。 - 如果是
import
,即使是在浏览器中运行也会使用 Node.js 版本的函数。{ setInterval } from ' timers '
这里比较重要的是 setInterval
和 clearInterval
要配套,不能一个用 Node.js 的一个用浏览器的,否则会 clear 不掉甚至报错。
一般来说直接写(不 window.
也不 import from 'timers'
)就是最好的,虽然看起来 TypeScript 会将 number
误认为是 NodeJS
,但这个 number
本来就不该被当作一个 number
来使用,这样的 typing 反而是好的。
这里如果再仔细研究一下可以单独写一篇博客了,但还是算了吧(
小作业即将截止
小作业 将于 3 月 16 日(周四)23:59 截止。
请注意截止时间不是 21:55。
……
后面省略的几句也是模仿的 DSA,DNS 前几天某位助教答疑的时候把 dns typo 成了 dna DNA 动了,好评,但开头这个对偶的 ddl 绷不住了(
大作业
组队
在另外一个通识课上体验了一下差点没组上队(虽然后来得知还有不少人比我更晚组上队),最后完全 rand 组队。所以真的很感谢上学期就找我来组队的队友。
起队名
HardToName / 起个名好难队
这是什么引用啊
队友提出了三个队名:啊对对队、BUG 全调队、[redacted] 队。前两个都被占了,后一个太魔幻了,于是最后还是用的我提出的队名:lazy / 这个队特别懒,什么都没留下~
(这 SECoder 怎么还在用这个很有年代感的个人描述 placeholder 啊。)
俗话说得好,贱名好养活,取名叫 lazy 一定摆不起来(
其实本来还想过利用五个人的名字缩写之类的组一个队名,但人脑去想一个符合要求且有意思的队名太难了,输入法给出的候选词也没啥意思,所以我还尝试了用 ChatGPT 来生成,本来只是以为它会生成一些没意思的队名,没想到它生成出来的队名不仅没意思,而且拼音首字母甚至字数都完全不符合要求,无论怎么拷打都认错不改 🌚
分工 & 技术栈
听说代码可能要求有队友审核,所以放弃了一个人写前端(可能来个人写测试)的想法(
几乎是理所当然地就决定了用 Vue,没有一点反对,感觉这个不需要学 React 的 React 小作业没有九分也有十分的功劳。
给队友布置学习任务的时候感觉,我好像也不会多少东西,像是前端的测试以及 Nuxt 都还得学,JS/TS/Vue 说到底也就那么点东西,我的优势可能主要还是在经验方面,但其实也没写过什么复杂的前后端交互,大作业和博客 / Codle 这种东西还是差别挺大,写这个博客的时候很多时间也是花在一些 blog-specific 的不重要的细节上。说的我都有点慌了(
咕了一周的 Nuxt 3.3 终于在大作业开始前发出来了,避免了刚开始写不久就要 bump minor version。yysy 我选的这些技术栈还是挺最新最热的,要是真的出锅了都不知道怎么向队友交代(
紧接着就看到 TypeScript 5.0 也发了,更新更热了,害怕(
摇号
3月14日24:00后进行项目志愿的摇号
结果还真是“24:00后”,手动 ddl 🌚
本来晚上 11 点还看到我们队的一志愿的一志愿数量从 7 变成了 6,就是 2/3 的概率抽上,结果早上起来一看还是掉了,寄。这下和不是队友的室友一个选题了。好在例会还是 4-3,要是 4-4 的话整个下午就消失了(
大例会 1
第一次例会竟然就要写分工以及完整的进度安排,虽然说是随便画饼也没事,但还是感觉不太敏捷(
顺便练习了一下 Slidev,在本机莫名 export 失败了,结果扔到 VPS 上就成功了,神秘。但在 VPS 上还得装上中文字体,并且配字体设置。
无奖竞猜,下面这些队名都是啥:
下午的展示顺序是
- 我
- 蛋白质
- 迷
- 对对对
- 懒
感觉这个第一次例会总体上传达的意思就是,看起来难的功能要想简单点,差不多得了(
大作业第一周
需求细化
第一次例会上是我提的问题最多,之后也基本上只有我在群里问需求细节问题。回答大部分都是两种之一,要么是“这样也可以,但那样也可以”,要么是“这样更好,但没分”。程设训练的时候也是这样,似乎是我的问题(,正常的想法是不是应该是只要不说清楚就可以混过去,而不是害怕没搞清楚而写错(
四个阶段
第一周总体上分成四个阶段:
- 设计任务计划,编写让队友知道要干什么的文档和 issue
- ping & push 队友
- 回答队友提问 & 指出队友代码里的问题
- 马上就要例会了,放弃指导队友,上手自己写(
感觉最后总的工作量还是超过了自己一个人把所有活干完(,希望以后能好一点,要一直是这样其他课真寄了。而且我本来就没打算管后端,就没学 Django,结果不仅要搞一点后端的规划设计,回答队友提问,还得赶着 ddl 硬上写代码,开发环境都没配好。
数据库配置
你见过只能挂载目录,不能设置环境变量,也不能挂载单个文件的 Docker 容器管理吗(
这个 SECoder 实在是有些过于离谱,最后只能把环境变量写在 Dockerfile 里,用 build 出来的数据库镜像。
小例会 1
主要就是聊了一下数据库和 API 的设计,也没检查 CI/CD,结束之后才想起来架构设计也没说。毕竟是小例会,确实比较轻松,感觉上午白着急把数据库跑起来了(
大作业第二周
SECoder SSH 卡住
之前就偶尔碰到 SECoder SSH 卡住的情况,今天发现是回了寝室连有线网就卡住,换成 Tsinghua Secure 就不卡了,怀疑和 IP 切换有关,不是很懂。
Nuxt 与 TypeScript 5.0
之前还说有点太新太热了,果然一堆锅。
之前一直随机报错 The "path" argument must be of type string. Received an instance of Array
,后来发现是我直接用了最新最热的 tsconfig 里 extends
写数组,然后 Nuxt 读取 tsconfig 的时候寄了。改成单个 extends
或者把 Nuxt 更新到 edge channel 就 ok 了。
另外还碰到 一个 bug,只能把路径改成英文。
npmmirror 更新不及时
CI 挂在了奇怪的地方,是 npmmirror 更新不及时,一个刚更新几分钟的包下不下来。npmmirror 的首页写的是每 10min 更新一次,但我等了它半小时还没更新。然后看到 npmmirror 上每个包的页面上有一个“SYNC”按钮,点一下就可以更新,草(
pnpm v8
感觉这个软工就和最新最热扯不开关系了(
碰到一个非常神秘的,一个 up-to-date with master 的 MR 在合并之后挂 CI,说是 lock file 有问题。本来我都没想管了,结果晚上发现博客也挂 CI 了,再一看,原来是 pnpm 发布 v8 了,草。
nuxt dev 报错 “Body is unusable”
Body is unusable (undici) · Issue #19245 · nuxt/nuxt 是一个非常玄学的 issue,我在部署到 SECoder 的时候遇到了,队友在 WSL2 上遇到了,但我在本地(Arch Linux)没遇到过。我让队友试着在 Docker 里跑 dev,似乎和 issue 里描述的一样,偶尔会遇到,但重试一下就好了;而且 WSL2 里加载非常慢,直接在 Windows 上运行 Docker 会快很多。
HMR 需要 WebSocket,于是在 Docker 里跑 dev 的时候发现 Vite 默认的 ws 端口是 24678,唤醒了一些远古的回忆(;结果去搜索引擎确认 自己的 DNA 没有刻错 的时候搜到的全是“原神‘港口驶过几艘船,二四六七八’任务攻略”,绷不住了(
大例会 2
例会前 CI 一直在排长队,一开始还只用多等一会儿,后来甚至会等待超时而直接挂掉,差点没部署上(
最后发现我们部署的 dev 环境可用 prod 环境不可用,直接用的 dev 环境做演示,没人问为什么地址里有个 dev(;后来发现确实是暴力改掉 migration 后数据库没重启的问题,但我是一开始试图重启了但还是挂,以为是其他问题,但实际上是 SECoder 的容器重启需要等待,而且不知道为什么有时候等好久也没重启成功,非常玄学。
其他队都看上去做了很多功能,我们能演示的只有注册登录。虽然我们的注册登录确实相对来说更加完善,而其他队的其他功能也一堆问题,但感觉在差不多得了的氛围下,似乎还是赶上进度更重要。
大作业第三周
Nitro proxy 与 set-cookie
说来实在是绷不住。
我在一个未登录时返回 403 的 API 设置 CSRF cookie,在前后端对接测试时发现这个 set-cookie
在经过前端 proxy 之后就没了,于是我以为是 Nitro 的 proxy 不转发 set-cookie
,又马上要例会了,就赶紧换成了 Nginx 反代。
例会结束后,我从 Nitro 源码翻到 h3 源码,再翻到 undici 的 issue,发现 Undici strips out set-cookie headers, even when "credentials: 'include'" is set,又用 httpsset-cookie
,还以为找到原因了。但又发现 feat: remove headers filtering,感觉很奇怪。
于是我再仔细看了一下,发现 httpsset-cookie
了。而一开始那个 API 没有返回 set-cookie
则是因为它返回的不是 200,改成 200 就正常了。但凡我测试了一个返回 200 的也不至于..
上午因为这个问题差点没在例会前部署好,例会后一直研究到晚上,几乎是搞了一整天,破防了(
姓游就要用尤大的 Vue,yóu だけにっ!
突然发现另一个队长姓游的队也用的 Vue,而其他队暂时没发现有用 Vue 的(我暂时只找到了 1/4 队伍的域名,剩下有找不到的也有懒得找的)。难道是因为和尤大一样姓 yóu 吗,这下爱姐狂喜了(
undici 发送带 Content-Length: 0
的 DELETE 请求
这是第 114514 次被 undici 背刺了,具体请看 Sending a DELETE request with "Content-Length: 0" fails with a RequestContentLengthMismatchError · Issue #2046 · nodejs/undici。
而这个 Content
是由已经没在维护的 http-proxy
加上的。不知为何,甚至不是给其他 method 加 Content-Length 的时候忘记排除 DELETE 了,而是专门给 DELETE 加的。更神秘的是,这段代码是在十年前只有一百多行的 initial commit 就加上了。反正给这段打个 patch 就好了。
SECoder commit 统计
之前还在想 SECoder 的数据统计什么时候开,没想到一开吓一跳(
它似乎是统计了所有 commit,包括没 merge 到 master 的以及被 force-push 覆盖掉的,结果我差不多到了 rk2 的两倍(,但要是只算 master 的话其实进 top10 都还差一点。应该主要是搭前端代码框架的时候,有 20 多个 commit rebase 了好几次,后来也经常 force-push 修小的 typo。这么说来 GitHub 只统计 master 还是挺好的,虽然我个人项目也经常会在 master 上疯狂 force-push(
但是 SECoder 没有 MR 评论数量的统计,其实我觉得这个才是我应该霸榜的地方(
大作业第四周
期中周事比较多,稍微有点拖,于是寄了。
整体开发过程中存在大量开发进度不协调的问题:前后端对接不协调,开发新功能与审核、测试之间不协调,没有按测试需要的拓扑顺序进行开发导致先开发的功能要等着后面的功能才能测试,对开发计划认识不统一,还没修完当周计划的锅就开始写之后的功能……
我本来以为后端搭起基本框架之后我就不用管了,可以让队友自己搞,结果到了最后才发现全是锅。虽然队友有各种奇妙操作,但不得不说很多都是我可以提早发现来避免的,还有很多是沟通上出了问题,并且我自己的活也干得有点晚。这毕竟是软工而不是大号程设训练,只能承认,我软工是真的菜,做好规划安排以及与人沟通协作的基本能力非常欠缺。
要是有万能的喵森来协调大家的开发进度就好了;软工中的总集篇:要是部署挂了赶紧换一个旧版镜像,可以说是没写完而不是部署挂了,就可以只扣 1 分而不是 2 分了。 突然想到的,但这么一想,好想在软工结束后的暑假二周目一下白箱,现在先拜一拜喵森吧(
只不过还有一个问题,Python 实在是,真的能比 Rust 写的快吗(,虽然看队友写了很多奇妙错误,但要是没有类型检查之类的(Python 可以一定程度上检查,但看起来队友并没有做)我大概也能写出不少 🌚
大作业第五周
Nuxt v3.3.3 起 nuxt-vitest 报错
Nuxt v3.3.3 causes tests to fail (Unhandled Errors) · Issue #122 · danielroe/nuxt-vitest
想着大例会结束后是升级依赖的好时机,结果一 bump 测试挂了。去 git bisect 了一下 Nuxt,Nuxt 的构建比 Next 轻松多了,又快又不报错。bisect 出来之后本来还在想没时间也没头绪继续研究下去了,这个 issue 也已经一周了,不知道作者会不会继续咕着。结果晚上一看,被点了个心,开了个 PR,再一看,原来 bisect 出来的那个 PR 就是 nuxt-vitest 的作者写的,我都没发现(
NSpace 内 key 会失效
<n-space>
会给每个 child 套一个 <div>
,于是 key
都失效了,如果在 <n-space>
里面用 v-for
就可能不停地 remount,调了一晚上(
这东西是真不如手写 flex。
小例会 3
比较水,只不过助教说他当年搞了个云服务器,出问题了就把反代换成云服务器,感觉很有道理(
大作业第六周
大家这周都比较摆,我还去修了不少后端的锅。太久没管后端,对代码复用、测试的完善程度、QuerySet 的编写、请求参数的校验等问题感到了一些震惊(,但确实是没力气去修(
响应式设计还作为自选需求被提了出来,要是打算加这个需求一开始不就应该有吗,这也不是什么需要由同学想到的功能,我看需求列表没有就默认也不会作为自选需求了,刚开课的时候还找助教吐槽没有响应式分数。响应式设计要 mobile first,能这么晚加吗。
劝大家还是别太信需求列表,该卷还得卷,自己提自选需求总比快结束了被别人提强太多。
大作业第 7~9 周
草,我有软工,没有能加分的课,为什么要打智能体(
《GIF 图片搜索网站》
来点 PRJ2 笑话:GIF 图片搜索网站,每个队都是最后两三周才开始写搜索功能(
大作业后面几周
没力气写游记了,这个课快从世界上消失吧(