[{"data":1,"prerenderedAt":153},["ShallowReactive",2],{"article-react-small-components":3,"related-articles":51},{"id":4,"title":5,"body":6,"categoryLabel":35,"categorySlug":36,"description":37,"extension":38,"featured":39,"meta":40,"navigation":41,"path":42,"publishedAt":43,"readingMinutes":44,"seo":45,"stem":46,"tags":47,"__hash__":50},"blog\u002Fblog\u002Freact-small-components.md","把重复的界面拆成小组件",{"type":7,"value":8,"toc":29},"minimark",[9,13,17,20,23,26],[10,11,12],"p",{},"页面出现重复结构时，可以先比较这些结构是否真的一致，再决定要不要提取组件。拆分组件的目标是让一段界面更容易理解和复用。",[14,15,16],"h2",{"id":16},"先找出稳定的部分",[10,18,19],{},"例如多个卡片都有标题、说明和链接，可以把这三部分作为 Props 传入。颜色、间距等样式如果完全相同，也可以放在组件内部统一维护。",[14,21,22],{"id":22},"不必过早抽象",[10,24,25],{},"只出现一次、内容还在快速变化的区域，暂时留在页面里往往更简单。等重复模式清楚以后，再提取组件会更容易定义接口。",[10,27,28],{},"一个实用的检查方式是：读组件调用处时，能否大致看懂页面结构；读组件本身时，能否说清它只负责什么。",{"title":30,"searchDepth":31,"depth":31,"links":32},"",2,[33,34],{"id":16,"depth":31,"text":16},{"id":22,"depth":31,"text":22},"React","react","从重复的按钮和卡片开始，认识组件拆分的几个简单信号。","md",false,{},true,"\u002Fblog\u002Freact-small-components","2026-07-16",5,{"title":5,"description":37},"blog\u002Freact-small-components",[35,48,49],"组件","练习记录","iKVF7eGuzM1840IqpWfbXnIA40oeaMnunV4Pk6ubTYg",[52,65,74,84,92,99,101,109,117,127,135,143],{"id":53,"path":54,"title":55,"description":56,"categorySlug":57,"categoryLabel":58,"publishedAt":59,"readingMinutes":60,"featured":41,"tags":61},"blog\u002Fblog\u002Fcss-grid-cards.md","\u002Fblog\u002Fcss-grid-cards","用 CSS Grid 排列自适应卡片","使用 repeat、minmax 和 auto-fit，让卡片列数随容器宽度自然变化。","styles","页面与样式","2026-09-10",4,[62,63,64],"CSS","Grid","页面布局",{"id":66,"path":67,"title":68,"description":69,"categorySlug":57,"categoryLabel":58,"publishedAt":70,"readingMinutes":44,"featured":39,"tags":71},"blog\u002Fblog\u002Fmobile-layout-checklist.md","\u002Fblog\u002Fmobile-layout-checklist","做移动端页面适配时的检查笔记","从内容宽度、触控区域、文字换行和键盘焦点几个方面检查窄屏体验。","2026-08-20",[62,72,73],"响应式","可访问性",{"id":75,"path":76,"title":77,"description":78,"categorySlug":36,"categoryLabel":35,"publishedAt":79,"readingMinutes":80,"featured":41,"tags":81},"blog\u002Fblog\u002Freact-controlled-form.md","\u002Fblog\u002Freact-controlled-form","React 受控表单：从一个输入框开始","用一个简单的姓名输入框，理解 value、onChange 与 React state 之间的关系。","2026-09-18",6,[35,82,83],"表单","学习笔记",{"id":85,"path":86,"title":87,"description":88,"categorySlug":36,"categoryLabel":35,"publishedAt":89,"readingMinutes":60,"featured":39,"tags":90},"blog\u002Fblog\u002Freact-list-keys.md","\u002Fblog\u002Freact-list-keys","React 列表渲染里的 key 有什么用","通过稳定的 key 帮助 React 识别列表项目，避免用数组下标造成的状态错位。","2026-08-12",[35,91,83],"列表",{"id":93,"path":94,"title":95,"description":96,"categorySlug":36,"categoryLabel":35,"publishedAt":97,"readingMinutes":44,"featured":39,"tags":98},"blog\u002Fblog\u002Freact-props-and-state.md","\u002Fblog\u002Freact-props-and-state","React 组件里的 Props 和 State","用一个简单的信息卡片区分外部传入的数据和组件内部会变化的数据。","2026-08-28",[35,48,83],{"id":4,"path":42,"title":5,"description":37,"categorySlug":36,"categoryLabel":35,"publishedAt":43,"readingMinutes":44,"featured":39,"tags":100},[35,48,49],{"id":102,"path":103,"title":104,"description":105,"categorySlug":36,"categoryLabel":35,"publishedAt":106,"readingMinutes":80,"featured":39,"tags":107},"blog\u002Fblog\u002Freact-use-effect-notes.md","\u002Fblog\u002Freact-use-effect-notes","React useEffect 的依赖和清理","整理 useEffect 何时运行，以及订阅和定时器为什么需要清理。","2026-06-30",[35,108,83],"useEffect",{"id":110,"path":111,"title":112,"description":113,"categorySlug":36,"categoryLabel":35,"publishedAt":114,"readingMinutes":60,"featured":39,"tags":115},"blog\u002Fblog\u002Freact-use-state-practice.md","\u002Fblog\u002Freact-use-state-practice","用 useState 管理一个简单的计数交互","从加一和重置两个操作，熟悉 React state 的声明与更新方式。","2026-07-30",[35,116,49],"useState",{"id":118,"path":119,"title":120,"description":121,"categorySlug":122,"categoryLabel":123,"publishedAt":124,"readingMinutes":44,"featured":41,"tags":125},"blog\u002Fblog\u002Ftypescript-component-props.md","\u002Fblog\u002Ftypescript-component-props","给 React 组件的 Props 添加 TypeScript 类型","用必填属性和可选属性描述组件接口，让使用方式在编辑器里更清晰。","typescript","TypeScript","2026-09-02",[123,35,126],"类型",{"id":128,"path":129,"title":130,"description":131,"categorySlug":122,"categoryLabel":123,"publishedAt":132,"readingMinutes":44,"featured":41,"tags":133},"blog\u002Fblog\u002Ftypescript-narrowing.md","\u002Fblog\u002Ftypescript-narrowing","TypeScript 类型收窄入门","使用 typeof 和条件判断缩小联合类型的范围，让后续代码更安全。","2026-09-25",[123,134,83],"类型收窄",{"id":136,"path":137,"title":138,"description":139,"categorySlug":122,"categoryLabel":123,"publishedAt":140,"readingMinutes":60,"featured":39,"tags":141},"blog\u002Fblog\u002Ftypescript-union-status.md","\u002Fblog\u002Ftypescript-union-status","用联合类型描述请求状态","把加载、成功和失败等状态限制在明确的集合里，减少拼写错误和遗漏分支。","2026-08-04",[123,142,83],"联合类型",{"id":144,"path":145,"title":146,"description":147,"categorySlug":148,"categoryLabel":149,"publishedAt":150,"readingMinutes":60,"featured":39,"tags":151},"blog\u002Fblog\u002Fvite-react-project.md","\u002Fblog\u002Fvite-react-project","用 Vite 创建一个 React + TypeScript 小项目","记录从初始化模板到启动开发服务器的基础步骤，以及几个值得先检查的文件。","tools","开发工具","2026-06-12",[152,35,123],"Vite",1792674168646]