DrillLab
第 04 / 09 节LESSON 04 / 09约 12 分钟~12 min

两个考试项目的目录,逐个说明The directory layout of both exam projects

哪些文件是你要改的,哪些是给好的,哪些是干扰项。Which files you are meant to edit, which are already done for you, and which are there to distract you.

2 个练习2 exercises地基 · 第 1 部分Foundations · Part 1
这一页有什么On this page6
学完这节你会After this lesson you can
  • 看懂两个 assessment 的完整目录结构Read the full directory layout of both exam projects
  • 分清「入口文件」「配置文件」「源码」「测试」各在哪Tell apart where the entry files, the config files, the source and the tests each sit
  • 知道 index.html → main.tsx → App.tsx → 组件 这条前端启动链Know the front-end start-up chain: index.html to main.tsx to App.tsx to the components
  • 认出项目里的干扰项Spot the files in the project that are only there to distract you
这在考试里考什么What the exam does with this

两个考试都明确标了「EDIT THIS」和「PROVIDED」。改错文件不加分;而找不到该改的文件会直接丢分。Both exams mark files as EDIT THIS or PROVIDED. Editing the wrong file earns nothing, and failing to find the file you were meant to edit loses points directly.

这节课要看的真实文件Real files this lesson looks at2 项 · 2 个可以展开看原文2 items · 2 can be opened
react-notes-app/Q1 在 src/,Q2 在 q2/Q1 lives in src/, Q2 in q2/
Textreact-notes-app/ · tree源项目From source
1react-notes-app/
2├── q2/
3│ ├── demo.ts
4│ └── taskRunner.ts
5├── src/
6│ ├── components/
7│ │ ├── NoteForm/
8│ │ │ └── index.tsx
9│ │ ├── NoteItem/
10│ │ │ └── index.tsx
11│ │ ├── NoteManager/
12│ │ │ └── index.tsx
13│ │ └── NoteTable/
14│ │ └── index.tsx
15│ ├── types/
16│ │ └── Note.ts
17│ ├── App.tsx
18│ ├── index.css
19│ ├── main.tsx
20│ └── NoteManager.test.tsx
21├── index.html
22├── package-lock.json
23├── package.json
24├── README.md
25├── tsconfig.json
26├── vite.config.ts
27└── vitest.setup.ts
Source: react-notes-app/
graphql-federation-practice/node-subgraph/ 和 java-service/ 两个服务Two services: node-subgraph/ and java-service/
Textgraphql-federation-practice/ · tree源项目From source
1graphql-federation-practice/
2├── java-service/
3│ ├── src/
4│ │ ├── main/
5│ │ │ ├── java/
6│ │ │ │ └── com/
7│ │ │ │ └── techflow/
8│ │ │ │ └── orders/
9│ │ │ │ ├── config/
10│ │ │ │ │ ├── CorrelationIdFilter.java
11│ │ │ │ │ └── MetricsConfig.java
12│ │ │ │ ├── controller/
13│ │ │ │ │ └── OrderController.java
14│ │ │ │ ├── dto/
15│ │ │ │ │ ├── CreateOrderRequest.java
16│ │ │ │ │ └── OrderItemRequest.java
17│ │ │ │ ├── exception/
18│ │ │ │ │ ├── EntityNotFoundException.java
19│ │ │ │ │ └── GlobalExceptionHandler.java
20│ │ │ │ ├── model/
21│ │ │ │ │ ├── Order.java
22│ │ │ │ │ ├── OrderItem.java
23│ │ │ │ │ └── OrderStatus.java
24│ │ │ │ ├── repository/
25│ │ │ │ │ ├── InMemoryOrderRepository.java
26│ │ │ │ │ └── OrderRepository.java
27│ │ │ │ ├── service/
28│ │ │ │ │ └── OrderService.java
29│ │ │ │ └── OrderServiceApplication.java
30│ │ │ └── resources/
31│ │ │ └── application.properties
32│ │ └── test/
33│ │ └── java/
34│ │ └── com/
35│ │ └── techflow/
36│ │ └── orders/
37│ │ └── OrderControllerTest.java
38│ ├── orders.db
39│ └── pom.xml
40├── node-subgraph/
41│ ├── __tests__/
42│ │ └── resolvers.test.js
43│ ├── src/
44│ │ ├── dataSources/
45│ │ │ └── orderDataSource.js
46│ │ ├── resolvers/
47│ │ │ └── orderResolvers.js
48│ │ ├── index.js
49│ │ └── schema.graphql
50│ ├── package-lock.json
51│ └── package.json
52├── QUESTIONS.md
53└── README.md
Source: graphql-federation-practice/
§01

react-notes-app 的完整结构The full layout of react-notes-app

