跳到主要内容
2026-07-13 · 一段与 batype 的对话(AI) · 由提问者公开

如何学懂一门技术?

回答由 AI 基于 batype 的笔记生成,不代表本人实时观点。

你平时怎么学技术?

我一般是先搞懂原理,再动手验证,最后整理成笔记。

具体来说我会这样学:

  1. 先看问题本质
    先问自己:它解决什么问题、为什么会有这个设计。

  2. 再看官方文档或权威资料
    先把概念框架搭起来,不急着记细节。

  3. 直接写小例子验证
    能跑起来、能复现,理解会快很多。

  4. 记录成自己的话
    我更偏向把知识写成“为什么”和“什么时候用”,而不是只抄 API。

  5. 过一段时间回头看
    真正记住的是反复用过、反复解释过的内容。

如果你愿意,我也可以直接按我学 React / TypeScript / 浏览器原理的方式,给你拆一套更具体的学习路线。

AI 推断
你怎么挑学习资料?

我一般按“能不能帮我快速建立正确模型”来挑,不按“讲得热不热闹”来挑。

我会优先看这几类:

  1. 官方文档 / RFC / 源码 / 原作者文章
    这是第一手资料,最接近事实。

  2. 能讲清原理的长文或教程
    我看重的是它有没有把“为什么”讲透,而不只是“怎么用”。

  3. 有完整例子、能自己验证的资料
    能跑、能复现、能对照结果的,我更信。

  4. 观点稳定、更新及时的资料
    技术变化快,过时内容会直接带偏。

我会避开这几类:

  • 只堆结论、不讲过程的
  • 纯标题党、碎片化但没上下文的
  • 只教“技巧”,不解释边界和代价的
  • 和当前版本明显不一致的旧文章

我的筛选顺序通常是:

  • 先官方
  • 再源码 / 规范
  • 再高质量博客
  • 最后才是视频、短内容

如果你想,我可以顺手把我选学习资料的“检查清单”列成一版,平时直接照着筛。

AI 推断
可以

可以,我会用这份清单筛资料:

先看可信度

  • 作者是否真正做过相关项目,或参与过该技术
  • 是否引用官方文档、RFC、规范、源码
  • 结论能否被其他独立资料交叉验证
  • 发布时间和适用版本是否明确

再看内容质量

  • 是否解释了“为什么”,而不只罗列 API
  • 是否交代使用场景、限制和代价
  • 示例是否完整、可运行、可复现
  • 是否区分事实、经验和个人观点
  • 是否覆盖失败情况与边界条件

最后看学习效率

  • 内容结构是否从问题出发
  • 能否帮我建立整体模型
  • 是否包含最小示例,而不是一上来就搭大项目
  • 信息密度是否合适,有没有大量重复和铺垫
  • 学完后能否用自己的话解释,并独立实现一遍

快速排雷

出现这些情况,我通常会换资料:

  • 标题很绝对,如“彻底掌握”“一篇精通”
  • 只给结论,不提供依据
  • 代码无法运行,或省略关键上下文
  • 用旧版本解释新版本行为
  • 把某种工程选择说成唯一答案
  • 大量复制其他文章,却没有验证和修正

实际使用时,不需要给每篇资料做完整评审。我通常先用官方文档确定事实,再找一篇讲解建立理解,最后写一个最小 Demo 验证。三者对不上时,以当前版本的规范、源码和实际运行结果为准。

AI 推断
你怎么分辨旧版本坑?

我主要靠“版本、时间、运行结果”三件事分辨,不能只看文章写得像不像真的。

1. 先确认资料对应的版本

重点找这些信息:

  • 发布时间和最后更新时间
  • package.json、CDN 地址里的版本号
  • 安装命令是否锁定版本
  • 文中使用的 Node.js、浏览器或框架版本
  • API 属于稳定版、实验版还是已废弃

没有版本信息的教程,我只拿来理解思路,不直接照抄。

2. 对照当前官方文档

