WangShuXian6 / blog

FE-BLOG
https://wangshuxian6.github.io/blog/
MIT License
45 stars 10 forks source link

React 19 深入浅出, 构建生产级React应用程序 Learn React 19 with Epic React v2/进行中 #204

Open WangShuXian6 opened 1 day ago

WangShuXian6 commented 1 day ago

React 19 深入浅出, 构建生产级React应用程序 Learn React 19 with Epic React v2

1 React Fundamentals


001 Intro 002 Hello World in JS 003 Hello World in JS (1) 004 Generate the Root Node 005 Generate the Root Node (1) 006 Dad Joke Break 007 Intro to Raw React APIs 008 Create React Elements 009 Create React Elements (1) 010 Nesting Elements 011 Nesting Elements (1) 012 Deep Nesting Elements 013 Deep Nesting Elements (1) 014 Dad Joke Break Raw React APIs 015 Intro to Using JSX 016 Compiling JSX 017 Compiling JSX (1) 018 Interpolation 019 Interpolation (1) 020 Spread props 021 Spread props (1) 022 Nesting JSX 023 Nesting JSX (1) 024 Fragments 025 Fragments (1) 026 Dad Joke Break Using JSX 027 Intro to Custom Components 028 Simple Function 029 Simple Function (1) 030 Raw API 031 Raw API (1) 032 JSX Components 033 JSX Components (1) 034 Props 035 Props (1) 036 Dad Joke Break Custom Components 037 Intro to TypeScript 038 Props (2) 039 Props (3) 040 Narrow Types 041 Narrow Types (1) 042 Derive Types 043 Derive Types (1) 044 Default Props 045 Default Props (1) 046 Reduce Duplication 047 Reduce Duplication (1) 048 Satisfies 049 Satisfies (1) 050 Dad Joke Break TypeScript 051 Intro to Styling 052 Styling 053 Styling (1) 054 Custom Component 055 Custom Component (1) 056 Size Prop 057 Size Prop (1) 058 Dad Joke Break Styling 059 Intro to Forms 060 Form 061 Form (1) 062 Form Action 063 Form Action (1) 064 Input Types 065 Input Types (1) 066 Submission 067 Submission (1) 068 Form Actions 069 Form Actions (1) 070 Dad Joke Break Forms 071 Intro to Inputs 072 Checkbox 073 Checkbox (1) 074 Select 075 Select (1) 076 Radios 077 Radios (1) 078 Hidden Inputs 079 Hidden Inputs (1) 080 Default Value 081 Default Value (1) 082 Dad Joke Break Inputs 083 Intro to Error Boundaries 084 Composition 085 Composition (1) 086 Other Errors 087 Other Errors (1) 088 Reset 089 Reset (1) 090 Dad Joke Break Error Boundaries 091 Intro to Rendering Arrays 092 Key prop 093 Key prop (1) 094 Focus State 095 Focus State (1) 096 Key Reset 097 Key Reset (1) 098 Dad Joke Break Rendering Arrays 099 Outro to React Fundamentals

2 React Hooks 001 React Hooks Intro 002 Intro to Managing UI State 003 useState 004 useState (1) 005 Controlling Inputs 006 Controlling Inputs (1) 007 Derive State 008 Derive State (1) 009 Initialize State 010 Initialize State (1) 011 Init Callback 012 Init Callback (1) 013 Dad Joke Break Managing UI State 014 Intro to Side-Effects 015 useEffect 016 useEffect (1) 017 Effect Cleanup 018 Effect Cleanup (1) 019 Dad Joke Break Side-Effects 020 Intro to Lifting State 021 Lift State 022 Lift State (1) 023 Lift More State 024 Lift More State (1) 025 Colocate State 026 Colocate State (1) 027 Dad Joke Break Lifting State 028 Intro to DOM Side-Effects 029 Refs 030 Refs (1) 031 Dependencies 032 Dependencies (1) 033 Primitive Dependencies 034 Primitive Dependencies (1) 035 Dad Joke Break DOM Side-Effects 036 Intro to Unique IDs 037 useId 038 useId (1) 039 Dad Joke Break Unique IDs 040 Intro to Tic Tac Toe 041 setState callback 042 setState callback (1) 043 Preserve State in localStorage 044 Preserve State in localStorage (1) 045 Add Game History Feature 046 Add Game History Feature (1) 047 Dad Joke Break Tic Tac Toe 048 Outro to React Hooks

3 Advanced React APIs

  1. Advanced React APIs Intro
  2. Intro to Advanced State Management
  3. New State _ Problem
  4. New State _ solution
  5. Previous State _ Problem
  6. Previous State _ solution
  7. State Object _ Problem
  8. State Object _ solution
  9. Action Function _ Problem
  10. Action Function _ solution
  11. Traditional Reducer _ Problem
  12. Traditional Reducer _ solution
  13. Real World _Problem
  14. Real World _ solution
  15. Dad Joke Break Advanced State Management
  16. Intro to State Optimization
  17. Optimize state updates _ Problem
  18. Optimize state updates _solution
  19. Dad Joke Break State Optimization
  20. Intro to Custom Hooks
  21. Hook Function _Problem
  22. Hook Function _ solution
  23. useCallback _ Problem
  24. useCallback _ solution
  25. Dad Joke Break Custom Hooks
  26. Intro to Shared Context
  27. Context Provider _ Problem
  28. Context Provider _ solution
  29. Context Hook _ Problem
  30. Context Hook _ solution
  31. Dad Joke Break Shared Context
  32. Intro to Portals
  33. createPortal _ Problem
  34. createPortal _ solution
  35. Dad Joke Break Portals
  36. Intro to Layout Computation
  37. useLayoutEffect _Problem
  38. useLayoutEffect _solution
  39. Dad Joke Break Layout Computation
  40. Intro to Imperative Handles
  41. useImperativeHandle _Problem
  42. useImperativeHandle _ solution
  43. Dad Joke Break Imperative Handles
  44. Intro to Focus Management
  45. flushSync _Problem
  46. flushSync _solution
  47. Dad Joke Break Focus Management
  48. Intro to Sync External State
  49. useSyncExternalStore _Problem
  50. useSyncExternalStore _solution
  51. Make Store Utility _Problem
  52. Make Store Utility _solution
  53. Handling Server Rendering _Problem
  54. Handling Server Rendering _solution
  55. Dad Joke Break Sync External State
  56. Outro to Advanced React APIs

    4 React Suspense

  57. Intro to Data fetching
  58. Throwing Promises _Problem
  59. Throwing Promises _Solution
  60. Error Handling _Problem
  61. Error Handling _ Solution
  62. Formal Status _Problem
  63. Formal Status _Solution
  64. Utility _ Problem
  65. Utility _Solution
  66. use React _Problem
  67. use React _Solution
  68. Dad Joke Break Data fetching
  69. Intro to Dynamic Promises
  70. Promise Cache _Problem
  71. Promise Cache _Solution
  72. useTransition _Problem
  73. useTransition _Solution
  74. Pending Flash _Problem
  75. Pending Flash _Solution
  76. Dad Joke Break Dynamic Promises
  77. Intro to Optimistic UI
  78. Optimistic UI _Problem
  79. Optimistic UI _Solution
  80. Form Status _ Problem
  81. Form Status _Solution
  82. Multi-step Actions _Problem
  83. Multi-step Actions _ Solution
  84. Dad Joke Break Optimistic UI
  85. Intro to Suspense img
  86. Img Component _Problem
  87. Img Component _ Solution
  88. Img Error Boundary _Problem
  89. Img Error Boundary _Solution
  90. Key prop _Problem
  91. Key prop _Solution
  92. Dad Joke Break Suspense img
  93. Intro to Responsive
  94. useDeferredValue _Problem
  95. useDeferredValue _Solution
  96. Dad Joke Break Responsive
  97. Intro to Optimizations
  98. Parallel Loading _Problem
  99. Parallel Loading _Solution
  100. Server Cache _Problem
  101. Server Cache _Solution
  102. Dad Joke Break Optimizations
  103. Outro to React Suspense

    5 Advanced React Patterns

  104. Advanced React Patterns Intro
  105. Intro to Composition
  106. Composition and Layout Components _ Problem
  107. Composition and Layout Components _Solution
  108. Dad Joke Break Composition
  109. Intro to Latest Ref
  110. Latest Ref _ Problem
  111. Latest Ref _Solution
  112. Dad Joke Break Latest Ref
  113. Intro to Compound Components
  114. Compound Components _ Problem
  115. Compound Components _ Solution
  116. Compound Components Validation _ Problem
  117. Compound Components Validation _Solution
  118. Dad Joke Break Compound Components
  119. Intro to Slots
  120. Slot Context _Problem
  121. Slot Context _ Solution
  122. Generic Slot Components _ Problem
  123. Generic Slot Components _ Solution
  124. Slot Prop _Problem
  125. Slot Prop _ Solution
  126. Dad Joke Break Slots
  127. Intro to Prop Collections and Getters
  128. Prop Collections _Problem
  129. Prop Collections _Solution
  130. Prop Getters _Problem
  131. Prop Getters _Solution
  132. Dad Joke Break Prop Collections and Getters
  133. Intro to State Initializer
  134. Initialize Toggle _Problem
  135. Initialize Toggle _Solution
  136. Stability _Problem
  137. Stability _Solution
  138. Dad Joke Break State Initializer
  139. Intro to State Reducer
  140. State Reducer _Problem
  141. State Reducer _Solution
  142. Default State Reducer _Problem
  143. Default State Reducer _Solution
  144. Dad Joke Break State Reducer
  145. Intro to Control Props
  146. Control Props _Problem
  147. Control Props _Solution
  148. Dad Joke Break Control Props
  149. Outro to Advanced React Patterns

    6 React Performance

  150. React Performance Intro
  151. Intro to Element Optimization
  152. Reusing Elements _Problem
  153. Reusing Elements _Solution
  154. Element Props _Problem
  155. Element Props _ Solution
  156. Context _ Problem
  157. Context _Solution
  158. Memoize Elements _Problem
  159. Memoize Elements _Solution
  160. Memoize Components _ Problem
  161. Memoize Components _Solution
  162. Dad Joke Break Element Optimization
  163. Intro to Optimize Context
  164. Memoize Context _Problem
  165. Memoize Context _Solution
  166. Provider Component _Problem
  167. Provider Component _Solution
  168. Split Context _Problem
  169. Split Context _Solution
  170. Dad Joke Break Optimize Context
  171. Concurrent Rendering Intro
  172. useDeferredValue + memo _Problem
  173. useDeferredValue + memo _Solution
  174. Dad Joke Break Concurrent Rendering
  175. Intro to Code Splitting
  176. lazy _ Problem
  177. lazy _Solution
  178. Eager Loading _Problem
  179. Eager Loading _Solution
  180. Transitions _Problem
  181. Transitions _ Solution
  182. Dad Joke Break Code Splitting
  183. Intro to Expensive Calculations
  184. useMemo _Problem
  185. useMemo _Solution
  186. Web Worker _Problem
  187. Web Worker _Solution
  188. Async Results _Problem
  189. Async Results _Solution
  190. Dad Joke Break Expensive Calculations
  191. Intro to Optimize Rendering
  192. Component Memoization _ Problem
  193. Component Memoization _Solution
  194. Custom Comparator _ Problem
  195. Custom Comparator _Solution
  196. Primitives _Problem
  197. Primitives _Solution
  198. Dad Joke Break Optimize Rendering
  199. Intro to Windowing
  200. Virtualizer _Problem
  201. Virtualizer _Solution
  202. Dad Joke Break Windowing
  203. Outro to React Performance

    7 React Server Components

  204. React Server Components Intro
  205. Intro to Warm Up
  206. Static React App _Problem
  207. Static React App _Solution
  208. Dad Joke Break Warm Up
  209. Intro to Server Components
  210. RSCs _Problem
  211. Async Components _ Problem
  212. Async Components _Solution
  213. Streaming _Problem
  214. Streaming _ Solution
  215. Server Context _Problem
  216. Server Context _Solution
  217. Dad Joke Break Server Components
  218. Intro to Client Components
  219. Node.js Loader _Problem
  220. Node.js Loader _Solution
  221. Module Resolution _Problem
  222. Module Resolution _Solution
  223. Dad Joke Break Client Components
  224. Intro to Client Router
  225. Client Router _Problem
  226. Client Router _Solution
  227. Pending UI _Problem
  228. Pending UI _Solution
  229. Race Conditions _Problem
  230. Race Conditions _Solution
  231. History _Problem
  232. History _Solution
  233. Cache _Problem
  234. Cache _Solution
  235. Dad Joke Break Client Router
  236. Intro to Server Actions
  237. Action Reference _ Problem
  238. Action Reference _Solution
  239. Client Side _Problem
  240. Client Side _ Solution
  241. Server Side _Problem
  242. Server Side _Solution
  243. Revalidation _Problem
  244. Revalidation _Solution
  245. History Revalidation _ Problem
  246. History Revalidation _Solution
  247. Dad Joke Break Server Actions
  248. Outro to React Server Components

    8 Bonus. Interviews With Experts

  249. Getting into Open Source with Aakansha Doshi
  250. Enhancing Forms using React 19 with Aurora Scharff
  251. Jenna Smith on AI, Building Radix, and Tokenami
  252. Evan Bacon brings React Server Components to React Native
  253. Kateryna Porshnieva on Building Accessible Apps with React 19
  254. React's Evolution_ Past, Present, and Future with Lee Robinson
  255. Matt Brophy on Remix, React Router, and Open-Source
  256. Michelle Beckles on Community Building and Developer Health
  257. Under the Hood of React 19 with Rick Hanlon
  258. Sam Selikoff on React's Impact in Modern Web Development
  259. Lydia Hallie on JavaScript, React, and the Future of Web Development
  260. Sebastian Silbermann on Testing, Tooling, and Transitions With React 19
  261. The Importance of Accessibility in Modern Web Development With Shruti Kapoor
  262. Sunil Pai on Changing Lives with Powerful Software, PartyKit, and Durable Objects
  263. Theo Browne on His Personal Experience as a Web Developer
  264. Dominik Dorfmeister on His Open-Source Journey