这个项目不大,一屏能看完。下面每一行都真实存在 (node_modules 和 lockfile 省略):

要注意它有两道互不相干的题: src/ 是 Q1(React 界面),q2/ 是 Q2 (纯 TypeScript 的异步调度器,完全不涉及 React)。 很多人一开始会以为 q2 是 src 的一部分,白花时间找关联。

The project is small — it fits on one screen. Every line below really exists (node_modules and the lockfile left out):

Notice that it holds two entirely unrelated questions: src/ is Q1 (the React UI) and q2/ is Q2 (a pure TypeScript async scheduler, no React in it at all). Plenty of people assume q2 is part of src at first and burn time hunting for a connection.

Text目录结构(实测)Directory layout (as measured)已跑通Verified
1react-notes-app/
2├── index.html Vite 入口,里面只有一个 <div id="root">
3├── package.json 依赖 + dev/build/q2 三个 script
4├── tsconfig.json strict: true,include: ["src","q2"]
5├── vite.config.ts React 插件 + 内联的 vitest 配置(jsdom)
6├── vitest.setup.ts 一行:import "@testing-library/jest-dom"
7├── src/ ← Q1:Notes Manager
8│ ├── main.tsx createRoot(...).render(<App />)
9│ ├── App.tsx 只渲染 <NoteManager />
10│ ├── index.css 全局样式与 .card / .layout-row 等工具类
11│ ├── types/Note.ts type Note = { id, title, content }
12│ ├── NoteManager.test.tsx ★ 判卷用的 4 个测试
13│ └── components/
14│ ├── NoteManager/index.tsx ★ 状态所有者:notes[] + noteToEdit
15│ ├── NoteForm/index.tsx ★ 受控表单 + 编辑回填 + Add/Update
16│ ├── NoteTable/index.tsx 表格骨架 + map + tbody 的 testid
17│ └── NoteItem/index.tsx 单行 + Edit / Delete 按钮
18└── q2/ ← Q2:并发任务调度器(与 React 无关)
19 ├── taskRunner.ts ★ 要实现的 runTasks
20 └── demo.ts 测试台,打印实时并发数
1react-notes-app/
2├── index.html Vite entry point; holds one <div id="root">
3├── package.json dependencies + three scripts: dev/build/q2
4├── tsconfig.json strict: true,include: ["src","q2"]
5├── vite.config.ts React plugin + inline vitest config (jsdom)
6├── vitest.setup.ts one line: import "@testing-library/jest-dom"
7├── src/ ← Q1:Notes Manager
8│ ├── main.tsx createRoot(...).render(<App />)
9│ ├── App.tsx renders only <NoteManager />
10│ ├── index.css global styles plus .card / .layout-row helpers
11│ ├── types/Note.ts type Note = { id, title, content }
12│ ├── NoteManager.test.tsx ★ the 4 tests used for grading
13│ └── components/
14│ ├── NoteManager/index.tsx ★ owns the state: notes[] + noteToEdit
15│ ├── NoteForm/index.tsx ★ controlled form + edit refill + Add/Update
16│ ├── NoteTable/index.tsx table shell + map + the testid on tbody
17│ └── NoteItem/index.tsx one row + the Edit / Delete buttons
18└── q2/ ← Q2: task scheduler, no React involved
19 ├── taskRunner.ts ★ runTasks, the function to implement
20 └── demo.ts test harness; prints live concurrency
§02

前端项目的启动链:谁调用谁The start-up chain of a front-end project: what calls what

浏览器打开一个空 div,最后长出整个界面 —— 中间这几跳要看清。The browser opens an empty div and ends up with the whole interface. Follow each step in between.

npm run dev 之后,Vite 起一个服务器,浏览器请求index.html。之后是这样一条链:

  1. index.html 里有 <div id="root"></div>(空的)和一行 <script type="module" src="/src/main.tsx">
  2. main.tsx 找到那个 div,用ReactDOM.createRoot(...).render(<App />)把 React 应用「挂」进去。
  3. App.tsx 渲染 <NoteManager />
  4. NoteManager 渲染 <NoteForm /><NoteTable />,后者再渲染一堆<NoteItem />

为什么要知道这条链?因为它决定了「state 应该放在哪」。NoteFormNoteTable 是兄弟, 它们没法直接互相通话。要让「表单提交」影响「表格内容」, 数据只能放在它们共同的父亲 NoteManager 里。 这是 Q1 三道题的全部结构基础。

After npm run dev, Vite starts a server and the browser asks for index.html. From there it is one chain:

  1. index.html holds a <div id="root"></div> (empty) and one line of <script type="module" src="/src/main.tsx">.
  2. main.tsx finds that div and uses ReactDOM.createRoot(...).render(<App />) to mount the React app into it.
  3. App.tsx renders <NoteManager />.
  4. NoteManager renders <NoteForm /> and <NoteTable />, and the latter renders a pile of <NoteItem />.