我会重点查:

  • Migration Guide
  • Changelog / Release Notes
  • Deprecated 标记
  • Breaking Changes
  • RFC 或升级指南

尤其是跨大版本,例如 React 17 → 18、Webpack 4 → 5,这时默认存在不兼容,不会假设旧代码还能直接用。

3. 识别旧写法的典型信号

例如 React 资料里出现下面这些内容,我会立即检查版本背景:

ReactDOM.render(<App />, document.getElementById('root'))

React 18 通常改成:

import { createRoot } from 'react-dom/client'

createRoot(document.getElementById('root')).render(<App />)

其他常见信号包括:

  • API 已被标记为 deprecated
  • 配置字段在当前文档里搜不到
  • 示例依赖很老,却没有锁版本
  • 使用已经停止维护的插件
  • 报错信息与教程完全不同
  • 教程依赖某个旧版默认行为

4. 建最小项目实际验证

我不会直接把旧教程塞进业务项目,而是先建一个最小 Demo:

npm create vite@latest version-check -- --template react-ts
cd version-check
npm install
npm run dev

然后逐段加入教程代码,观察:

  • 安装时有没有 deprecated warning
  • TypeScript 是否报错
  • 开发环境和生产构建是否都通过
  • 控制台有没有警告
  • 行为是否与文章描述一致

5. 检查依赖的真实状态

常用命令:

npm view react version
npm view react versions
npm outdated
npm ls

还会看 npm 发布时间、GitHub Release、维护频率和 issue。文章发布日期新,不代表里面的依赖新;有些文章只是转载或重新发布。

6. 区分“过时”和“错误”

旧资料不一定没价值。我会分三层看:

  • 原理仍然成立:可以继续学,例如 Event Loop、Fiber 的设计动机。
  • API 已变化:保留理解,代码按新文档重写。
  • 底层模型已变化:整篇需要重新评估,不能只替换 API。

我的最终判断标准很简单:当前官方资料负责定版本边界,最小 Demo 负责验证实际行为,旧文章只负责提供理解路径。

AI 推断
旧教程怎么快速验真?

我会用“查版本 → 对文档 → 跑最小 Demo → 验生产构建”四步快速验真,通常十几分钟就能判断教程值不值得继续看。

1. 先找版本锚点

从教程里找:

  • package.json
  • 安装命令中的版本号
  • API、配置字段和 import 路径
  • Node.js、框架、构建工具版本
  • 文章发布时间与最后更新时间

如果完全没有版本信息,我只参考思路,不直接复制代码。

2. 搜当前官方文档

直接搜索教程里的关键 API:

site:react.dev API名称
site:vite.dev 配置项名称
site:typescriptlang.org API名称

重点检查:

  • 当前文档是否仍存在
  • 是否标记 deprecated
  • import 路径是否改变
  • 默认行为是否改变
  • Migration Guide 是否提到它

搜不到不一定代表错误,也可能是内部 API、旧 API,或者教程本身写错了。

3. 建最小 Demo

不要先放进真实项目。用当前版本单独验证:

npm create vite@latest verify-demo -- --template react-ts
cd verify-demo
npm install
npm run dev

只复制教程中最核心的代码,不复制整套工程。这样报错时更容易判断是 API 过期,还是业务代码干扰。

4. 同时验证“旧版”和“当前版”

如果教程注明了依赖版本,我会先复现原环境:

npm install react@18.2.0 react-dom@18.2.0

然后再升级测试:

npm outdated
npm install react@latest react-dom@latest

判断结果可以分成三种:

  • 旧版也跑不通:教程本身可能缺步骤或写错了。
  • 旧版能跑,当前版不能跑:教程当时正确,但已经过时。
  • 两个版本都能跑:继续检查行为和生产构建,不能只看“没有报错”。

5. 检查警告和构建结果

至少跑这几项:

npm run dev
npm run build
npm ls
npm outdated