1 React基础 001 介绍
002 在JS中实现Hello World
003 在JS中实现Hello World (1)
004 生成根节点
005 生成根节点 (1)
006 爸爸笑话时间
007 原生React API简介
008 创建React元素
009 创建React元素 (1)
010 嵌套元素
011 嵌套元素 (1)
012 深度嵌套元素
013 深度嵌套元素 (1)
014 爸爸笑话时间 原生React API
015 使用JSX简介
016 编译JSX
017 编译JSX (1)
018 插值
019 插值 (1)
020 Spread props
021 Spread props (1)
022 嵌套JSX
023 嵌套JSX (1)
024 片段
025 片段 (1)
026 爸爸笑话时间 使用JSX
027 自定义组件简介
028 简单函数
029 简单函数 (1)
030 原生API
031 原生API (1)
032 JSX组件
033 JSX组件 (1)
034 Props
035 Props (1)
036 爸爸笑话时间 自定义组件
037 TypeScript简介
038 Props (2)
039 Props (3)
040 类型收窄
041 类型收窄 (1)
042 推导类型
043 推导类型 (1)
044 默认Props
045 默认Props (1)
046 减少重复
047 减少重复 (1)
048 Satisfies
049 Satisfies (1)
050 爸爸笑话时间 TypeScript
051 样式简介
052 样式
053 样式 (1)
054 自定义组件
055 自定义组件 (1)
056 尺寸Props
057 尺寸Props (1)
058 爸爸笑话时间 样式
059 表单简介
060 表单
061 表单 (1)
062 表单操作
063 表单操作 (1)
064 输入类型
065 输入类型 (1)
066 提交
067 提交 (1)
068 表单操作
069 表单操作 (1)
070 爸爸笑话时间 表单
071 输入简介
072 复选框
073 复选框 (1)
074 下拉选择
075 下拉选择 (1)
076 单选按钮
077 单选按钮 (1)
078 隐藏输入
079 隐藏输入 (1)
080 默认值
081 默认值 (1)
082 爸爸笑话时间 输入
083 错误边界简介
084 组合
085 组合 (1)
086 其他错误
087 其他错误 (1)
088 重置
089 重置 (1)
090 爸爸笑话时间 错误边界
091 数组渲染简介
092 Key prop
093 Key prop (1)
094 焦点状态
095 焦点状态 (1)
096 Key重置
097 Key重置 (1)
098 爸爸笑话时间 数组渲染
099 React基础结束

2 React钩子 001 React钩子简介
002 UI状态管理简介
003 useState
004 useState (1)
005 控制输入
006 控制输入 (1)
007 推导状态
008 推导状态 (1)
009 初始化状态
010 初始化状态 (1)
011 初始化回调
012 初始化回调 (1)
013 爸爸笑话时间 UI状态管理
014 副作用简介
015 useEffect
016 useEffect (1)
017 清理副作用
018 清理副作用 (1)
019 爸爸笑话时间 副作用
020 状态提升简介
021 提升状态
022 提升状态 (1)
023 提升更多状态
024 提升更多状态 (1)
025 状态合并
026 状态合并 (1)
027 爸爸笑话时间 状态提升
028 DOM副作用简介
029 Refs
030 Refs (1)
031 依赖项
032 依赖项 (1)
033 原始依赖项
034 原始依赖项 (1)
035 爸爸笑话时间 DOM副作用
036 唯一ID简介
037 useId
038 useId (1)
039 爸爸笑话时间 唯一ID
040 井字棋简介
041 setState回调
042 setState回调 (1)
043 在localStorage中保存状态
044 在localStorage中保存状态 (1)
045 添加游戏历史功能
046 添加游戏历史功能 (1)
047 爸爸笑话时间 井字棋
048 React钩子结束

3 高级React API

  1. 高级React API简介
  2. 高级状态管理简介
  3. 新状态_问题
  4. 新状态_解决方案
  5. 以前的状态_问题
  6. 以前的状态_解决方案
  7. 状态对象_问题
  8. 状态对象_解决方案
  9. 动作函数_问题
  10. 动作函数_解决方案
  11. 传统Reducer_问题
  12. 传统Reducer_解决方案
  13. 现实场景_问题
  14. 现实场景_解决方案
  15. 爸爸笑话时间 高级状态管理
  16. 状态优化简介
  17. 优化状态更新_问题
  18. 优化状态更新_解决方案
  19. 爸爸笑话时间 状态优化
  20. 自定义钩子简介
  21. 钩子函数_问题
  22. 钩子函数_解决方案
  23. useCallback_问题
  24. useCallback_解决方案
  25. 爸爸笑话时间 自定义钩子
  26. 共享上下文简介
  27. 上下文提供者_问题
  28. 上下文提供者_解决方案
  29. 上下文钩子_问题
  30. 上下文钩子_解决方案
  31. 爸爸笑话时间 共享上下文
  32. 门户简介
  33. createPortal_问题
  34. createPortal_解决方案
  35. 爸爸笑话时间 门户
  36. 布局计算简介
  37. useLayoutEffect_问题
  38. useLayoutEffect_解决方案
  39. 爸爸笑话时间 布局计算
  40. 命令式处理简介
  41. useImperativeHandle_问题
  42. useImperativeHandle_解决方案
  43. 爸爸笑话时间 命令式处理
  44. 焦点管理简介
  45. flushSync_问题
  46. flushSync_解决方案
  47. 爸爸笑话时间 焦点管理
  48. 同步外部状态简介
  49. useSyncExternalStore_问题
  50. useSyncExternalStore_解决方案
  51. 创建存储实用程序_问题
  52. 创建存储实用程序_解决方案
  53. 处理服务器渲染_问题
  54. 处理服务器渲染_解决方案
  55. 爸爸笑话时间 同步外部状态
  56. 高级React API结束

4 React Suspense

  1. 数据获取简介
  2. 抛出Promise_问题
  3. 抛出Promise_解决方案
  4. 错误处理_问题
  5. 错误处理_解决方案
  6. 正式状态_问题
  7. 正式状态_解决方案
  8. 实用工具_问题
  9. 实用工具_解决方案
  10. 使用React_问题
  11. 使用React_解决方案
  12. 爸爸笑话时间 数据获取
  13. 动态Promise简介
  14. Promise缓存_问题
  15. Promise缓存_解决方案
  16. useTransition_问题
  17. useTransition_解决方案
  18. 挂起闪烁_问题
  19. 挂起闪烁_解决方案
  20. 爸爸笑话时间 动态Promise
  21. 乐观UI简介
  22. 乐观UI_问题
  23. 乐观UI_解决方案
  24. 表单状态_问题
  25. 表单状态_解决方案
  26. 多步操作_问题
  27. 多步操作_解决方案
  28. 爸爸笑话时间 乐观UI
  29. Suspense图像组件简介
  30. 图像组件_问题
  31. 图像组件_解决方案
  32. 图像错误边界_问题
  33. 图像错误边界_解决方案
  34. Key属性_问题
  35. Key属性_解决方案
  36. 爸爸笑话时间 Suspense图像
  37. 响应式简介
  38. useDeferredValue_问题
  39. useDeferredValue_解决方案
  40. 爸爸笑话时间 响应式
  41. 优化简介
  42. 并行加载_问题
  43. 并行加载_解决方案
  44. 服务器缓存_问题
  45. 服务器缓存_解决方案
  46. 爸爸笑话时间 优化
  47. React Suspense结束

5 高级React模式

  1. 高级React模式简介
  2. 组合简介
  3. 组合和布局组件_问题
  4. 组合和布局组件_解决方案
  5. 爸爸笑话时间 组合
  6. 最新Ref简介
  7. 最新Ref_问题
  8. 最新Ref_解决方案
  9. 爸爸笑话时间 最新Ref
  10. 复合组件简介
  11. 复合组件_问题
  12. 复合组件_解决方案
  13. 复合组件验证_问题
  14. 复合组件验证_解决方案
  15. 爸爸笑话时间 复合组件
  16. 插槽简介
  17. 插槽上下文_问题
  18. 插槽上下文_解决方案
  19. 通用插槽组件_问题
  20. 通用插槽组件_解决方案
  21. 插槽Prop_问题
  22. 插槽Prop_解决方案
  23. 爸爸笑话时间 插槽
  24. Prop集合和Getters简介
  25. Prop集合_问题
  26. Prop集合_解决方案
  27. Prop Getters_问题
  28. Prop Getters_解决方案
  29. 爸爸笑话时间 Prop集合和Getters
  30. 状态初始化器简介
  31. 初始化切换_问题
  32. 初始化切换_解决方案
  33. 稳定性_问题
  34. 稳定性_解决方案
  35. 爸爸笑话时间 状态初始化器
  36. 状态Reducer简介
  37. 状态Reducer_问题
  38. 状态Reducer_解决方案
  39. 默认状态Reducer_问题
  40. 默认状态Reducer_解决方案
  41. 爸爸笑话时间 状态Reducer
  42. 控制属性简介
  43. 控制属性_问题
  44. 控制属性_解决方案
  45. 爸爸笑话时间 控制属性
  46. 高级React模式结束

6 React性能优化

  1. React性能优化简介
  2. 元素优化简介
  3. 重用元素_问题
  4. 重用元素_解决方案
  5. 元素Props_问题
  6. 元素Props_解决方案
  7. 上下文_问题
  8. 上下文_解决方案
  9. 元素记忆化_问题
  10. 元素记忆化_解决方案
  11. 组件记忆化_问题
  12. 组件记忆化_解决方案
  13. 爸爸笑话时间 元素优化
  14. 优化上下文简介
  15. 上下文记忆化_问题
  16. 上下文记忆化_解决方案
  17. 提供者组件_问题
  18. 提供者组件_解决方案
  19. 分割上下文_问题
  20. 分割上下文_解决方案
  21. 爸爸笑话时间 优化上下文
  22. 并发渲染简介
  23. useDeferredValue + memo_问题
  24. useDeferredValue + memo_解决方案
  25. 爸爸笑话时间 并发渲染
  26. 代码拆分简介
  27. lazy_问题
  28. lazy_解决方案
  29. 预加载_问题
  30. 预加载_解决方案
  31. 过渡_问题
  32. 过渡_解决方案
  33. 爸爸笑话时间 代码拆分
  34. 高耗计算简介
  35. useMemo_问题
  36. useMemo_解决方案
  37. Web Worker_问题
  38. Web Worker_解决方案
  39. 异步结果_问题
  40. 异步结果_解决方案
  41. 爸爸笑话时间 高耗计算
  42. 渲染优化简介
  43. 组件记忆化_问题
  44. 组件记忆化_解决方案
  45. 自定义比较器_问题
  46. 自定义比较器_解决方案
  47. 原始类型_问题
  48. 原始类型_解决方案
  49. 爸爸笑话时间 渲染优化
  50. 窗口化简介
  51. Virtualizer_问题
  52. Virtualizer_解决方案
  53. 爸爸笑话时间 窗口化
  54. React性能优化结束

7 React服务器组件

  1. React服务器组件简介
  2. 热身简介
  3. 静态React应用_问题
  4. 静态React应用_解决方案
  5. 爸爸笑话时间 热身
  6. 服务器组件简介
  7. 服务器组件_问题
  8. 异步组件_问题
  9. 异步组件_解决方案
  10. 流式传输_问题
  11. 流式传输_解决方案
  12. 服务器上下文_问题
  13. 服务器上下文_解决方案
  14. 爸爸笑话时间 服务器组件
  15. 客户端

组件简介

  1. Node.js加载器_问题
  2. Node.js加载器_解决方案
  3. 模块解析_问题
  4. 模块解析_解决方案
  5. 爸爸笑话时间 客户端组件
  6. 客户端路由简介
  7. 客户端路由_问题
  8. 客户端路由_解决方案
  9. 挂起UI_问题
  10. 挂起UI_解决方案
  11. 竞争条件_问题
  12. 竞争条件_解决方案
  13. 历史_问题
  14. 历史_解决方案
  15. 缓存_问题
  16. 缓存_解决方案
  17. 爸爸笑话时间 客户端路由
  18. 服务器操作简介
  19. 操作引用_问题
  20. 操作引用_解决方案
  21. 客户端_问题
  22. 客户端_解决方案
  23. 服务器_问题
  24. 服务器_解决方案
  25. 重新验证_问题
  26. 重新验证_解决方案
  27. 历史重新验证_问题
  28. 历史重新验证_解决方案
  29. 爸爸笑话时间 服务器操作
  30. React服务器组件结束

8 额外:专家访谈

  1. 与Aakansha Doshi讨论如何进入开源
  2. Aurora Scharff讲解如何使用React 19增强表单
  3. Jenna Smith谈AI、Radix构建和Tokenami
  4. Evan Bacon将React服务器组件引入React Native
  5. Kateryna Porshnieva讲解如何使用React 19构建无障碍应用
  6. Lee Robinson讲述React的演变:过去、现在与未来
  7. Matt Brophy谈Remix、React Router和开源
  8. Michelle Beckles谈社区建设和开发者健康
  9. Rick Hanlon讲解React 19的内幕
  10. Sam Selikoff谈React在现代Web开发中的影响
  11. Lydia Hallie讨论JavaScript、React和Web开发的未来
  12. Sebastian Silbermann讲解React 19的测试、工具和过渡
  13. Shruti Kapoor讲述现代Web开发中的无障碍性重要性
  14. Sunil Pai谈如何通过强大的软件、PartyKit和耐久对象改变生活
  15. Theo Browne谈他的个人Web开发经验
  16. Dominik Dorfmeister谈他的开源之旅
WangShuXian6 commented 1 day ago

1 React基础

001 介绍

使用 HTML 和 JavaScript 实现 Hello World

要在 HTML 中实现一个 "Hello World",其实并不复杂。你只需要创建一个简单的 HTML 文档,使用 <body> 标签和一个包含 “Hello World” 的 <div> 或者 <p> 标签。

但如果你想通过 JavaScript 实现 "Hello World",也不会特别复杂。这是你第一次接触如何使用 JavaScript 与页面互动,并创建动态和交互式用户体验的入口。

要在页面上添加 JavaScript,可以使用 <script> 标签。你可以直接在这个标签内编写 JavaScript 代码,或者使用 src 属性链接到一个外部的 JavaScript 文件。虽然我们通常会将 JavaScript 代码放在单独的文件中,但为了简单起见,在初步学习中,我们会将所有内容放在一个文件里。

学习目标