Why does the chain matter? Because it decides where the state belongs. NoteForm and NoteTable are siblings, and siblings cannot talk to each other directly. For “submit the form” to change “what the table shows”, the data has to live in their shared parent NoteManager. That is the whole structural basis of the three Q1 tasks.

TSXsrc/main.tsx源项目From source
1import React from "react";
2import ReactDOM from "react-dom/client";
3import App from "./App";
4import "./index.css";
5
6ReactDOM.createRoot(document.getElementById("root")!).render(
7 <React.StrictMode>
8 <App />
9 </React.StrictMode>
10);
Source: react-notes-app/src/main.tsx
那个 ! 是 TypeScript 的非空断言:「我保证 getElementById 不会返回 null」。StrictMode 是 React 的开发期严格模式,它会故意把组件渲染两次来帮你发现副作用问题 —— 所以开发时看到 console.log 打两遍是正常的。The ! is a TypeScript non-null assertion. It means: I promise getElementById will not return null. StrictMode is React's strict development mode. It renders each component twice on purpose so that side-effect problems show up early, which is why a console.log prints twice during development.
§03

Federation 项目:两个服务,两个语言The Federation project: two services, two languages

这个项目的 README 直接标了哪些文件要改。只有两个。其余全是 PROVIDED(给好了,别动)。

This project’s README marks outright which files you edit. There are only two. Everything else is PROVIDED (given to you, hands off).

Text目录结构(实测)Directory layout (as measured)已跑通Verified
1graphql-federation-practice/
2├── README.md 任务说明,标了 EDIT THIS / PROVIDED
3├── QUESTIONS.md ★ 两道书面题,要写答案
4
5├── node-subgraph/ Node.js + Apollo Server 4(端口 4000)
6│ ├── package.json
7│ ├── src/
8│ │ ├── index.js PROVIDED:起服务器,每请求造 context
9│ │ ├── schema.graphql PROVIDED:GraphQL schema
10│ │ ├── dataSources/orderDataSource.js PROVIDED:3 个 mock 数据源
11│ │ └── resolvers/orderResolvers.js ★ EDIT THIS:4 个 TODO + 3 处埋雷
12│ └── __tests__/resolvers.test.js PROVIDED:10 个测试
13
14└── java-service/ Java 17 + Spring Boot 3.3(端口 8080)
15 ├── pom.xml Maven 依赖(web / validation / actuator / test)
16 ├── orders.db ⚠ 干扰项:没有任何代码引用它
17 └── src/
18 ├── main/java/com/techflow/orders/
19 │ ├── controller/OrderController.java ★ EDIT THIS:6 个 TODO
20 │ ├── service/OrderService.java PROVIDED:业务逻辑全给好了
21 │ ├── repository/ PROVIDED:内存仓库
22 │ ├── model/ dto/ exception/ config/ PROVIDED
23 │ └── resources/application.properties PROVIDED(书面题 2 会用到)
24 └── test/java/.../OrderControllerTest.java PROVIDED:5 个测试
1graphql-federation-practice/
2├── README.md task description, marks EDIT THIS / PROVIDED
3├── QUESTIONS.md ★ two written questions, answers required
4
5├── node-subgraph/ Node.js + Apollo Server 4 (port 4000)
6│ ├── package.json
7│ ├── src/
8│ │ ├── index.js PROVIDED: starts server, context per request
9│ │ ├── schema.graphql PROVIDED:GraphQL schema
10│ │ ├── dataSources/orderDataSource.js PROVIDED:3 mock data sources
11│ │ └── resolvers/orderResolvers.js ★ EDIT THIS:4 TODOs + 3 defects
12│ └── __tests__/resolvers.test.js PROVIDED:10 tests
13
14└── java-service/ Java 17 + Spring Boot 3.3 (port 8080)
15 ├── pom.xml Maven deps (web / validation / actuator / test)
16 ├── orders.db ⚠ a distractor: no code refers to it
17 └── src/
18 ├── main/java/com/techflow/orders/
19 │ ├── controller/OrderController.java ★ EDIT THIS:6 TODOs
20 │ ├── service/OrderService.java PROVIDED: all logic written
21 │ ├── repository/ PROVIDED: in-memory repository
22 │ ├── model/ dto/ exception/ config/ PROVIDED
23 │ └── resources/application.properties PROVIDED (question 2 uses it)
24 └── test/java/.../OrderControllerTest.java PROVIDED:5 tests
§04

认出干扰项Spotting the distractors