重点看:

  • deprecated warning
  • TypeScript 类型错误
  • peer dependency 冲突
  • 浏览器控制台警告
  • 开发环境正常但生产构建失败
  • 页面结果是否真的符合教程描述

6. 最后做一次行为断言

“页面能打开”不等于教程正确。我会给核心行为加一个最小测试或明确输出:

const result = tutorialFunction(input)

console.assert(
  result === expected,
  `expected ${expected}, got ${result}`
)

我的快速判断标准是:能锁定原版本复现、能从官方变更记录解释差异、能在最小 Demo 中得到预期行为。缺任何一项,我都不会直接把旧教程的代码带进项目。

AI 推断
按我学 React / TypeScript / 浏览器原理的方式,给你拆一套更具体的学习路线。

可以。我建议不要把 React、TypeScript、浏览器原理拆成三门孤立课程,而是按“先会用 → 追调用链 → 做最小实现 → 回到项目验证”推进。主线做一个 React + TypeScript 项目,遇到问题再下钻浏览器和框架原理。

第一阶段:补齐 JavaScript 与浏览器执行基础

目标不是刷语法,而是能解释代码为什么按这个顺序运行。

学习内容

  1. 执行上下文、调用栈
  2. 作用域、闭包、this
  3. Promise、async/await
  4. Event Loop
  5. DOM 事件传播
  6. HTTP 请求与浏览器渲染的基本流程

必做实验

console.log('start')

setTimeout(() => {
  console.log('timeout')
}, 0)

Promise.resolve()
  .then(() => console.log('promise 1'))
  .then(() => console.log('promise 2'))

console.log('end')

不要只背输出顺序,要能解释:

  • 同步代码为什么先执行
  • Promise 为什么先于 setTimeout
  • microtask 在哪个阶段清空
  • 每一步调用栈和任务队列里有什么

阶段产出

自己画一张图,完整说明:

JavaScript 代码
  → Call Stack
  → Web APIs
  → Task / Microtask Queue
  → Event Loop

如果画不出来,说明还只是记住了结论。


第二阶段:TypeScript 从工程使用开始

TypeScript 不要一上来就钻类型体操。先解决“如何为真实数据建模”。

学习顺序

  1. 基础类型和类型推断
  2. interfacetype
  3. 联合类型、交叉类型
  4. 类型收窄
  5. 泛型
  6. keyoftypeof、索引访问类型
  7. 映射类型、条件类型
  8. infer
  9. 常用工具类型的手写实现

先做一个数据模型

type Status = 'idle' | 'loading' | 'success' | 'error'

type User = {
  id: number
  name: string
}

type UserState =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: User[] }
  | { status: 'error'; error: Error }

function renderState(state: UserState) {
  switch (state.status) {
    case 'idle':
      return '等待加载'
    case 'loading':
      return '加载中'
    case 'success':
      return state.data.map(user => user.name).join(',')
    case 'error':
      return state.error.message
  }
}

这个例子比到处写 as 更能体现 TypeScript 的价值:让非法状态更难表达。

再手写工具类型

按这个顺序:

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P]
}

type MyReadonly<T> = {
  readonly [P in keyof T]: T[P]
}

type MyOmit<T, K extends keyof any> = MyPick<
  T,
  Exclude<keyof T, K>
>

type MyReturnType<T extends (...args: any[]) => any> =
  T extends (...args: any[]) => infer R ? R : never

type DeepReadonly<T> =
  T extends (...args: any[]) => any
    ? T
    : T extends object
      ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
      : T

每写一个都回答三个问题:

  1. 输入类型是什么?
  2. 遍历或匹配发生在哪里?
  3. 最终生成了什么类型?

阶段产出

为一个请求模块完整建模,禁止使用裸 any

type ApiResponse<T> =
  | { ok: true; data: T }
  | { ok: false; message: string }

async function request<T>(url: string): Promise<ApiResponse<T>> {
  try {
    const response = await fetch(url)

    if (!response.ok) {
      return {
        ok: false,
        message: `HTTP ${response.status}`,
      }
    }

    return {
      ok: true,
      data: await response.json() as T,
    }
  } catch (error) {
    return {
      ok: false,
      message: error instanceof Error ? error.message : 'Unknown error',
    }
  }
}