在这个练习中,我们的重点是学习以下内容:

  1. 文档对象模型(DOM):JavaScript 如何通过 DOM 操作页面。
  2. 创建与附加元素:如何使用 JavaScript 在页面上创建和附加 HTML 元素。

可以通过访问 MDN DOM 文档 来了解更多关于 DOM 的信息。DOM 是从 JavaScript 在 Web 诞生之初就存在的一部分,通过 DOM,我们能够与页面进行交互。

尽管直接操作 DOM 可能有点混乱,但现代 JavaScript 和 DOM 的操作已经变得非常简洁高效。你可以使用这些技术构建一些不错的用户体验。当然,像 React 这样的框架提供了更多的功能,帮助我们更方便地进行复杂应用的开发,但在这个练习中,我们的重点是了解 DOM 的基础知识。

练习目标

通过本次练习,你将学会使用 JavaScript 直接在 DOM 上实现 "Hello World",为之后深入学习 React 和其他高级工具打下坚实的基础。

002 在JS中实现Hello World

第一步:使用 JavaScript 和 HTML 创建 Hello World

在本次练习的第一步中,我们将使用 JavaScript 在 HTML 中创建一个 "Hello World"。当你完成后,页面应简单显示 "Hello World",如同示例中那样。

你可以按照以下步骤进行操作:

  1. 创建一个基本的 HTML 页面,如 index.html
  2. 在页面中使用 <script> 标签,编写或引用 JavaScript 代码,将 "Hello World" 添加到页面。
  3. 确保页面在浏览器中运行时,能够显示 "Hello World"。

这是一项非常基础的练习,指引会帮助你完成,祝你玩得开心!

003 在JS中实现Hello World (1)

这个视频讲解了如何使用JavaScript和DOM API在HTML中创建一个“Hello World”的示例。以下是关键步骤的总结:

  1. 基本HTML结构

    • 创建一个包含ID为“root”的div元素的HTML文件。
    • 在文件中加入一个<script>标签,并将type设为module,用来嵌入JavaScript代码。
  2. 使用JavaScript操作DOM

    • <script>标签中,使用document.getElementById('root')选择页面上的root元素。
    • 使用document.createElement('div')创建一个新的div元素。
    • 给新创建的div元素添加一个className(例如:container),并设置其textContent为“Hello World”。
    • 使用appendChild()append()将新创建的div元素追加到root元素中。
  3. DOM操作

    • 该脚本展示了如何直接操作DOM,并解释了在内存中创建一个元素并不会自动显示在页面上。
    • 要显示这个元素,必须显式地将其追加到已经存在于文档中的某个元素上(在这个例子中就是root元素)。

通过这些步骤,你可以使用JavaScript和DOM操作技术,动态地在网页上创建并追加元素,实现“Hello World”的显示。

004 生成根节点

这段视频介绍了一个新的练习,目的是让你更深入地理解如何使用JavaScript操作DOM。与之前的练习不同,这次我们将完全用JavaScript生成根元素,而不是依赖于HTML预先定义的元素。以下是视频的主要内容:

  1. 挑战的目标

    • 通过JavaScript生成一个根元素(如<div>),而不是在HTML中预先定义。
    • 进一步推动你对DOM操作的理解和实践。
  2. 任务内容

    • 使用document.createElement动态生成一个根元素,并将其插入到文档中。
    • 通过JavaScript为页面添加内容,而不是依赖HTML。
  3. 动手实验

    • 该练习旨在让你更熟悉JavaScript操作DOM的能力,提升你在前端开发中的实践技能。

练习的核心是完全通过JavaScript操控页面结构,摆脱对HTML的依赖,增强对动态DOM操作的理解。

005 生成根节点 (1)

在这段视频中,主要讲解了如何使用JavaScript动态创建并添加HTML元素到DOM中,以下是视频的核心内容总结:

  1. 删除现有元素

    • 首先,通过删除HTML中的根元素来触发错误。页面将报错,提示“cannot read properties of null reading append”,因为在DOM中找不到该元素。
  2. 使用JavaScript创建元素

    • 使用document.createElement创建一个新的div元素,并设置其id为"root"。
  3. 将新元素添加到DOM中

    • 通过document.body.append方法,将动态创建的div元素(rootElement)添加到body中,使其真正显示在页面上。
    • 如果需要将元素插入到页面的顶部,可以使用document.body.prepend,而不是append
  4. DOM的动态操作

    • 本例展示了如何使用JavaScript动态创建、修改并将元素添加到DOM结构中。这种操作是构建动态Web应用的基础。
    • 进一步扩展,你可以添加事件处理、按钮等,实现更复杂的交互功能。

这段视频的目的是展示如何通过JavaScript操控DOM,创建并修改页面元素,帮助理解如何在动态网页中使用JavaScript进行基本的DOM操作。

006 爸爸笑话时间

在这段视频中,介绍了一个轻松的休息时间。讲者强调了在学习过程中定期休息的重要性,帮助大脑吸收和巩固所学内容。视频还分享了一个轻松的笑话:

之后,讲者提醒观众保持充足的水分,并鼓励大家利用这段时间稍作休息,为接下来的学习做好准备。

007 原生React API简介

这段视频介绍了如何在引入React的基础上,通过不使用JSX的方式,使用原生React API创建一个简单的"Hello World"应用。讲者首先解释了React是基于传统的document.createElement构建的,目的是让开发者理解React背后的工作原理。以下是视频中的一些关键点:

  1. React与React DOM:讲者介绍了React不仅仅是一个单一的包,它实际上分为两个主要的包:ReactReact DOMReact负责管理组件、hooks和API,而React DOM则负责将React组件渲染到网页的DOM上。

  2. React的跨平台能力:除了网页上的React渲染器(React DOM),React还可以用于虚拟现实、原生桌面应用和命令行界面等场景。

  3. 从原生DOM到React的转换:通过引入React和React DOM,开发者能够将React元素转换为可以在网页上显示的DOM元素。

视频的目的是引导开发者通过逐步学习React的底层API,最终理解React如何简化开发工作,并逐步深入React的使用。

008 创建React元素

在这个练习中,我们将React和React DOM引入页面,并使用它们的createElementcreateRoot API来替代原生的document.createElement等操作。讲者提醒,这并不是通常引入React的方式,通常你会使用构建工具(如Webpack、Parcel)来处理React的构建和优化工作,包括类型检查、打包和性能优化等。

在这个简单的练习中,我们的目标是通过React的声明式API(如React.createElement)来实现之前使用原生DOM API的效果。尽管最后页面的展示效果与之前完全相同,但我们将会使用React来构建整个页面结构。这是从传统DOM操作向React世界过渡的第一步。

你可以通过查看项目仓库中的公共目录来了解如何加载React和React DOM库,并使用它们来创建页面元素。希望你在这次练习中有所收获!

009 创建React元素 (1)

在这个示例中,我们展示了如何使用React和React DOM将元素渲染到页面上。首先,我们通过createElement从React创建一个新的元素,并设置classNamechildren属性来替代传统的classtextContent

React元素只是一个UI描述符,它不是直接的DOM元素。因此,我们还需要通过React DOM中的createRoot API来将这个React元素渲染为实际的DOM元素。以下是我们所做的关键步骤:

  1. 使用createElement:我们用React的createElement来创建一个元素。在这个例子中,我们创建了一个div,并给它设置了classNamechildren属性。
  2. 使用createRoot渲染元素:通过createRoot API,我们将这个元素附加到页面上已经存在的根节点rootElement。然后通过render方法将React元素实际渲染到DOM中。

最后的效果是在页面上显示一个带有"Hello World"文本的div元素。

这是React基本工作原理的一个很好的演示,展示了从UI描述符(React元素)到实际DOM渲染的流程。

010 嵌套元素

在这个练习中,我们将处理多个子元素并确保它们之间的空格正确显示。具体来说,我们会在一个React元素中创建两个span标签,一个用于显示"Hello",另一个用于显示"World",并确保它们之间有一个空格。

目标:

  1. 使用React创建两个span元素,一个显示"Hello",另一个显示"World"。
  2. 确保这两个元素之间有一个空格。

解决方案:

React允许我们将多个子元素传递给一个父元素,而不仅限于单个子元素。要实现这个需求,我们可以通过创建两个span元素并在它们之间添加一个空格或字符串来实现。

示例代码如下:

import React from 'react';
import ReactDOM from 'react-dom/client';

const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);

const element = React.createElement(
  'div', 
  null, 
  React.createElement('span', null, 'Hello'), 
  ' ',  // 这是我们用来添加空格的部分
  React.createElement('span', null, 'World')
);

root.render(element);

说明:

  1. React.createElement: 使用该方法分别创建两个span元素,分别包含"Hello"和"World"。
  2. 空格的处理: 在两个span元素之间直接添加一个字符串' '(一个空格字符),确保在它们之间有空格。

最终效果是页面上显示"Hello World",并且"Hello"和"World"之间有一个空格。

这个练习展示了如何在React中使用多个子元素,并处理它们之间的布局问题。

011 嵌套元素 (1)

在这个练习中,我们使用 React 的 createElement API 来构建嵌套的 UI,并处理多个子元素。这次的目标是创建两个 span 元素,一个显示“Hello”,另一个显示“World”,并确保它们之间有空格。

关键步骤:

  1. 使用 React.createElement 创建 span 元素:

    • 我们将使用 React.createElement('span', null, 'Hello') 创建第一个 span,内容为“Hello”。
    • 然后,创建第二个 span,内容为“World”。
  2. span 元素之间添加空格:

    • 通过直接传递一个空格字符串 ' ',React 将确保这个空格被渲染为文本节点,从而在两个 span 元素之间显示空格。
  3. 将所有元素作为子元素传递:

    • React.createElement 的第三个参数开始表示子元素,可以是文本、元素或数组形式。在这个例子中,我们通过传递多个子元素来构建 div 标签的内容。

以下是完整代码:

import React from 'react';
import ReactDOM from 'react-dom/client';

const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);

const element = React.createElement(
  'div', 
  null, 
  React.createElement('span', null, 'Hello'), 
  ' ',  // 这是用来添加空格的字符串
  React.createElement('span', null, 'World')
);

root.render(element);

解析:

注意点:

  1. React 处理字符串子元素: React 会自动将字符串转换为文本节点并插入到 DOM 中。因此,我们可以简单地传递 ' ' 来表示空格。
  2. 多个子元素: React.createElement 接受多个子元素作为第三个及后续参数。它们会一起渲染到父元素中。

通过这种方式,我们可以确保在页面上正确显示“Hello World”,并且“Hello”和“World”之间有一个空格。

012 深度嵌套元素

在这个练习中,我们将使用 React.createElement 来构建一个更加复杂的嵌套结构。最终的目标是创建一个包含 p 标签、一个 ul 列表及其多个 li 项的结构。当我们完成时,DOM 树应看起来如下:

<div class="container">
  <p>Some text here</p>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
  </ul>
</div>

任务描述:

代码实现:

import React from 'react';
import ReactDOM from 'react-dom/client';

const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);

// 创建包含嵌套元素的结构
const element = React.createElement(
  'div', 
  { className: 'container' }, // 父 div 容器
  React.createElement('p', null, 'Some text here'), // p 标签
  React.createElement(
    'ul', 
    null, 
    React.createElement('li', null, 'Item 1'), // li 项目1
    React.createElement('li', null, 'Item 2'), // li 项目2
    React.createElement('li', null, 'Item 3')  // li 项目3
  )
);

// 渲染到页面
root.render(element);

解析:

  1. 顶层 div 元素:

    • 使用 React.createElement('div', { className: 'container' }, ...) 创建一个 div,并赋予 className 为 "container"。
  2. p 标签:

    • React.createElement('p', null, 'Some text here') 创建一个 p 标签,包含文本内容 "Some text here"。
  3. ul 列表和 li 项目:

    • React.createElement('ul', null, ...) 创建一个 ul 列表,并在其中嵌套多个 li 项目。
  4. 嵌套结构:

    • ul 列表的每个 li 项是通过调用 React.createElement('li', null, 'Item X') 创建的,X 代表不同的项目编号。

总结:

虽然这个结构不算特别复杂,但通过这种方式嵌套多个 createElement 调用,可以让我们深刻体会到 JSX 的优势。直接使用 createElement 来构建复杂的 UI 结构时,代码的可读性会变差,层次感也不明显。而在 JSX 中,嵌套结构可以以更接近 HTML 的方式书写,既直观又高效。

这个练习让我们理解 React.createElement 的灵活性和强大之处,同时也预示了 JSX 如何简化开发体验。

013 深度嵌套元素 (1)

在这个练习中,我们使用了 React.createElement 来创建更复杂的嵌套 UI 结构。与之前的 Hello World 相比,我们现在正在构建一个包含 <p> 标签、<ul> 列表和多个 <li> 项的结构。这个结构展示了 React 元素嵌套的工作方式,但同时也让我们理解到 JSX 在处理大量嵌套时的优势。

代码实现

import React from 'react';
import ReactDOM from 'react-dom/client';

// 获取 root 元素
const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);

// 使用 createElement 创建嵌套的 UI 结构
const element = React.createElement(
  'div', 
  { className: 'container' }, 
  React.createElement('p', null, "Sam's favorite food"),  // p 标签
  React.createElement(
    'ul', 
    { className: 'sams-food' },  // ul 列表,带有 className 属性
    React.createElement('li', null, 'Green eggs'),  // li 项目1
    React.createElement('li', null, 'Ham')  // li 项目2
  )
);

// 渲染到页面
root.render(element);

解析:

  1. p 标签

    • 使用 React.createElement('p', null, "Sam's favorite food") 创建一个 p 标签,显示文本内容为 "Sam's favorite food"。
  2. ul 列表

    • React.createElement('ul', { className: 'sams-food' }, ...) 创建一个带有 classNamesams-foodul 列表,并在其中嵌套多个 li 项。
  3. li 项目

    • React.createElement('li', null, 'Green eggs')React.createElement('li', null, 'Ham') 分别创建两个列表项,显示 "Green eggs" 和 "Ham"。

总结:

虽然 React.createElement 提供了创建复杂 UI 的能力,但随着嵌套层级增加,代码的可读性和维护性可能会变差。这就是 JSX 存在的主要原因:它让我们用类似 HTML 的语法来编写 React 元素,大大提高了代码的可读性和简洁性。