考试项目里经常有「看起来很重要但其实没用」的东西。Exam projects often contain things that look important but are never used.

  • java-service/orders.db —— 一个数据库文件。但 pom.xml没有任何 JDBC 或 JPA 依赖, 代码里也没有一处引用它。数据实际来自InMemoryOrderRepository(一个内存 HashMap)。 看到 .db 就去配数据源,是纯浪费时间。
  • schema 里的 @shareable —— 在 @link(import: [...]) 里被引入了,但全文没用到。
  • InventoryDataSource.getInventoryStatus() —— 存在,但没有任何 resolver 需要它。(getProductPrice()倒是必须用,后面会讲为什么。)
  • MetricsConfig.java —— 一个空的 @Configuration 类,里面什么都没有。

判断方法很朴素:顺着依赖和引用找。一个文件如果没有被 import、没有被注入、没有在配置里出现, 它就跟这次任务无关。

  • java-service/orders.db — a database file. But pom.xml has no JDBC or JPA dependency whatsoever, and not one line of code touches it. The data actually comes from InMemoryOrderRepository (an in-memory HashMap). Seeing a .db and going off to configure a data source is pure wasted time.
  • @shareable in the schema — it gets pulled in inside @link(import: [...]), then never used anywhere.
  • InventoryDataSource.getInventoryStatus() — it exists, but no resolver needs it. (getProductPrice() really is mandatory; we get to why later.)
  • MetricsConfig.java — an empty @Configuration class with nothing inside it.

The test for this is plain: follow the dependencies and the references. If a file is never imported, never injected and never named in config, it has nothing to do with this task.

练习Practice

动手做Get your hands on it

填空只是过渡。真正掌握的标准,是在没有答案的时候从头写出来 —— 所以做完 L2 之后一定要往 L3、L4 走。Filling blanks is a stepping stone. The real bar is writing it from nothing, so once L2 is comfortable, push on to L3 and L4.

L1认出来Spot itQ1 的 state 应该放在哪个文件Which file should hold the state for Q1

Q1 要求「表单提交后,新笔记出现在表格里」。表单是NoteForm,表格是 NoteTable,两者是兄弟。 那份笔记列表数据应该存在哪个组件里?

Q1 asks that a new note appears in the table after the form is submitted. The form is NoteForm, the table is NoteTable, and the two are siblings. Which component should hold the list of notes?

先选一个选项Pick an option first
L1认出来Spot it哪个是干扰项Which one is the distractor

Federation 项目里,下面哪个文件与你要完成的任务完全无关?

In the Federation project, which of these files has nothing at all to do with the task you have to finish?

先选一个选项Pick an option first
迁移Transfer

换一道题也能用Works on other problems too

考试不会原题重考。真正能带走的是「看到这种信号 → 伸手去拿这个解法」。The exam will not reuse the same question. What you take away is the reflex: see this signal, reach for that solution.

两个兄弟组件要共享数据Two sibling components need to share data
把 state 提升到共同父组件Move the state up into the parent they share
不确定某文件要不要改You are not sure whether a file should be edited
看 README 的 EDIT THIS / PROVIDED 标注Check the EDIT THIS / PROVIDED labels in the README
看到一个可疑的资源文件You find a file that looks suspicious
搜一下有没有代码引用它,没有就是干扰项Search for code that references it. If nothing does, it is a distractor
这节的要点What to take away
  1. react-notes-app 有两道独立的题:src/ 是 React 的 Q1,q2/ 是纯 TS 的 Q2。react-notes-app holds two separate questions: src/ is the React Q1, q2/ is the plain TypeScript Q2.
  2. 前端启动链:index.html → main.tsx → App.tsx → NoteManager → 子组件。The front-end start-up chain: index.html, main.tsx, App.tsx, NoteManager, then the child components.
  3. 兄弟组件不能直接通话,所以共享数据必须放在共同父组件里。Sibling components cannot talk to each other directly, so shared data has to sit in the parent they share.
  4. Federation 项目只有两个文件要改:orderResolvers.js 和 OrderController.java。The Federation project has only two files to edit: orderResolvers.js and OrderController.java.
  5. orders.db、@shareable、getInventoryStatus、MetricsConfig 都是干扰项。orders.db, @shareable, getInventoryStatus and MetricsConfig are all distractors.

接下来What next

  1. 把这一节的练习做掉Do this lesson’s exercises2 个,就在这一页上面 —— 别攒着最后一起做2 of them, further up this page — do not save them for later
    回到练习 ↑Back up to them ↑
  2. 接着看下一节Continue to the next lesson数组与对象:不可变更新三件套Arrays and objects: three ways to update without changing the original
    下一节Next lesson
读完并且做过上面的练习了吗?Read it and worked through the exercises above?
上一节:Previous: npm scripts:命令到底跑了什么npm scripts: what the command actually runs