同时要清楚:泛型不会自动验证运行时返回的数据。TypeScript 类型检查不能替代 runtime validation。


第三阶段:先把 React 当成 UI 工具使用

这一阶段先建立组件化思维,不急着读 Fiber。

学习内容

  1. JSX 与组件
  2. Props
  3. State
  4. 事件处理
  5. 条件渲染和列表渲染
  6. useState
  7. useEffect
  8. useRef
  9. 自定义 Hook
  10. Context 与状态提升

项目建议

做一个任务管理器,至少包含:

  • 新增、修改、删除任务
  • 状态筛选
  • 搜索
  • 本地持久化
  • 请求 loading、error、success 状态
  • 自定义 Hook
  • React Router
  • 基础测试

类型模型可以这样开始:

type Todo = {
  id: string
  title: string
  completed: boolean
  createdAt: number
}

type TodoAction =
  | { type: 'add'; payload: Todo }
  | { type: 'toggle'; payload: { id: string } }
  | { type: 'remove'; payload: { id: string } }

这能把 React 状态管理和 TypeScript 联合类型连接起来。

useEffect 的学习重点

不要把它理解成“组件生命周期替代品”。先理解它用于和外部系统同步:

import { useEffect, useState } from 'react'

function OnlineStatus() {
  const [online, setOnline] = useState(navigator.onLine)

  useEffect(() => {
    const handleOnline = () => setOnline(true)
    const handleOffline = () => setOnline(false)

    window.addEventListener('online', handleOnline)
    window.addEventListener('offline', handleOffline)

    return () => {
      window.removeEventListener('online', handleOnline)
      window.removeEventListener('offline', handleOffline)
    }
  }, [])

  return <span>{online ? '在线' : '离线'}</span>
}

每个 Effect 都问:

  • 在和哪个外部系统同步?
  • 依赖为什么是这些?
  • 是否需要 cleanup?
  • 这段逻辑能不能直接在事件处理函数里完成?

第四阶段:下钻 React 原理

会写基本项目后,再研究 React 为什么这样工作。

学习顺序

1. JSX 到页面

先能解释完整链路:

JSX
→ 编译后的元素对象
→ Fiber 节点
→ render/reconciliation
→ commit
→ DOM 更新
→ 浏览器重新渲染

可以直接观察 JSX 编译结果:

const element = <button className="primary">Save</button>

它本质上会转成创建 React Element 的调用,而不是直接创建 DOM。

2. reconciliation

重点理解:

  • 为什么同层比较
  • type 变化为什么会重建
  • key 的真正作用
  • 为什么不能随便用数组索引作为 key
  • State 为什么和组件在树中的位置相关

做一个实验:列表重排时分别使用稳定 ID 和数组索引,观察输入框状态是否错位。

3. Fiber

不要先背字段。先理解旧递归更新的问题:

  • 同步递归工作难以中断
  • 大任务可能长期占用主线程
  • Fiber 把工作拆成节点
  • render 阶段可以组织和调度工作
  • commit 阶段执行实际 DOM 变更

然后再看常见字段之间的关系:

child
sibling
return
alternate
flags

4. Hooks

重点研究:

  • Hooks 为什么依赖调用顺序
  • 为什么不能放在条件语句里
  • State 更新为什么可能是异步表现
  • 闭包为什么会导致 stale state
  • 函数组件每次渲染意味着什么

5. 合成事件

把 React 事件和浏览器事件连接起来:

  • 捕获与冒泡
  • 事件委托
  • SyntheticEvent
  • React 事件处理与原生监听器的关系
  • 更新批处理发生在什么上下文中

阶段产出

自己实现一个简化版 React,至少包含:

createElement
→ 构建 Fiber
→ 可中断的 work loop
→ 创建 DOM
→ commit
→ useState