通过这个练习,你已经学会了如何使用 React.createElement 创建嵌套的 UI 结构,同时也体验到当 UI 复杂度增加时,JSX 的优势会变得非常明显。

014 爸爸笑话时间 原生React API

哈哈,那个笑话真是让人忍俊不禁!抓住一个放了一个,真是个有趣的双关。希望这个小休息能让你放松一下,记得保持水分和适当休息哦!当你准备好了,我们会继续学习更多的 React 知识,深入探索这个强大的框架,掌握更多有趣的技巧!

015 使用JSX简介

JSX 确实为 React 编写用户界面带来了很大的方便,它使得我们可以以接近 HTML 的语法编写代码,而不必通过复杂的 createElement API 一层层嵌套调用。

React 团队创建了 JSX 语法,它本质上是一种让你在 JavaScript 中写 XML 类似的语法,JSX 然后会通过编译器(如 Babel)转换为 React 的 createElement 调用。虽然浏览器本身不理解 JSX 语法,但是通过像 Babel 这样的工具,JSX 可以被编译成常规的 JavaScript,从而在浏览器中运行。

为了保持事情简单,React 的 JSX 编译器 Babel 可以在浏览器中直接运行,因此我们可以避免使用复杂的构建工具。在本次练习中,你将学习如何在浏览器中通过引入 Babel 来编写 JSX 语法,并了解 JSX 是如何转换为 React.createElement 调用的。

在这项练习中,你将体验到 JSX 的强大和简洁性,让编写 UI 变得更加高效流畅。准备好了吗?让我们一起开始学习 JSX 吧!

016 编译JSX

在这个练习中,我们将把 Babel 添加到页面中,并将 createElement 调用转换为 JSX 语法。

首先,我们需要在页面中引入 Babel,这次我们会使用 Babel 的单独版本(Babel standalone),它是一个打包好的单个脚本,可以直接在浏览器中加载并执行。通过这个脚本,Babel 会在页面中查找具有特定 type 类型的 <script> 标签,并对其内容进行编译,最终生成新的脚本标签让浏览器评估执行。

具体步骤如下:

  1. 加载 Babel standalone:

    • 我们将 Babel standalone 文件放在公共目录下。在 HTML 文件中,通过 <script> 标签将其引入页面。
  2. 更新 <script> 标签:

    • 我们需要将使用 React 的 <script> 标签的 type 属性设置为 "text/babel",这会告诉 Babel,它应该编译这个脚本中的 JSX 内容。
  3. 转换 JSX:

    • 将我们当前使用的 createElement API 替换为 JSX 语法。Babel 会自动将 JSX 转换为对应的 React.createElement 调用。

这个过程让你能够直观地了解 JSX 如何转换为常规的 JavaScript 调用,虽然这种方式不常用于生产环境,但它能帮助你在没有构建工具的情况下运行 React 项目。

祝你练习愉快!

017 编译JSX (1)

在这个练习中,我们已经成功地将 Babel 添加到页面中,并且将 createElement 调用转换为了 JSX 语法。通过这个过程,我们学到了几个关键点:

  1. 添加 Babel: Babel 是一个编译器,可以将 JSX 代码转换为 JavaScript 代码,让浏览器能够理解和执行。虽然我们在练习中加载了一个较大的 Babel standalone 文件,这种方法不适合生产环境,但它适合我们进行开发和学习。

  2. 使用 JSX 替代 createElement 调用: 我们将 JSX 替换了原本的 React.createElement 调用。JSX 语法更接近 HTML,简洁直观,可以更容易地编写 React 组件。

  3. Babel 编译: Babel 会自动查找类型为 text/babel<script> 标签,编译其中的内容,并将其转化为浏览器可以执行的 JavaScript 代码。在生产环境中,你通常会使用编译工具(如 Webpack 等)来实现这一过程。

  4. 模块导入: Babel 编译后的代码依赖于 React.createElement,所以我们需要确保在代码中引入 React。通过 import * as React from 'react';,我们可以确保 React 被正确导入和引用。

  5. JSX 的好处: 使用 JSX 语法,让我们可以轻松地嵌套和构建复杂的 UI 元素,而不必手动编写繁琐的 createElement 调用。这样可以大大提高开发效率和代码的可读性。

接下来,我们可以继续探索 JSX 的更多特性和强大之处,并且通过这些练习加深对 React 的理解。

018 插值

在这个练习中,我们讨论了插值(Interpolation)的概念,特别是在 JSX 中的应用。在 React 和 JSX 中,插值是将 JavaScript 代码嵌入到 JSX 表达式中的一种方式,这与在模板字符串(Template Literal)中的插值非常类似。