目的不是造框架,而是把抽象压缩成能运行的代码。


第五阶段:系统学习浏览器导航与渲染

这部分要和项目中的真实现象连接,而不是背面试题。

主线一:输入 URL 到页面展示

按顺序学习:

URL 解析
→ 缓存检查
→ DNS
→ TCP
→ TLS
→ HTTP
→ 服务端响应
→ HTML 解析
→ 资源加载
→ DOM / CSSOM
→ Render Tree
→ Layout
→ Paint
→ Composite

每一层都追问:

  • 输入是什么?
  • 输出是什么?
  • 哪些步骤会阻塞后续过程?
  • 浏览器在哪些地方能够复用缓存?
  • 出错时能从 DevTools 哪里看出来?

主线二:渲染与性能

重点区分:

  • Style calculation
  • Layout
  • Paint
  • Composite
  • 回流与重绘
  • 主线程与合成线程
  • 长任务
  • requestAnimationFrame

做一个可观察实验:

const box = document.querySelector('.box')

for (let i = 0; i < 1000; i++) {
  box.style.width = `${box.offsetWidth + 1}px`
}

这里在循环中交替读取布局信息和修改样式,可能触发强制同步布局。再改成一次读取、集中计算、一次写入,对比 Performance 面板。

主线三:网络与缓存

建立这套模型:

强缓存:
Cache-Control / Expires

协商缓存:
ETag / If-None-Match
Last-Modified / If-Modified-Since

不要只背状态码,要在 DevTools Network 中验证:

  • 请求是否真的发出
  • 状态显示是 200304 还是 memory/disk cache
  • response header 和 request header 如何对应
  • reload、hard reload 的结果有什么不同

第六阶段:把三条线合并

最后做一个小型但完整的 React + TypeScript 项目,例如文章列表或后台管理页。

技术要求

  • React + TypeScript
  • 请求状态使用可辨识联合类型
  • 自定义 Hook 封装请求或本地存储
  • 路由级懒加载
  • 错误边界
  • 基础单元测试
  • 构建产物分析
  • 缓存策略说明
  • Performance 与 Network 面板验证

需要回答的问题

项目做完后,我会要求自己解释:

  1. JSX 如何变成 DOM?
  2. 一次 setState 之后发生了什么?
  3. key 如何影响节点复用?
  4. Effect 为什么执行,为什么清理?
  5. TypeScript 在哪里消除了非法状态?
  6. 哪些类型只存在于编译期?
  7. 点击按钮后,事件经过了哪些阶段?
  8. 请求经历了哪些网络步骤?
  9. 页面为什么会发生 Layout 或 Paint?
  10. 首屏慢,应该先测什么,而不是先优化什么?

如果这些只能背答案,项目价值还没完全转化成理解。


一个可执行的 12 周安排

周数主线产出
1JavaScript 执行模型、Event Loop任务执行顺序实验
2DOM 事件、HTTP、渲染基础URL 导航流程图
3TypeScript 基础与类型收窄请求数据模型
4泛型、映射类型、条件类型手写常用工具类型
5React 组件、Props、StateTodo 基础功能
6Effect、Ref、自定义 Hook持久化与请求功能
7路由、状态组织、测试完整小项目
8JSX、React Element、reconciliationJSX 转换实验
9Fiber、render、commit简化 Fiber 实现
10Hooks、合成事件简化 useState
11浏览器导航、缓存、渲染DevTools 验证记录
12性能优化与综合复盘项目分析报告

我的验收标准

每个知识点都过四关:

  1. 能使用:能独立写出代码。
  2. 能解释:能说清输入、过程和输出。
  3. 能验证:能通过 Demo、DevTools 或测试证明。
  4. 能判断边界:知道它何时不适用,以及有什么代价。

路线不必严格按天执行,但顺序别反:先建立可运行的经验,再研究抽象和源码。只看源码不做项目容易失去上下文;只做项目不研究原理,又容易停留在 API 层。

AI 推断