关键点回顾:

  1. 插值的概念: 插值的核心思想是允许在字符串或 JSX 中嵌入 JavaScript 表达式。通过这种方式,我们可以动态地生成内容。例如,使用反引号 (`) 和 ${} 在模板字符串中嵌入变量或表达式,这与在 JSX 中使用花括号 {} 插入 JavaScript 表达式类似。

  2. JSX 中的插值: 在 JSX 中,你可以通过花括号 {} 在 HTML 标签的属性和内容中嵌入 JavaScript 表达式。例如:

    const name = "React";
    return <div className={`container ${name}`}>Hello, {name}!</div>;

    在这个例子中,classNamediv 的内容都使用了插值,将 JavaScript 表达式插入 JSX。

  3. 插值位置

    • 属性:你可以在 JSX 元素的属性中使用插值,例如 className={classNameVariable}
    • 内容:你还可以在元素的内容中插入变量或表达式,例如 <div>{message}</div>,其中 message 是一个变量。
  4. JSX 和 JavaScript 模式的切换: 在 JSX 中使用 {} 插入 JavaScript 表达式相当于在普通 JavaScript 中使用 ${} 插入变量或表达式。花括号 {} 是一种告诉 JSX 解析器 "进入 JavaScript 模式" 的方式,解析器会解析 {} 中的表达式,然后返回其结果并插入到 DOM 中。

总结:

插值是 React 和 JSX 的重要特性,它使得我们可以轻松地将动态数据插入到组件中,使得我们的 UI 更加灵活和可复用。在接下来的练习中,你将能够实践这些插值的技巧,从而进一步掌握它们的应用。

019 插值 (1)

在这个练习中,我们讨论了如何在 JSX 中进行插值(Interpolation),特别是在 JSX 和 JavaScript 之间切换的方式。

关键点总结:

  1. JSX 与 JavaScript 的切换: 在 JSX 中,通过使用 {} 可以将 JavaScript 表达式嵌入到 JSX 中。当 JSX 编译时,任何在 {} 中的内容都会被当作 JavaScript 表达式进行评估,并插入到对应的位置。例如:

    const className = "container";
    const children = "Hello World";
    return <div className={className}>{children}</div>;

    这里的 classNamechildren 都是通过插值的方式传递给 JSX 元素。

  2. 插值的语法

    • 属性插值:可以将 JavaScript 表达式作为 JSX 元素的属性值,例如 className={className}
    • 内容插值:可以将 JavaScript 表达式作为元素的子元素插入,例如 <div>{children}</div>
  3. 切换状态

    • JSX 语法:当使用 <div></div> 等 JSX 语法时,React 正在解析类似于 HTML 的代码。
    • 进入 JavaScript 模式:当我们使用 {} 包裹表达式时,解析器会将其转换为 JavaScript 代码,执行后插入结果。
    • 回到 JSX 模式:一旦表达式结束,解析器会返回到 JSX 语法中。
  4. 自闭合标签: 在 JSX 中可以使用自闭合标签,例如 <img /><input />,这是 JSX 的一项简化功能,它允许你在没有子元素时不必书写结束标签。

  5. 表达式与逻辑

    • 只能插入表达式:在 JSX 中,你只能使用表达式,不能使用语句。例如,不能使用 iffor 语句,但可以使用三元运算符来控制逻辑:
      {isTrue ? <p>True</p> : <p>False</p>}
    • 表达式的编译:React 会将 JSX 中的插值内容直接编译成相应的 JavaScript 代码,将结果插入到最终的 DOM 中。

通过这些概念的掌握,你将能够更好地在 JSX 中处理动态内容和属性,使得你的 React 应用更加灵活和强大。

020 Spread props

在这个练习中,我们介绍了如何将一个包含多个属性的对象应用到 JSX 元素上,而无需单独为每个属性进行手动设置。你可能会经常遇到这种情况,尤其是在属性动态生成或者属性数量较多的场景下。

使用属性对象的传统方式:

假设我们有一个 props 对象,其中包含了 classNamechildren 等属性。常规的做法是这样写:

const props = {
  className: "container",
  children: "Hello World",
};

return (
  <div className={props.className}>
    {props.children}
  </div>
);

这需要你逐个指定对象中的属性,例如 className={props.className}children={props.children}

更优雅的方式——使用 JSX 的 Spread 属性:

为了避免手动传递每个属性,JSX 提供了一种更简洁的方法,即 属性扩展语法,类似于 JavaScript 中的 扩展操作符。你可以通过 ...props 将对象中的所有属性应用到 JSX 元素上:

const props = {
  className: "container",
  children: "Hello World",
};

return (
  <div {...props} />
);

工作原理:

...props 会将 props 对象中的每个键值对分别扩展为该元素的属性。上面的代码与以下手动传递属性的代码是等效的:

<div className="container">Hello World</div>

使用场景:

这种写法特别适合以下场景:

  1. 动态属性:当属性的数量和内容是动态变化时,例如根据 API 响应或函数返回的结果动态生成属性。
  2. 简化代码:减少重复代码,尤其是在属性较多的情况下,使代码更加简洁和易于维护。

通过使用这种方式,你可以更轻松地处理具有多个属性的 JSX 元素。

021 Spread props (1)

在这个练习中,我们探讨了如何在 JSX 中使用扩展语法将一个对象中的所有属性应用到某个元素上。这种方式对于处理动态属性或者复杂的属性集合时非常有用。

扩展操作符的工作原理

我们可以使用 ...props 将一个对象中的所有属性“展开”并应用到 JSX 元素上。例如:

const props = {
  className: "container",
  children: "Hello World",
};

return <div {...props} />;

这段代码会将 props 对象中的 classNamechildren 属性直接应用到 div 元素上,等效于手动指定这些属性:

<div className="container">Hello World</div>

覆盖属性

扩展操作符的一个关键点是属性覆盖。如果你在展开属性对象后又指定了同名的属性,那么 JSX 会采用后面定义的属性值。例如:

const props = {
  className: "container",
  children: "Hello World",
};

return <div {...props} className="my-container" />;

在这个例子中,className="my-container" 会覆盖 props 中的 className="container",因为它出现在 ...props 之后。

输出的结果是:

<div class="my-container">Hello World</div>

特殊的 children 属性

children 属性有点特殊。如果你直接在元素标签之间定义了内容,例如:

return <div {...props}>Goodbye World</div>;

此时,"Goodbye World" 会覆盖 props 对象中的 children 属性。换句话说,标签内部的内容优先级更高。

复杂的属性扩展

你可以将多个属性对象进行扩展,并且扩展顺序决定了最终应用的属性。例如:

const props1 = { className: "container" };
const props2 = { className: "my-container" };

return <div {...props1} {...props2} />;

在这个例子中,props2 中的 className="my-container" 会覆盖 props1 中的 className="container",因为 props2 位于 props1 之后。

输出的结果是:

<div class="my-container"></div>

通过这种方式,你可以灵活地管理和覆盖 JSX 元素的属性,同时减少手动编写重复代码的麻烦。

022 嵌套JSX

在这个练习中,我们将把之前使用 createElement 创建的 "Sam's Favorite Food" 列表(包含绿色鸡蛋和火腿)转化为使用 JSX 编写的版本。这将展示 JSX 在处理嵌套结构时的简洁性和可读性。

使用 JSX 进行嵌套

createElement 中,我们需要通过多层函数调用来构建嵌套的 HTML 结构。而使用 JSX 时,编写这样的嵌套结构会更加直观和接近原生的 HTML。以下是我们如何使用 JSX 来重现 "Sam's Favorite Food" 列表的代码:

function FavoriteFood() {
  return (
    <div className="container">
      <p>Sam's favorite food:</p>
      <ul className="sam-food">
        <li>Green Eggs</li>
        <li>Ham</li>
      </ul>
    </div>
  );
}

createElement 的区别

createElement 的多层函数调用不同,JSX 更加贴近 HTML 的语法,并且在可读性和直观性上有显著提高:

  1. 更接近 HTML: JSX 看起来就像 HTML,减少了函数嵌套的复杂性。
  2. 简洁明了: 你可以直接在代码中看到结构的嵌套层级,而不需要通过多个函数参数的方式进行传递。
  3. 代码更干净: 不需要反复调用 createElement,让代码变得更加简洁易懂。

额外注意点:JSX 与 HTML 的区别

  1. class 与 className: 在 JSX 中,HTML 中的 class 属性被改为 className,因为 class 是 JavaScript 中的保留字。
  2. 闭合标签: 在 JSX 中,像 <img /><br /> 这样的单标签元素需要自闭合。

通过这种方式,JSX 让我们更轻松地处理复杂的嵌套结构,尤其是在大型应用程序中,它可以极大地简化代码的编写与维护。

023 嵌套JSX (1)

在这个片段中,讲解了如何通过将 HTML 代码直接复制粘贴到 JSX 中,并且展示了在 JSX 中处理嵌套结构是多么简单和高效。然而,JSX 与 HTML 之间有一些重要的区别,尤其是关于属性的命名。例如:

  1. classclassName 的区别:在 JSX 中,我们不能像在 HTML 中那样使用 class 属性,因为 class 在 JavaScript 中是一个保留字。在 JSX 中,你需要使用 className 来指定 CSS 类名。这是因为 JSX 更关注的是 DOM 属性,而不是 HTML 属性。

  2. HTML 属性 vs. DOM 属性:JSX 使用的是 DOM 属性,而不是 HTML 属性。例如,表单中的 for 属性在 JSX 中应该写作 htmlFor。这种转换有助于避免与 JavaScript 保留字的冲突,并保持一致性。

  3. 改进后的编程体验:通过 JSX,我们不再需要使用 React.createElement 来手动创建和嵌套元素。JSX 更加直观和简洁,类似于 HTML,这使得编写复杂的用户界面变得更加容易。

结论:

使用 JSX 明显提高了代码的可读性和开发效率,特别是在处理复杂的嵌套结构时。虽然 JSX 和 HTML 之间存在一些细微的差异(如属性命名),但这都是为了与 JavaScript 保持一致,并优化开发体验。

024 片段

在这一段中,讲解了如何使用 React Fragments 来避免不必要的包裹元素,尤其是在某些布局需求(如 CSS Grid 或 Flexbox)中可能非常有用。

背景:

通常情况下,React 元素必须被一个单一的父元素包裹。这意味着如果你想返回多个兄弟元素(而不希望它们被一个 div 包裹),你可以使用 React Fragments 来实现。Fragments 允许你返回多个元素,而不在 DOM 中添加额外的包裹元素。

Fragments 有两种写法:

  1. 标准写法

    <React.Fragment>
     <Element1 />
     <Element2 />
    </React.Fragment>

    或者你可以先导入 Fragment

    import { Fragment } from 'react';

    然后:

    <Fragment>
     <Element1 />
     <Element2 />
    </Fragment>
  2. 简洁写法: 你可以用简写的形式,省略 Fragment 的名字,只需使用空标签包裹内容:

    <>
     <Element1 />
     <Element2 />
    </>

什么时候用 Fragment?

通过使用 Fragment,你可以确保只输出你需要的元素,而不会在 DOM 中添加额外的节点,这对于保持 DOM 结构简洁和避免不必要的布局影响非常有帮助。

总结:

通过使用 React Fragments,你可以避免多余的容器元素,同时保持 JSX 代码的简洁和清晰。这种技巧特别适合处理复杂的布局结构,或者在组件返回多个元素时更好地控制 DOM 结构。

025 片段 (1)

这一段解释了 React Fragments 的使用,并展示了为什么需要使用它来避免不必要的 div 包裹,同时还深入讲解了为何在 JavaScript 中无法直接返回多个顶层元素。

关键点:

  1. 问题的来源

    • 在 React 中,组件返回的 JSX 必须被一个父元素包裹。当你试图直接返回多个元素时,比如一个 div 和一个 ul,会导致编译错误,因为 JavaScript 无法让一个变量同时指向两个不同的值。
  2. 为什么不能直接移除父元素

    • 如果你去掉了包裹元素(比如 div),React 会抛出错误,因为它无法理解如何将多个兄弟元素直接渲染在 DOM 中。JavaScript 不支持给一个变量赋值多个值,这就是为什么必须有一个父元素包裹它们。
  3. React Fragments 的引入

    • 为了解决这个问题,React 提供了 Fragment,它是一个 "虚拟" 的容器,允许你返回多个元素,但不会在 DOM 中生成任何实际的包裹元素。
    • 这非常有用,尤其是在你想要避免多余的 div 或者 span,但仍然需要满足 React 的语法要求时。
  4. Fragment 的两种写法

    • 标准写法
      <React.Fragment>
      <Element1 />
      <Element2 />
      </React.Fragment>
    • 简写形式
      <>
      <Element1 />
      <Element2 />
      </>
    • 简写形式更简洁,通常开发者更喜欢使用。

结论:

通过使用 React Fragments,你可以在不生成多余 DOM 元素的情况下返回多个兄弟元素,这使得代码更简洁,同时也避免了多余的嵌套层级。它非常适合在 CSS 布局或组件返回多个元素时使用。

026 爸爸笑话时间 使用JSX

哈哈!这个笑话很轻松有趣:“什么样的贝果会飞?答案是——普通贝果(plane bagel)!” 这也是个非常好的提示,让我们放松一下、站起来活动活动。

现在正是时候伸展一下身体,去喝点水或者吃个小零食。如果有机会,还可以去对别人说点鼓励的话,帮助别人也会让自己感觉很好。之后回来继续学习更多有趣的 React 内容!

027 自定义组件简介

在这个练习中,我们开始了解自定义组件的概念。在 React 中,自定义组件其实很简单,它就是一个函数,该函数接受一个对象(通常是 props),并返回一些可以渲染的内容。那就是自定义组件的全部定义。

通常,返回的内容是 React 元素,但实际上它也可以是一个字符串、数字等可渲染的值。从本质上讲,自定义组件是你可以传递给 createElement API 的东西。在 JSX 中,自定义组件有专门的语法,可以让你轻松地在 JSX 中使用这些自定义组件。

这里有一个简单的例子:一个名为 greeting 的函数组件,它接收 props 对象,然后这些 props 就是你渲染该组件时传递的值。

在接下来的练习中,你会逐步通过这些步骤来熟悉自定义组件的创建和使用,学习 JSX 是如何将这些组件编译成实际的函数调用,并了解 React 是如何处理这些调用的。

这个过程很有趣,也非常有用。希望你在接下来的练习中玩得开心,祝你好运!

028 简单函数

在这个练习的第一部分,我们将逐步靠近创建一个可以生成可重用 JSX 的通用函数。现在我们有一个容器,它里面有两个消息,分别是“hello world”和“goodbye world”。显然,这里有一些重复的代码,我们可以优化它。

为了使代码更加通用,你可以编写一个函数,这个函数可以接收动态的子元素 children,从而减少重复。例如,你可以创建一个名为 message 的函数,它允许你传递不同的 children 内容。

这个练习的目标是让你实现这样一个通用的函数接口。虽然这还不是一个完整的 React 组件,但我们正在逐渐靠近 React 组件的形式。通过插值(interpolation),我们可以将函数调用的结果作为表达式插入 JSX 中,而函数返回的 React 元素可以作为 div 的子元素渲染。

任务:你需要编写这个通用的 message 函数,并通过插值将它的输出作为 JSX 的一部分。

祝你在这个过程中玩得愉快,完成之后我们会继续!

029 简单函数 (1)

在这个步骤中,我们通过创建一个 message 函数来减少代码重复。这个函数接收一个对象作为参数,并将其子元素 children 渲染到指定位置。最终,我们可以调用这个函数,传递需要显示的内容,从而避免代码的重复。

通过使用这个 message 函数,如果你想要在多个地方更新样式或结构,你只需要修改函数内部,而不必手动修改每个使用的地方。这就是减少代码重复的优势。

这个练习让我们更接近于 React 组件的结构,虽然当前的写法还不是完全的 React 组件形式,但它展示了如何通过参数化来灵活地处理 JSX 的渲染。在下一步中,我们将进一步探索真正的 React 组件并逐步优化这一过程。

祝你在这个过程愉快!

030 原生API

在这个步骤中,我们将进一步优化代码,使其更加符合 React 组件的语义,特别是通过使用 CreateElement API。

目标:

我们需要将自定义的 message 函数转化为真正的 React 组件,并通过 CreateElement API 来处理它,而不是直接调用函数。通过这种方式,React 将能够更好地管理组件的生命周期,比如何时渲染或更新组件。

步骤:

  1. 组件的定义: 你仍然会保留 message 函数,但是不再直接调用它。相反,你会将这个函数作为一个特殊的元素传递给 React.createElement。这样 React 就会负责调用这个函数,而不是你手动调用它。

  2. 传递 props: 在 React 中,当你通过 CreateElement API 创建一个元素时,所有的 props 都会被组合成一个对象并传递给组件。我们会把 children 作为这个 props 的一部分。

  3. Render 流程: 通过 React.createElement API 传递 message 函数,然后由 React 来调用这个函数,并将 props 传递给它。

代码示例:

function Message({ children }) {
    console.log('Rendering Message component');
    return <div className="message">{children}</div>;
}

// 使用 CreateElement API
const element = React.createElement(Message, null, 'Hello World');

// Render 到 DOM
ReactDOM.createRoot(document.getElementById('root')).render(element);

在这个例子中,我们定义了一个 Message 组件,它接受一个 children prop 并渲染它。然后,我们使用 React.createElement 来创建这个组件的实例,而不是手动调用它。React 会自动处理组件的调用并传递 props。

通过这种方法,当你引入更多复杂的功能,比如 hooks 时,你会发现 React 组件的这种形式非常重要和方便。

你可以通过在 Message 函数和 React.createElement 的各个位置添加 console.log,来观察 React 是如何管理组件的渲染的。

总结:

希望你在这个过程中愉快探索!

031 原生API (1)

在这个步骤中,我们通过使用 React.createElement 进一步加深了对 React 组件的理解,并学习了如何让 React 自己决定何时调用我们的自定义组件。通过这种方式,我们的组件更符合 React 的生命周期和渲染机制。

关键点总结:

  1. React.createElement 调用: 我们将 message 函数作为自定义组件传递给 React.createElement,并让 React 负责在渲染时调用该组件。React 会传递 props,并在必要时调用组件函数。

  2. 延迟调用: 使用 React.createElement 后,React 将延迟调用组件,直到真正需要渲染时才调用组件。这与我们直接调用组件函数的方式不同。之前的方式是立即调用函数,而现在是由 React 自己来控制。

  3. React 特有的元素类型: 当我们使用 React.createElement 时,React 创建了一个特殊的 React 元素,其类型不是普通的 HTML 标签,而是我们的自定义组件。这为组件的扩展和复用提供了可能性。

  4. 生命周期: 在我们继续深入理解 React 的状态管理和生命周期方法时,使用 React.createElement 这种方式的优势会变得更加明显。React 可以更高效地管理组件的渲染和更新,同时保持应用的状态隔离和一致性。

结论:

通过这一步,我们的组件已经在语义上符合了 React 的工作方式,即 React 自己负责组件的调用和管理。而接下来我们将进一步完善 JSX 语法,使自定义组件的使用更加简洁和直观。

希望这个过程帮助你更好地理解了 React.createElement 的机制和自定义组件的工作原理。

032 JSX组件

在这个练习中,我们的目标是从使用 React.createElement 过渡到更简洁、易读的 JSX 语法。不过需要注意的是:自定义组件的名称大小写非常重要。

关键概念:JSX 中的大小写规则

在使用 JSX 时,React 会将小写字母开头的名称(例如 divspan)视为 HTML 标签。如果你使用了小写的自定义组件名称(例如 message),React 会将它当作一个字符串,认为它是一个 HTML 标签。然而,当你将名称首字母大写(例如 Message)时,React 就知道这是一个函数引用(自定义组件)。

操作步骤:

  1. 首先不要大写自定义组件的名称,将它切换为 JSX 语法,并观察 React 在编译后的输出是如何处理的。
  2. 然后将函数名称首字母大写,看看 React 如何正确识别它为一个自定义组件。

通过这个过程,你将理解 React 如何编译 JSX 以及它如何区分 HTML 元素和自定义组件。

步骤:

  1. 不大写组件名称: 首先,将你的自定义组件名称写成小写形式,并查看 React 如何将其编译为字符串:

    const element = (
     <div>
       <message>Hello World</message>
     </div>
    );
  2. 将名称首字母大写: 现在,将组件名称首字母大写,查看编译后的不同:

    const element = (
     <div>
       <Message>Hello World</Message>
     </div>
    );

结果:

当你将名称大写时,React 会识别 Message 是一个自定义组件,而不是一个 HTML 标签,并会调用函数,而不是将其当作字符串标签名处理。

这是以后你在 JSX 中编写 React 组件的标准方式,它比 React.createElement 提供了更清晰、可读性更高的语法。

033 JSX组件 (1)

一开始,我们会将 React.createElement 替换为 JSX 语法。让我们把 message 标签应用到 JSX 中:

<Message>Hello World</Message>
<Message>Goodbye World</Message>

发现问题

表面上看,这似乎是有效的,但我们会得到一个警告:“浏览器无法识别标签 message”。这意味着 JSX 正在尝试将 message 作为一个 HTML 标签,而不是 React 组件。React 提示我们:如果要渲染一个 React 组件,请将名称的首字母大写。

解决问题

为了让 React 识别这是一个自定义组件,而不是原生 DOM 元素,我们需要将自定义组件的名称首字母大写:

<Message>Hello World</Message>
<Message>Goodbye World</Message>

解析过程

当我们查看编译后的输出时,原来使用小写 message 时,React 编译器会将其解释为字符串 'message',即浏览器尝试渲染一个不存在的原生 DOM 元素。而当我们将其大写为 Message 后,React 就能识别这是一个函数引用,并调用对应的 React 组件。

通过这种方式,你可以轻松地创建并使用自定义组件,并遵循 React 的约定使代码更加规范和清晰。

小结

React 使用组件名称的大小写来区分 HTML 元素和自定义组件。因此,当定义自定义组件时,始终记得将其名称首字母大写,以确保 JSX 能够正确编译并渲染组件。

034 Props

这个练习是为了加深你对自定义组件和 props 的理解。我们将创建一个简单的计算器组件,这个组件将接收 leftoperatorright 作为属性,并渲染一个包含计算结果的 div

任务概述

你需要实现一个 Calculator 组件,它会:

  1. 接受 left(左操作数)、operator(运算符)和 right(右操作数)作为 props
  2. 根据这些 props 生成一个计算表达式,比如:3 + 5
  3. 然后输出这个表达式的结果。

示例代码

下面是一个简单的实现思路:

function Calculator({ left, operator, right }) {
  let result;

  switch (operator) {
    case '+':
      result = left + right;
      break;
    case '-':
      result = left - right;
      break;
    case '*':
      result = left * right;
      break;
    case '/':
      result = right !== 0 ? left / right : 'Error';
      break;
    default:
      result = 'Invalid operator';
  }

  return (
    <div>
      {left} {operator} {right} = {result}
    </div>
  );
}

// 使用示例
<Calculator left={3} operator="+" right={5} />
<Calculator left={10} operator="*" right={2} />

解析

下一步

尝试使用不同的 props 调用 Calculator 组件,并查看其工作效果。这个练习旨在帮助你理解如何通过 props 在 React 中传递数据并创建动态组件。

035 Props (1)

在这个练习中,我们创建了一个 Calculator 组件,并通过解构 props 来获取 leftoperatorright 三个参数。然后根据这些参数执行运算,并展示结果。

解析

  1. 解构 props 我们从传入的 props 对象中解构出 leftoperatorright。这种方式可以让代码更加简洁和可读。

  2. 执行运算: 使用 operator 来决定执行哪种运算(加、减、乘、除),并将结果存储在 result 变量中。这个步骤模拟了简单的数学运算。

  3. 渲染结果: 我们使用 JSX 来将 leftoperatorright 的值显示出来,并将计算结果显示在 output 标签中,以便于提高无障碍性。

示例代码:

function Calculator({ left, operator, right }) {
  let result;

  switch (operator) {
    case '+':
      result = left + right;
      break;
    case '-':
      result = left - right;
      break;
    case '*':
      result = left * right;
      break;
    case '/':
      result = right !== 0 ? left / right : 'Error';
      break;
    default:
      result = 'Invalid operator';
  }

  return (
    <div>
      <code>{left} {operator} {right}</code> = <output>{result}</output>
    </div>
  );
}

// 使用示例
<Calculator left={1} operator="+" right={2} />
<Calculator left={1} operator="/" right={2} />

关键点

  1. 灵活的 props 组件的 props 可以是任意类型,不仅限于原始类型(如数字、字符串),还可以是对象或其他 React 元素。

  2. JSX 语法: 在 JSX 中,通过 {} 可以插入 JavaScript 表达式来显示变量或计算结果。

  3. 组件本质: React 组件本质上是一个函数,它接受 props 作为参数,并返回需要渲染的 JSX。

通过这个练习,你可以进一步熟悉如何使用 props 传递数据,并在 React 组件中灵活渲染动态内容。

036 爸爸笑话时间 自定义组件

哈哈,Michael Jackson 的笑话和 React Router 的双关真是有趣!看来你学到很多 React 的知识,确实是时候让这些内容在大脑中“炖一炖”了!趁着这个休息时间,站起来活动一下,让身体和大脑都焕然一新。

在你休息之后,我们还会继续深入学习 React 的精彩部分。我很期待你回来,咱们继续一起学习 React 的其他酷炫内容!

037 TypeScript简介

从现在开始,我们将使用 TypeScript 来完成所有的开发。TypeScript 是一种建立在 JavaScript 之上的强类型语言,它可以为你带来类型安全性。

为什么要用 TypeScript?

类型安全的最大好处就是,当别人调用你的函数时,如果传递的参数类型不对,TypeScript 会在编译时给你报错。比如,如果一个函数需要传入字符串(因为可能会用到 .toUpperCase() 这样的操作),但你传入了一个数字,TypeScript 就会提前告知你。在普通的 JavaScript 中,这是不会被捕获的。

整个行业目前基本都在使用 TypeScript,你也肯定想跟上这个趋势。接下来我们将开始在这个练习中探索 TypeScript。

为什么选择 TypeScript?

有些人可能会觉得 TypeScript 会给他们的代码带来额外的“红色波浪线”,但其实这些都是帮助你避免潜在错误的警告。可以把 TypeScript 想象成一个“无情但诚实的朋友”,虽然它会指出代码中的问题,但它的目的是为了避免你犯下严重的错误。

TypeScript 和 React 的结合

React 组件本质上是接受对象(props)的函数,并返回可以渲染的东西。从 TypeScript 的角度来看,你只需要为这些函数定义类型。没有什么特别的规则,就是为函数加上类型定义。

TypeScript 函数的类型定义

这是一个普通的 JavaScript 函数:

function getUserDisplayName(user) {
  return user.name || 'Unknown';
}

如果我们用 TypeScript,可以这样定义类型:

function getUserDisplayName(user: { name?: string }): string {
  return user.name || 'Unknown';
}

上面的 user 参数必须是一个对象,并且可能有一个可选的 name 属性,类型为 string。TypeScript 会帮你确保返回的结果始终是一个 string

TypeScript 中的 React 组件

下面是一个没有类型定义的 React 组件:

const Message = ({ children }) => <div>{children}</div>;

我们可以使用 TypeScript 为 children 添加类型:

const Message: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  return <div>{children}</div>;
};

这样,children 可以是任何 React 可渲染的内容,比如字符串、元素或其它组件。

使用 TypeScript 的技巧

如果你刚开始使用 TypeScript,可能会因为某些类型错误感到困惑。这时候,不妨使用 // @ts-expect-error 来临时忽略这些错误。等你对 TypeScript 更加熟悉后,可以回过头来修复这些问题。

我们将在接下来的练习中更深入地使用 TypeScript,帮助你写出更加健壮的 React 代码。

038 Props (2)

在这个练习中,我们将为计算器组件添加类型安全,以确保左操作数、运算符和右操作数都具有正确的类型。这将帮助我们避免潜在的错误,比如传入一个字符串而不是数字。

步骤一:定义 CalculatorProps 类型

首先,我们需要定义一个类型来表示计算器的属性(props)。这可以通过创建一个接口来完成:

interface CalculatorProps {
  left: number;       // 左操作数
  operator: '+' | '-' | '*' | '/'; // 运算符
  right: number;      // 右操作数
}

步骤二:更新计算器组件

接下来,我们将使用这个类型来更新计算器组件的 props:

const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
  let result: number;

  switch (operator) {
    case '+':
      result = left + right;
      break;
    case '-':
      result = left - right;
      break;
    case '*':
      result = left * right;
      break;
    case '/':
      result = right !== 0 ? left / right : 0; // 防止除以零
      break;
    default:
      throw new Error(`Unknown operator: ${operator}`);
  }

  return (
    <div>
      <code>
        {left} {operator} {right} = {result}
      </code>
    </div>
  );
};

步骤三:在应用中使用计算器组件

最后,我们可以在应用中使用这个组件,并确保传递正确的 props:

<Calculator left={1} operator="+" right={2} />
<Calculator left={5} operator="-" right={3} />
<Calculator left={4} operator="*" right={2} />
<Calculator left={10} operator="/" right={2} />

注意事项

通过这样的设置,我们的计算器组件现在就具备了类型安全,这不仅提高了代码的健壮性,也提高了开发过程中的便利性。您可以继续在应用中添加更多的计算器实例,看看 TypeScript 如何为您提供更好的开发体验。

039 Props (3)

在这个练习中,我们将通过添加类型安全来改进计算器组件的使用体验。这样,开发者在使用组件时可以获得更好的错误提示和自动补全功能。

步骤一:定义 CalculatorProps 类型

首先,我们需要定义一个类型 CalculatorProps,以确保传递给计算器组件的参数符合预期:

interface CalculatorProps {
  left: number; // 左操作数
  operator: '+' | '-' | '*' | '/'; // 运算符,可以限制为特定的字符串
  right: number; // 右操作数
}

步骤二:更新计算器组件

接下来,我们将使用这个类型更新计算器组件的 props:

const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
  let result: number;

  switch (operator) {
    case '+':
      result = left + right;
      break;
    case '-':
      result = left - right;
      break;
    case '*':
      result = left * right;
      break;
    case '/':
      result = right !== 0 ? left / right : 0; // 防止除以零
      break;
    default:
      throw new Error(`Unknown operator: ${operator}`);
  }

  return (
    <div>
      <code>
        {left} {operator} {right} = {result}
      </code>
    </div>
  );
};

步骤三:在应用中使用计算器组件

使用组件时,我们确保传递正确的类型。例如:

<Calculator left={1} operator="+" right={2} />
<Calculator left={5} operator="-" right={3} />
<Calculator left={4} operator="*" right={2} />
<Calculator left={10} operator="/" right={2} />

错误测试

为了确保类型安全,我们可以故意传递错误的类型,比如将 left 设置为字符串,TypeScript 将会抛出错误,提醒你这个 prop 的类型不匹配:

<Calculator left={"one"} operator="+" right={2} /> // 会产生错误

小结

通过这种方式,我们可以确保组件的 props 类型安全,同时在开发过程中获得更好的错误提示和自动补全功能。这样做不仅提高了代码的可维护性,也提升了开发者的体验。继续在应用中添加更多的计算器实例,测试 TypeScript 的类型检查功能,确保你熟悉它的用法。

040 类型收窄

要将 operator 的类型限制为特定的字符串(如“+”、“-”、“*”和“/”),我们可以在 CalculatorProps 接口中使用字符串字面量类型。这将使 TypeScript 在编译时检查 operator 的值是否符合这些特定的运算符。

步骤一:更新 CalculatorProps 类型

首先,我们需要更新 CalculatorProps 接口,具体如下:

interface CalculatorProps {
  left: number; // 左操作数
  operator: '+' | '-' | '*' | '/'; // 限制运算符为特定的字符串
  right: number; // 右操作数
}

步骤二:更新计算器组件

在更新了类型定义后,我们的计算器组件会自动获得这些类型的安全性。代码如下:

const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
  let result: number;

  switch (operator) {
    case '+':
      result = left + right;
      break;
    case '-':
      result = left - right;
      break;
    case '*':
      result = left * right;
      break;
    case '/':
      result = right !== 0 ? left / right : 0; // 防止除以零
      break;
    default:
      throw new Error(`Unknown operator: ${operator}`);
  }

  return (
    <div>
      <code>
        {left} {operator} {right} = {result}
      </code>
    </div>
  );
};

步骤三:测试类型安全

现在,当你尝试将不支持的运算符传递给 Calculator 组件时,TypeScript 会在编译时发出错误:

<Calculator left={1} operator="+" right={2} /> // 正确
<Calculator left={5} operator="-" right={3} /> // 正确
<Calculator left={4} operator="*" right={2} /> // 正确
<Calculator left={10} operator="/" right={2} /> // 正确
<Calculator left={10} operator="%" right={2} /> // 错误:类型“%”不可赋值给类型“'+' | '-' | '*' | '/'”。

小结

通过将运算符类型限制为字符串字面量类型,开发者可以在编译时捕获潜在的错误,而不必在运行时检查。这提高了代码的安全性和可维护性。继续使用这些特性,在你的应用程序中进一步应用 TypeScript,确保在构建更复杂的功能时保持类型安全。

041 类型收窄 (1)

要限制运算符为特定字符串(如加法、减法、乘法和除法),并获得更好的类型安全和自动完成功能,可以使用 TypeScript 的字符串字面量类型。你已经成功实现了这个过程,并且得到了 TypeScript 的类型检查和代码编辑器的自动完成功能。这是 TypeScript 的强大之处,能够在开发过程中提供实时的反馈。

解决方案回顾

以下是你所做的关键步骤的总结:

  1. 定义 CalculatorProps 接口

    interface CalculatorProps {
     left: number; 
     operator: '+' | '-' | '*' | '/'; // 使用字符串字面量类型限制运算符
     right: number; 
    }
  2. 在组件中使用这些类型: 在 Calculator 组件中,你使用这些定义的类型来确保正确的参数传递。

    const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
       // 计算逻辑...
    };
  3. 错误处理: 通过 TypeScript 的类型检查,当你尝试传递不允许的运算符(如 **^)时,编辑器将给出错误提示,而不必运行应用程序。

进一步的改进

虽然当前实现已经很好地解决了问题,但你提到可以进一步优化。以下是一些潜在的改进方向:

  1. 使用枚举: 如果运算符数量较多或可能变化,考虑使用枚举来管理运算符。

    enum Operator {
     Add = '+',
     Subtract = '-',
     Multiply = '*',
     Divide = '/',
    }
    
    interface CalculatorProps {
     left: number;
     operator: Operator; // 使用枚举类型
     right: number;
    }
  2. 自动化运算符提示: 结合 TypeScript 的类型提示功能,增强用户体验。例如,可以在函数内部提供详细的错误信息或提示,指导用户如何正确使用组件。

  3. 运行时检查: 尽管 TypeScript 提供了静态检查,但在组件运行时也可以加入检查,以确保运算符的有效性(例如,抛出错误或警告)。

通过这些方法,你不仅可以提高代码的可读性和可维护性,还能让使用组件的开发者获得更好的体验。继续探索 TypeScript 的特性,将其应用于你的 React 项目中,以构建更安全、更高效的应用程序!

042 推导类型

为了使 TypeScript 的运算符更具可扩展性并避免手动添加每个运算符,你可以使用类似于之前所述的typeofkeyof关键字来创建派生类型。这样,我们就可以从现有的运算符对象中提取出所有的运算符,而不是手动维护一个字符串字面量类型。

实现步骤

以下是你可以遵循的步骤,以便在 TypeScript 中实现更灵活的运算符类型:

  1. 定义一个运算符对象: 首先,定义一个包含所有有效运算符的对象。这将允许你从这个对象中提取出运算符。

    const operations = {
     add: '+',
     subtract: '-',
     multiply: '*',
     divide: '/',
    } as const; // 使用 `as const` 来确保这是一个只读对象
  2. 提取运算符类型: 然后,使用typeofkeyof来提取运算符的类型。

    type Operator = typeof operations[keyof typeof operations]; // 这将是 '+' | '-' | '*' | '/'
  3. 更新 CalculatorProps 接口: 修改 CalculatorProps 接口,使其使用新的运算符类型。

    interface CalculatorProps {
     left: number;
     operator: Operator; // 使用派生的 Operator 类型
     right: number;
    }

完整示例

以下是一个完整的示例代码,展示了如何实现上述步骤:

const operations = {
  add: '+',
  subtract: '-',
  multiply: '*',
  divide: '/',
} as const;

type Operator = typeof operations[keyof typeof operations]; // '+' | '-' | '*' | '/'

interface CalculatorProps {
  left: number;
  operator: Operator;
  right: number;
}

const Calculator: React.FC<CalculatorProps> = ({ left, operator, right }) => {
  let result: number;

  switch (operator) {
    case '+':
      result = left + right;
      break;
    case '-':
      result = left - right;
      break;
    case '*':
      result = left * right;
      break;
    case '/':
      result = left / right;
      break;
    default:
      throw new Error(`Unsupported operator: ${operator}`);
  }

  return (
    <div>
      <code>
        {left} {operator} {right} = {result}
      </code>
    </div>
  );
};

总结

通过这种方式,你可以方便地添加或删除运算符,而不必手动维护类型定义。当你更新运算符对象时,TypeScript 会自动反映这些更改,保持类型的一致性。这种方法在开发过程中可以减少错误并提高代码的可维护性。

043 推导类型 (1)

很高兴你已经成功实现了运算符的类型派生!下面是对这个解决方案的进一步分析,帮助你理解这一过程的细节和优势。

概要

你所使用的解决方案利用了 TypeScript 的类型系统,通过从一个包含有效运算符的对象中提取出运算符类型。这不仅简化了代码,还使得类型定义更加灵活和可扩展。

具体实现

  1. 定义运算符对象: 你创建了一个对象 operations,它包含了所有有效的运算符,并且使用 as const 确保它是一个只读的常量。这使得 TypeScript 能够推断出对象的字面量类型。

    const operations = {
     add: '+',
     subtract: '-',
     multiply: '*',
     divide: '/',
     // 可以在这里添加其他运算符
    } as const;
  2. 提取运算符类型: 你通过使用 typeofkeyof 创建了一个运算符类型,这个类型自动从 operations 对象中派生而来。

    type Operator = typeof operations[keyof typeof operations]; // 结果是 '+' | '-' | '*' | '/'
  3. 更新 CalculatorProps 接口: 最后,你更新了 CalculatorProps 接口,将 operator 的类型改为新的 Operator 类型。

    interface CalculatorProps {
     left: number;
     operator: Operator; // 使用派生的 Operator 类型
     right: number;
    }

优势

总结

这种类型派生的方式非常有效,能够让你的代码更易于维护和扩展。继续保持这种方式,不断优化你的代码!如果有其他问题或需要进一步的帮助,请随时问我。

044 默认Props

好的,我们来讨论如何为计算器组件设置默认值并使某些属性变为可选属性。在 TypeScript 中,我们可以通过定义接口来实现这一点,同时结合提供默认值的方式。下面是如何完成这一任务的步骤。

步骤概述

  1. 修改 CalculatorProps 接口:使 leftrightoperator 属性变为可选。
  2. 设置默认值:在组件内部为这些属性提供默认值。

具体实现

1. 修改 CalculatorProps 接口

首先,我们需要更新 CalculatorProps 接口,以便将 leftrightoperator 属性标记为可选的。使用问号 ? 表示属性是可选的。

interface CalculatorProps {
    left?: number;      // 可选属性
    operator?: Operator; // 可选属性
    right?: number;     // 可选属性
}

2. 设置默认值

在你的计算器组件内部,你可以使用逻辑运算符来设置默认值。这样,如果传入的 props 中没有这些属性,组件就会使用默认值。

const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = '+', right = 0 }) => {
    // 现在 left、operator 和 right 都有默认值
    const result = operations[operator](left, right); // 计算结果
    return (
        <div>
            <code>{`${left} ${operator} ${right}`}</code>
            = <output>{result}</output>
        </div>
    );
};

完整代码示例

下面是完整的示例代码,展示如何将所有这些组合在一起:

import React from 'react';

// 定义运算符
const operations = {
    add: (a: number, b: number) => a + b,
    subtract: (a: number, b: number) => a - b,
    multiply: (a: number, b: number) => a * b,
    divide: (a: number, b: number) => a / b,
} as const;

// 定义可选的运算符类型
type Operator = typeof operations[keyof typeof operations];

// 定义 CalculatorProps 接口
interface CalculatorProps {
    left?: number;
    operator?: Operator;
    right?: number;
}

// Calculator 组件
const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = 'add', right = 0 }) => {
    const result = operations[operator](left, right);
    return (
        <div>
            <code>{`${left} ${operator} ${right}`}</code>
            = <output>{result}</output>
        </div>
    );
};

// 使用 Calculator 组件
const App = () => (
    <div>
        <Calculator left={1} right={2} operator="add" />
        <Calculator right={5} operator="multiply" />
        <Calculator left={4} />
        <Calculator />
    </div>
);

export default App;

总结

通过这些步骤,你已经成功地为计算器组件实现了可选属性和默认值。这不仅提高了组件的灵活性,也使得使用组件时的开发体验更加友好。如果用户不传入某些属性,组件会自动使用预设的默认值。这样,你可以确保组件始终能正常工作。继续加油!如果你还有其他问题,欢迎随时问我。

045 默认Props (1)

好的,我们来总结一下如何在 TypeScript 中为 React 组件设置可选属性和默认值。在这段代码中,我们的目标是为计算器组件的 leftrightoperator 属性提供默认值,并确保这些属性是可选的。

实现步骤

  1. 修改属性接口:将 leftrightoperator 属性标记为可选属性。
  2. 设置默认值:在组件内部为这些属性提供默认值。

具体实现

以下是一个简单的实现示例:

import React from 'react';

// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';

// 定义计算器属性接口
interface CalculatorProps {
    left?: number;      // 可选属性
    operator?: Operator; // 可选属性
    right?: number;     // 可选属性
}

// 定义运算操作
const operations: Record<Operator, (a: number, b: number) => number> = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => a / b,
};

// Calculator 组件
const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = 'add', right = 0 }) => {
    // 计算结果
    const result = operations[operator](left, right);

    return (
        <div>
            <code>{`${left} ${operator} ${right}`}</code>
            = <output>{result}</output>
        </div>
    );
};

// 使用 Calculator 组件
const App = () => (
    <div>
        <Calculator left={1} right={2} operator="add" />
        <Calculator right={5} operator="multiply" />
        <Calculator left={4} />
        <Calculator />
    </div>
);

export default App;

代码说明

  1. 接口定义:我们定义了 CalculatorProps 接口,其中 leftrightoperator 都是可选属性。

    interface CalculatorProps {
       left?: number;
       operator?: Operator;
       right?: number;
    }
  2. 默认值设置:在组件定义中,我们为 leftoperatorright 属性设置了默认值。这意味着如果没有传入这些属性,组件会使用默认值:

    const Calculator: React.FC<CalculatorProps> = ({ left = 0, operator = 'add', right = 0 }) => {
       const result = operations[operator](left, right);
       // ... 组件的返回部分
    };
  3. 运算操作:使用一个记录类型 Record<Operator, (a: number, b: number) => number> 来定义运算操作,这样我们就可以安全地引用运算符,确保不会传入错误的运算符。

测试默认值和可选属性

总结

通过这次的实现,你已经成功为计算器组件设置了可选属性和默认值。这不仅提高了组件的灵活性,也使得使用组件时的开发体验更加友好。继续加油,如果你有其他问题,随时可以问我!

046 减少重复

在这一步中,我们将专注于优化 TypeScript 的类型定义,以便更好地处理数学运算,而不需要在每个操作中重复定义函数的类型。这将使我们能够轻松添加新的操作而不必担心类型声明。

目标

我们希望创建一个操作类型,以便我们可以从中导出所需的参数类型和返回类型,而无需手动指定每个操作的详细类型。

实现步骤

  1. 定义一个通用的操作类型:我们可以定义一个类型 Operation,它是一个接受两个数字参数并返回数字的函数类型。
  2. 创建一个操作对象:在这个对象中,我们将每个运算符作为属性,并将对应的运算函数赋值给它。
  3. 使用类型推断:利用 TypeScript 的类型推断功能来自动推断参数和返回值的类型。

示例实现

下面是一个简化的实现示例:

import React from 'react';

// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';

// 定义操作类型
type Operation = (left: number, right: number) => number;

// 创建操作对象
const operations: Record<Operator, Operation> = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => a / b,
};

// 定义计算器属性接口
interface CalculatorProps {
    left?: number;
    operator?: Operator;
    right?: number;
}

// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
    left = 0,
    operator = 'add',
    right = 0,
}) => {
    const result = operations[operator](left, right);

    return (
        <div>
            <code>{`${left} ${operator} ${right}`}</code>
            = <output>{result}</output>
        </div>
    );
};

// 使用 Calculator 组件
const App = () => (
    <div>
        <Calculator left={1} right={2} operator="add" />
        <Calculator right={5} operator="multiply" />
        <Calculator left={4} />
        <Calculator />
    </div>
);

export default App;

代码解释

  1. 定义 Operation 类型:我们定义了一个 Operation 类型,表示接受两个 number 类型参数并返回 number 的函数。

    type Operation = (left: number, right: number) => number;
  2. 创建 operations 对象:使用 Record<Operator, Operation> 来定义一个运算对象,允许我们将每个运算符与其对应的实现关联起来。

    const operations: Record<Operator, Operation> = {
       add: (a, b) => a + b,
       subtract: (a, b) => a - b,
       multiply: (a, b) => a * b,
       divide: (a, b) => a / b,
    };
  3. 计算器组件:在 Calculator 组件中,我们使用 operations 对象来执行所需的运算,而不需要为每个操作手动定义类型。

优点

结论

通过以上步骤,我们成功地优化了 TypeScript 的类型定义,使得添加新操作变得更加容易。这样的结构不仅提高了代码的可读性和可维护性,也使得开发过程更加顺畅。如果你有其他问题或需要进一步的帮助,请随时告诉我!

047 减少重复 (1)

在这个阶段,我们要进一步改进我们的 TypeScript 类型定义,以确保我们的计算器操作具备更好的类型安全和自动完成功能。我们需要处理的主要任务是定义一个 Operation 函数类型,并利用这个类型来确保我们能够限制操作符的可用性,同时又不会牺牲类型安全性。

目标

  1. 定义操作类型:创建一个 Operation 函数类型,该类型接受两个数字参数并返回一个数字。
  2. 限制操作符:使用 TypeScript 的联合类型来限制可用的操作符。
  3. 确保类型安全性:在定义运算时确保 TypeScript 知道它们的类型,从而能够提供正确的类型检查和自动完成功能。

示例实现

以下是一个改进的实现示例:

import React from 'react';

// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';

// 定义运算类型
type Operation = (left: number, right: number) => number;

// 创建操作对象,使用 Record 来限定操作符
const operations: Record<Operator, Operation> = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => a / b,
};

// 定义计算器属性接口
interface CalculatorProps {
    left?: number;
    operator?: Operator;
    right?: number;
}

// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
    left = 0,
    operator = 'add',
    right = 0,
}) => {
    const result = operations[operator](left, right);

    return (
        <div>
            <code>{`${left} ${operator} ${right}`}</code>
            = <output>{result}</output>
        </div>
    );
};

// 使用 Calculator 组件
const App = () => (
    <div>
        <Calculator left={1} right={2} operator="add" />
        <Calculator right={5} operator="multiply" />
        <Calculator left={4} />
        <Calculator />
    </div>
);

export default App;

代码解释

  1. 定义操作符类型:我们定义了一个 Operator 类型,它只允许 'add', 'subtract', 'multiply', 和 'divide' 这四个字符串。

    type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
  2. 定义运算类型:定义了一个 Operation 类型,用于表示运算函数。

    type Operation = (left: number, right: number) => number;
  3. 创建操作对象:使用 Record<Operator, Operation> 来确保 operations 对象中的每个运算符都与相应的操作函数匹配。

    const operations: Record<Operator, Operation> = {
       add: (a, b) => a + b,
       subtract: (a, b) => a - b,
       multiply: (a, b) => a * b,
       divide: (a, b) => a / b,
    };
  4. 定义计算器组件:在 Calculator 组件中,定义了 CalculatorProps 接口,以确保左侧和右侧参数为可选的数字,操作符为可选的 Operator 类型。

关键点

结论

通过以上步骤,我们成功实现了对计算器组件的类型安全检查和操作符限制,使得代码更加健壮。这样的结构不仅提高了代码的可读性和可维护性,也使得开发过程更加顺畅。如果你还有其他问题或者需要进一步的帮助,请随时告诉我!

048 Satisfies

在这个步骤中,我们将通过使用 TypeScript 的 satisfies 关键字来改进我们的 operations 对象的类型定义。这样做可以简化类型声明,同时保持类型安全和良好的开发体验。

目标

  1. 使用 satisfies 语法:我们将使 operations 对象满足更广泛的类型定义。
  2. 避免重复的类型定义:通过使 operations 符合更广泛的类型,而不是强制性地将其定义为一个特定的类型。

示例实现

以下是如何实现的示例代码:

import React from 'react';

// 定义运算符类型
type Operator = 'add' | 'subtract' | 'multiply' | 'divide';

// 定义运算类型
type Operation = (left: number, right: number) => number;

// 创建操作对象
const operations = {
    add: (a: number, b: number) => a + b,
    subtract: (a: number, b: number) => a - b,
    multiply: (a: number, b: number) => a * b,
    divide: (a: number, b: number) => a / b,
} satisfies Record<Operator, Operation>; // 使用 satisfies 语法

// 定义计算器属性接口
interface CalculatorProps {
    left?: number;
    operator?: Operator;
    right?: number;
}

// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
    left = 0,
    operator = 'add',
    right = 0,
}) => {
    const result = operations[operator](left, right);

    return (
        <div>
            <code>{`${left} ${operator} ${right}`}</code>
            = <output>{result}</output>
        </div>
    );
};

// 使用 Calculator 组件
const App = () => (
    <div>
        <Calculator left={1} right={2} operator="add" />
        <Calculator right={5} operator="multiply" />
        <Calculator left={4} />
        <Calculator />
    </div>
);

export default App;

代码解释

  1. 运算符类型:我们定义了一个 Operator 类型,它限制了可用的运算符。

    type Operator = 'add' | 'subtract' | 'multiply' | 'divide';
  2. 运算类型:定义了一个 Operation 类型,表示接收两个数字并返回一个数字的函数。

    type Operation = (left: number, right: number) => number;
  3. 使用 satisfies 语法:在 operations 对象中,使用 satisfies 关键字来指定 operations 应符合 Record<Operator, Operation> 类型,而不强制为特定的类型。这使得我们可以更灵活地扩展 operations 对象而不会破坏类型检查。

    const operations = {
       add: (a: number, b: number) => a + b,
       subtract: (a: number, b: number) => a - b,
       multiply: (a: number, b: number) => a * b,
       divide: (a: number, b: number) => a / b,
    } satisfies Record<Operator, Operation>;

关键点

结论

通过这个步骤,我们不仅优化了类型定义,同时也提升了开发体验。你可以根据需要继续扩展 operations 对象,而不必担心手动维护类型。如果你有其他问题或需要进一步的帮助,请随时告诉我!

049 Satisfies (1)

在这个步骤中,我们将优化我们对运算符类型的管理,使其更具灵活性,同时保持类型安全和良好的开发体验。我们将通过使用 TypeScript 的 satisfies 语法来实现这一点。

目标

  1. 消除单独的运算符类型:避免因使用单独的运算符类型而导致的限制。
  2. 利用 satisfies 语法:使 operations 对象能够满足更广泛的类型,而无需重复类型定义。
  3. 保持类型安全和自动补全:确保在使用 operations 时仍能获得类型安全和 IDE 的自动补全功能。

示例实现

以下是更新后的代码示例:

import React from 'react';

// 定义运算类型
type Operation = (left: number, right: number) => number;

// 创建操作对象
const operations = {
    add: (a: number, b: number) => a + b,
    subtract: (a: number, b: number) => a - b,
    multiply: (a: number, b: number) => a * b,
    divide: (a: number, b: number) => a / b,
} satisfies Record<string, Operation>; // 使用 satisfies 语法

// 定义计算器属性接口
interface CalculatorProps {
    left?: number;
    operator?: keyof typeof operations; // 限制为 operations 对象的键
    right?: number;
}

// 计算器组件
const Calculator: React.FC<CalculatorProps> = ({
    left = 0,
    operator = 'add',
    right = 0,
}) => {
    const result = operations[operator](left, right);

    return (
        <div>
            <code>{`${left} ${operator} ${right}`}</code>
            = <output>{result}</output>
        </div>
    );
};

// 使用 Calculator 组件
const App = () => (
    <div>
        <Calculator left={1} right={2} operator="add" />
        <Calculator right={5} operator="multiply" />
        <Calculator left={4} />
        <Calculator />
    </div>
);

export default App;

代码解释

  1. 运算类型:定义了一个 Operation 类型,表示接收两个数字并返回一个数字的函数。

    type Operation = (left: number, right: number) => number;
  2. 使用 satisfies 语法:在 operations 对象中,使用 satisfies 关键字来指定 operations 应符合 Record<string, Operation> 类型。这样,operations 可以是任意字符串映射到 Operation 类型的对象,同时保持对各个操作的类型检查和自动补全功能。

    const operations = {
       add: (a: number, b: number) => a + b,
       subtract: (a: number, b: number) => a - b,
       multiply: (a: number, b: number) => a * b,
       divide: (a: number, b: number) => a / b,
    } satisfies Record<string, Operation>;
  3. 限制运算符:通过 keyof typeof operations,我们确保 operator 属性只能是 operations 对象中的有效键,从而增强了类型安全。

    operator?: keyof typeof operations; 

关键点

结论

通过这个步骤,我们不仅优化了运算符的类型管理,还提升了代码的可维护性和可扩展性。你可以根据需要继续扩展 operations 对象,而不必担心手动维护类型。如果你有其他问题或需要进一步的帮助,请随时告诉我!

050 爸爸笑话时间 TypeScript

这是一个很好的时刻来休息一下!给自己一些时间来补充水分,放松心情,帮助你更好地消化和巩固所学的知识。

在学习新内容时,特别是编程和技术方面,休息和自我照顾是非常重要的。就像你提到的,写下你所学到的东西,即使不再查看,写的过程本身也会帮助你记住这些信息。

当你准备好继续时,欢迎回来,我们将继续学习更多有趣的内容!如果你有任何问题或者需要进一步的帮助,请随时告诉我。

051 样式简介

052 样式

053 样式 (1)

054 自定义组件

055 自定义组件 (1)

056 尺寸Props

057 尺寸Props (1)

058 爸爸笑话时间 样式

059 表单简介

060 表单

061 表单 (1)

062 表单操作

063 表单操作 (1)

064 输入类型

065 输入类型 (1)

066 提交

067 提交 (1)

068 表单操作

069 表单操作 (1)

070 爸爸笑话时间 表单

071 输入简介

072 复选框

073 复选框 (1)

074 下拉选择

075 下拉选择 (1)

076 单选按钮

077 单选按钮 (1)

078 隐藏输入

079 隐藏输入 (1)

080 默认值

081 默认值 (1)

082 爸爸笑话时间 输入

083 错误边界简介

084 组合

085 组合 (1)

086 其他错误

087 其他错误 (1)

088 重置

089 重置 (1)

090 爸爸笑话时间 错误边界

091 数组渲染简介

092 Key prop

093 Key prop (1)

094 焦点状态

095 焦点状态 (1)

096 Key重置

097 Key重置 (1)

098 爸爸笑话时间 数组渲染

099 React基础结束

WangShuXian6 commented 1 day ago

2 React钩子

001 React钩子简介

002 UI状态管理简介

003 useState

004 useState (1)

005 控制输入

006 控制输入 (1)

007 推导状态

008 推导状态 (1)

009 初始化状态

010 初始化状态 (1)

011 初始化回调

012 初始化回调 (1)

013 爸爸笑话时间 UI状态管理

014 副作用简介

015 useEffect

016 useEffect (1)

017 清理副作用

018 清理副作用 (1)

019 爸爸笑话时间 副作用

020 状态提升简介

021 提升状态

022 提升状态 (1)

023 提升更多状态

024 提升更多状态 (1)

025 状态合并

026 状态合并 (1)

027 爸爸笑话时间 状态提升

028 DOM副作用简介

029 Refs

030 Refs (1)

031 依赖项

032 依赖项 (1)

033 原始依赖项

034 原始依赖项 (1)

035 爸爸笑话时间 DOM副作用

036 唯一ID简介

037 useId

038 useId (1)

039 爸爸笑话时间 唯一ID

040 井字棋简介

041 setState回调

042 setState回调 (1)

043 在localStorage中保存状态

044 在localStorage中保存状态 (1)

045 添加游戏历史功能

046 添加游戏历史功能 (1)

047 爸爸笑话时间 井字棋

048 React钩子结束

WangShuXian6 commented 1 day ago

3 高级React API

1 高级React API简介

2 高级状态管理简介

3 新状态_问题

4 新状态_解决方案

5 以前的状态_问题

6 以前的状态_解决方案

7 状态对象_问题

8 状态对象_解决方案

9 动作函数_问题

10 动作函数_解决方案

11 传统Reducer_问题

12 传统Reducer_解决方案

13 现实场景_问题

14 现实场景_解决方案

15 爸爸笑话时间 高级状态管理

16 状态优化简介

17 优化状态更新_问题

18 优化状态更新_解决方案

19 爸爸笑话时间 状态优化

20 自定义钩子简介

21 钩子函数_问题

22 钩子函数_解决方案

23 useCallback_问题

24 useCallback_解决方案

25 爸爸笑话时间 自定义钩子

26 共享上下文简介

27 上下文提供者_问题

28 上下文提供者_解决方案

29 上下文钩子_问题

30 上下文钩子_解决方案

31 爸爸笑话时间 共享上下文

32 门户简介

33 createPortal_问题

34 createPortal_解决方案

35 爸爸笑话时间 门户

36 布局计算简介

37 useLayoutEffect_问题

38 useLayoutEffect_解决方案

39 爸爸笑话时间 布局计算

40 命令式处理简介

41 useImperativeHandle_问题

42 useImperativeHandle_解决方案

43 爸爸笑话时间 命令式处理

44 焦点管理简介

45 flushSync_问题

46 flushSync_解决方案

47 爸爸笑话时间 焦点管理

48 同步外部状态简介

49 useSyncExternalStore_问题

50 useSyncExternalStore_解决方案

51 创建存储实用程序_问题

52 创建存储实用程序_解决方案

53 处理服务器渲染_问题

54 处理服务器渲染_解决方案

55 爸爸笑话时间 同步外部状态

56 高级React API结束

WangShuXian6 commented 1 day ago

4 React Suspense

1 数据获取简介

2 抛出Promise_问题

3 抛出Promise_解决方案

4 错误处理_问题

5 错误处理_解决方案

6 正式状态_问题

7 正式状态_解决方案

8 实用工具_问题

9 实用工具_解决方案

10 使用React_问题

11 使用React_解决方案

12 爸爸笑话时间 数据获取

13 动态Promise简介

14 Promise缓存_问题

15 Promise缓存_解决方案

16 useTransition_问题

17 useTransition_解决方案

18 挂起闪烁_问题

19 挂起闪烁_解决方案

20 爸爸笑话时间 动态Promise

21 乐观UI简介

22 乐观UI_问题

23 乐观UI_解决方案

24 表单状态_问题

25 表单状态_解决方案

26 多步操作_问题

27 多步操作_解决方案

28 爸爸笑话时间 乐观UI

29 Suspense图像组件简介

30 图像组件_问题

31 图像组件_解决方案

32 图像错误边界_问题

33 图像错误边界_解决方案

34 Key属性_问题

35 Key属性_解决方案

36 爸爸笑话时间 Suspense图像

37 响应式简介

38 useDeferredValue_问题

39 useDeferredValue_解决方案

40 爸爸笑话时间 响应式

41 优化简介

42 并行加载_问题

43 并行加载_解决方案

44 服务器缓存_问题

45 服务器缓存_解决方案

46 爸爸笑话时间 优化

47 React Suspense结束

WangShuXian6 commented 1 day ago

5 高级React模式

1 高级React模式简介

2 组合简介

3 组合和布局组件_问题

4 组合和布局组件_解决方案

5 爸爸笑话时间 组合

6 最新Ref简介

7 最新Ref_问题

8 最新Ref_解决方案

9 爸爸笑话时间 最新Ref

10 复合组件简介

11 复合组件_问题

12 复合组件_解决方案

13 复合组件验证_问题

14 复合组件验证_解决方案

15 爸爸笑话时间 复合组件

16 插槽简介

17 插槽上下文_问题

18 插槽上下文_解决方案

19 通用插槽组件_问题

20 通用插槽组件_解决方案

21 插槽Prop_问题

22 插槽Prop_解决方案

23 爸爸笑话时间 插槽

24 Prop集合和Getters简介

25 Prop集合_问题

26 Prop集合_解决方案

27 Prop Getters_问题

28 Prop Getters_解决方案

29 爸爸笑话时间 Prop集合和Getters

30 状态初始化器简介

31 初始化切换_问题

32 初始化切换_解决方案

33 稳定性_问题

34 稳定性_解决方案

35 爸爸笑话时间 状态初始化器

36 状态Reducer简介

37 状态Reducer_问题

38 状态Reducer_解决方案

39 默认状态Reducer_问题

40 默认状态Reducer_解决方案

41 爸爸笑话时间 状态Reducer

42 控制属性简介

43 控制属性_问题

44 控制属性_解决方案

45 爸爸笑话时间 控制属性

46 高级React模式结束

WangShuXian6 commented 1 day ago

6 React性能优化

1 React性能优化简介

2 元素优化简介

3 重用元素_问题

4 重用元素_解决方案

5 元素Props_问题

6 元素Props_解决方案

7 上下文_问题

8 上下文_解决方案

9 元素记忆化_问题

10 元素记忆化_解决方案

11 组件记忆化_问题

12 组件记忆化_解决方案

13 爸爸笑话时间 元素优化

14 优化上下文简介

15 上下文记忆化_问题

16 上下文记忆化_解决方案

17 提供者组件_问题

18 提供者组件_解决方案

19 分割上下文_问题

20 分割上下文_解决方案

21 爸爸笑话时间 优化上下文

22 并发渲染简介

23 useDeferredValue + memo_问题

24 useDeferredValue + memo_解决方案

25 爸爸笑话时间 并发渲染

26 代码拆分简介

27 lazy_问题

28 lazy_解决方案

29 预加载_问题

30 预加载_解决方案

31 过渡_问题

32 过渡_解决方案

33 爸爸笑话时间 代码拆分

34 高耗计算简介

35 useMemo_问题

36 useMemo_解决方案

37 Web Worker_问题

38 Web Worker_解决方案

39 异步结果_问题

40 异步结果_解决方案

41 爸爸笑话时间 高耗计算

42 渲染优化简介

43 组件记忆化_问题

44 组件记忆化_解决方案

45 自定义比较器_问题

46 自定义比较器_解决方案

47 原始类型_问题

48 原始类型_解决方案

49 爸爸笑话时间 渲染优化

50 窗口化简介

51 Virtualizer_问题

52 Virtualizer_解决方案

53 爸爸笑话时间 窗口化

54 React性能优化结束

WangShuXian6 commented 1 day ago

7 React服务器组件

1 React服务器组件简介

2 热身简介

3 静态React应用_问题

4 静态React应用_解决方案

5 爸爸笑话时间 热身

6 服务器组件简介

7 服务器组件_问题

8 异步组件_问题

9 异步组件_解决方案

10 流式传输_问题

11 流式传输_解决方案

12 服务器上下文_问题

13 服务器上下文_解决方案

14 爸爸笑话时间 服务器组件

15 客户端组件简介

16 Node.js加载器_问题

17 Node.js加载器_解决方案

18 模块解析_问题

19 模块解析_解决方案

20 爸爸笑话时间 客户端组件

21 客户端路由简介

22 客户端路由_问题

23 客户端路由_解决方案

24 挂起UI_问题

25 挂起UI_解决方案

26 竞争条件_问题

27 竞争条件_解决方案

28 历史_问题

29 历史_解决方案

30 缓存_问题

31 缓存_解决方案

32 爸爸笑话时间 客户端路由

33 服务器操作简介

34 操作引用_问题

35 操作引用_解决方案

36 客户端_问题

37 客户端_解决方案

38 服务器_问题

39 服务器_解决方案

40 重新验证_问题

41 重新验证_解决方案

42 历史重新验证_问题

43 历史重新验证_解决方案

44 爸爸笑话时间 服务器操作

45 React服务器组件结束

WangShuXian6 commented 1 day ago

8 额外:专家访谈

1 与Aakansha Doshi讨论如何进入开源

2 Aurora Scharff讲解如何使用React 19增强表单

3 Jenna Smith谈AI、Radix构建和Tokenami

4 Evan Bacon将React服务器组件引入React Native

5 Kateryna Porshnieva讲解如何使用React 19构建无障碍应用

6 Lee Robinson讲述React的演变:过去、现在与未来

7 Matt Brophy谈Remix、React Router和开源

8 Michelle Beckles谈社区建设和开发者健康

9 Rick Hanlon讲解React 19的内幕

10 Sam Selikoff谈React在现代Web开发中的影响

11 Lydia Hallie讨论JavaScript、React和Web开发的未来

12 Sebastian Silbermann讲解React 19的测试、工具和过渡

13 Shruti Kapoor讲述现代Web开发中的无障碍性重要性

14 Sunil Pai谈如何通过强大的软件、PartyKit和耐久对象改变生活

15 Theo Browne谈他的个人Web开发经验

16 Dominik Dorfmeister谈他的开源之旅