引言本文将开始React最新框架研究。React 是由 Facebook现 Meta开发并于 2013 年开源的 JavaScript 库专门用于构建用户界面特别是单页应用程序SPA。React 允许开发者使用声明式的方式来构建可复用的 UI 组件。React 的定位不是框架而是库React 专注于视图层MVC 中的 V不像 Angular 那样提供完整的框架解决方案组件化思想将 UI 拆分成独立、可复用的组件JavaScript 为中心一切皆 JavaScript包括结构、样式和逻辑1 基本特点主要归功于以下几个核心特点1.1 组件化React 鼓励将整个用户界面拆分成一个个独立、可复用的小部件即“组件”。每个组件管理自身的逻辑和状态就像搭积木一样通过组合这些组件来构建复杂的页面。可复用性一个按钮、一个卡片都可以是组件在应用的不同地方甚至不同项目中复用。可维护性组件职责单一修改某个功能时只需关注对应的组件不影响其他部分。1.2 声明式编程使用 React你只需要描述 UI “应该是什么样子”即 UI 与数据状态的对应关系而不用关心“如何一步步更新 UI”。当数据状态改变时React 会自动高效地更新并渲染正确的组件。更直观代码更简洁、更易读让开发者专注于业务逻辑。更易调试因为每个状态都对应一个明确的 UI 视图使得追踪问题变得更容易。1.3 虚拟 DOM虚拟 DOM 是 React 高性能的关键。它是一个存在于内存中的、轻量级的真实 DOM 的 JavaScript 对象副本。当组件状态发生变化时React 会先在内存中创建一个新的虚拟 DOM 树。然后通过高效的Diffing 算法对比新旧虚拟 DOM 树的差异。最后只将变化的部分精确地更新到真实的 DOM 上。这种方式最大限度地减少了对真实 DOM 的直接操作真实 DOM 操作非常消耗性能从而实现了高效的渲染。1.3 JSX 语法JSX 是 JavaScript 的一种语法扩展它允许你在 JavaScript 代码中直接编写类似 HTML 的标记。更直观将 UI 结构和逻辑代码紧密结合在一起使组件的结构一目了然。功能强大JSX 最终会被编译成标准的 JavaScript 对象React 元素你可以在其中使用 JavaScript 的全部能力。所以JSX是HTML和JavaScript杂交主要用于 React 应用中。代码看起了自然不那么清爽因为html的属性可能是js的关键子所以一些属性发放到JSX中时需要改变名字有点考记忆。注释要写在大括号中对大括号是情有独钟啊{/*注释...*/}1.3.1 Vue 3 中的支持虽然 Vue 最经典的用法是“模板语法”Template Syntax即 HTML 风格的写法但 Vue 对 JSX 有着极好的支持特别是在 Vue 3 中。使用场景当你需要编写逻辑非常复杂的组件或者需要利用 JavaScript 的全功能来处理渲染逻辑时Vue 开发者往往会切换到 JSX/TSX。现状Vue 3 的生态系统中TSXTypeScript JSX的使用非常普遍。Vue 官方甚至专门优化了对 JSX 的支持移除了全局命名空间以避免与 React 冲突。文件名从js-jsx-tsx 这种变化对代码规范和工程框架都影响很大。不过现在都关心代码是不是优雅了加上AI生成估计更没有人去管了。这里提这个的原因时Vue组件文件以.vue结尾和相当于JSX如果按上面的说法直接改成JSX不知到和react有啥区别了。真实情况时几乎没有人改不是一个简单的rename那么简单特别时大型项目。就像那些想用typescript把.js改成改成.ts文件一样哪有那么简单的事情都是站起说话不腰疼的。这些演变其实兼容性都很差。你做几年了发现有点项目用的时js,有的时ts有的是tsx其实是非常混乱的。一项技术都迭代得很快所以要想做全栈再加上手机端估计得天天加班学习。如果你在vue中使用JSX,你得问问自己不直接用React理由是啥。1.3.2 SolidJS这是一个近年来非常火爆的高性能框架。特点SolidJS默认就是使用 JSX 的。它的语法看起来和 React 的 JSX 几乎一模一样但底层原理完全不同SolidJS 没有虚拟 DOM它是直接编译成原生 DOM 操作。体验如果你习惯了 React 的 JSX 写法上手 SolidJS 会非常快但能享受到更高的运行时性能。这里就不得理解一下好奇的方式理解一下它为什么高性能了。vuereact本质上就是通过jssctipt动态生成和修改dom结构自己在内存中维护了一份虚拟的dom树如果状态变了要生成新树然后计算新树和老树差异最后在dom上应用变更。这地方对比过程vue或者react做了优化据说很高效。现在SolidJS来了直接回归原始没有虚拟dom直接生成dom傻眼了吧。虚拟dom的高性能一下被打脸了。这个机制其实就是类似原生dom的监听机制和局部刷新只不过SolidJS 自动帮你做了。因为一直做后端所以长时间没有精力跟踪前端框架。所以一直好奇为啥虚拟Dom会比我局部刷新更快局部刷新那个数据变了我就刷哪里虚拟dom怎么做到更快的经常用到react或者vue写的页面发现还没有以前jquery写的流畅。因为没有输入研究也不知道式写的人没做好。还是框架的问题。大道至简最有效的方法通常都是最简单直白的。1.4 单向数据流在 React 中数据通常是从父组件通过属性props单向地流向子组件。子组件不能直接修改父组件传来的数据如果需要改变状态必须通过父组件传递下来的回调函数来通知父组件进行更新。数据清晰这种设计让数据流向变得清晰、可预测便于理解和调试大型应用的状态。1.5 钩子Hooks 是 React 16.8 版本引入的一项革命性特性它让你在不编写类组件的情况下也能在函数组件中使用状态state和其他 React 特性如生命周期。简化组件让函数组件成为主流代码更简洁。逻辑复用通过自定义 Hooks可以非常方便地在不同组件间复用有状态逻辑避免了类组件时代复杂的模式如高阶组件、render props。1.6 丰富的生态系统与跨平台能力React 本身专注于 UI 视图层但拥有极其庞大和活跃的社区生态。灵活组合你可以根据项目需求自由选择并搭配路由如 React Router、状态管理如 Redux, Zustand等库。跨平台开发基于 React 的核心思想衍生出了React Native让你可以用几乎相同的语法来开发原生的 iOS 和 Android 应用实现“一次学习随处编写”。1.7 JSX与VUE模板风格对比js是动态类型语言其语言本身可读性就查。用jquery的年代偶尔还会遇到动态添加html的场景就会用js拼接html代码html div classuser-card activeClass >import HelloWorld from ./components/HelloWorld.jsx function App() { const items [ { id: 1, name: 苹果 }, { id: 2, name: 香蕉 }, ] {/*不用写引号和号连接了不然要疯这注释要带一个大阔号...*/} return ( ul {items.map((item) ( li key{item.id} {item.name} /li ))} /ul ) } export default AppVUEtemplate ul li v-foritem in items :keyitem.id {{ item.name }} /li /ul /template script setup import { ref } from vue import HelloWorld from ./components/HelloWorld.vue const items ref([ { id: 1, name: 苹果 }, { id: 2, name: 香蕉 }, ]) /script其他的地方都一样把关键地方放在一起来对比一下return ( ul {items.map((item) ( li key{item.id} {item.name} /li ))} /ul ) # 上面这个夹了几个括号看起来有点蛋疼欣赏不来这种艺术感 # 所以vue也支持JSX不太懂 # 只喜欢干净直接一目录了然的代码 template ul li v-foritem in items :keyitem.id {{ item.name }} /li /ul /template1.8 JSX组件名字Reat要求组件首字母大写以区别自定义组件的名字和原始html组件。实际上你会发现有很多HTML标签都是纯大写的。这个形同虚设关于Vue组件命名规范也有类似问题。所有这种脚本感觉在规范性这方面做得都很差工程化程度不友好。2 基础框架搭建项目架构都使用vite了依赖管理使用pnpm 如果不太了解的可以看我前面的文章。2.1 无tspnpm create vitelatest test-react-app1 -- --template react2.2 有tspnpm create vitelatest my-react-app -- --template react-ts创建的时候还自动给你跑起来了不像vue需要你选组件。2.3 导入创建后用vs code导入项目就行了。3 框架结构分析3.1 无ts结构我们先看看无生成的工程结构3.1.1 index.html!doctype html html langen head meta charsetUTF-8 / link relicon typeimage/svgxml href/vite.svg / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titletest-react-app1/title /head body div idroot/div script typemodule src/src/main.jsx/script /body /html入口文件引入了main.jsx。div idroot/div是渲染容器.这可以说是个模板文件内容基本需要动。3.1.2 main.jsximport { StrictMode } from react import { createRoot } from react-dom/client import ./index.css import App from ./App.jsx /* 将虚拟的dom渲染到html的root元素上 */ createRoot(document.getElementById(root)).render( /*严格模式是 React 提供的一个开发辅助工具*/ StrictMode /* 入口 */ App / /StrictMode, )导入了index.css全局样式文件和App.jsx组件这个可以说是根组件应用入口。这个也可以不用动是个模板文件。3.1.3 App.jsximport { useState } from react import reactLogo from ./assets/react.svg import viteLogo from /vite.svg import ./App.css /*应用的样式*/ /*返回了一个app组件*/ function App() { const [count, setCount] useState(0) return ( div a hrefhttps://vite.dev target_blank img src{viteLogo} classNamelogo altVite logo / /a a hrefhttps://react.dev target_blank img src{reactLogo} classNamelogo react altReact logo / /a /div h1Vite React/h1 div classNamecard button onClick{() setCount((count) count 1)} count is {count} /button p Edit codesrc/App.jsx/code and save to test HMR /p /div p classNameread-the-docs Click on the Vite and React logos to learn more /p / ) } export default App这个就是你需要根据需求开始编写代码的地方了。3.1.4 package.json这个是个工程的核心配置文件一般不需要人工改动依赖主要包括生产和开发devDependencies,安装或者卸载依赖时是自动维护的这里我们关注的是scripts部分3.1.5 运行vite dev什么都没干一秒多有点吓人3.1.6 查看运行结果看看跑起来了牛逼不。搓它几下计数器变了3.2 有ts结构讲到这里我又忘记ts语法了那玩意不一直用过不了多久就全忘记了实在是语法过于烧脑。3.2.1 index.html!doctype html html langen head meta charsetUTF-8 / link relicon typeimage/svgxml href/favicon.svg / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titletest-react-app2/title /head body div idroot/div script typemodule src/src/main.tsx/script /body /html注意文件后缀变成了tsx3.2.2 main.tsx这个文件感觉命名成index.tsx会更恰当。但是默认是这个名字就是算了import { StrictMode } from react import { createRoot } from react-dom/client import ./index.css import App from ./App.tsx /*文件后缀变成tsx*/ createRoot(document.getElementById(root)!).render( StrictMode App / /StrictMode, )3.2.3 App.tsximport { useState } from react import reactLogo from ./assets/react.svg import viteLogo from ./assets/vite.svg import heroImg from ./assets/hero.png import ./App.css function App() { const [count, setCount] useState(0) return ( section idcenter div classNamehero img src{heroImg} classNamebase width170 height179 alt / img src{reactLogo} classNameframework altReact logo / img src{viteLogo} classNamevite altVite logo / /div div h1Get started/h1 p Edit codesrc/App.tsx/code and save to test codeHMR/code /p /div button typebutton classNamecounter onClick{() setCount((count) count 1)} Count is {count} /button /section div classNameticks/div section idnext-steps div iddocs svg classNameicon rolepresentation aria-hiddentrue use href/icons.svg#documentation-icon/use /svg h2Documentation/h2 pYour questions, answered/p ul li a hrefhttps://vite.dev/ target_blank img classNamelogo src{viteLogo} alt / Explore Vite /a /li li a hrefhttps://react.dev/ target_blank img classNamebutton-icon src{reactLogo} alt / Learn more /a /li /ul /div div idsocial svg classNameicon rolepresentation aria-hiddentrue use href/icons.svg#social-icon/use /svg h2Connect with us/h2 pJoin the Vite community/p ul li a hrefhttps://github.com/vitejs/vite target_blank svg classNamebutton-icon rolepresentation aria-hiddentrue use href/icons.svg#github-icon/use /svg GitHub /a /li li a hrefhttps://chat.vite.dev/ target_blank svg classNamebutton-icon rolepresentation aria-hiddentrue use href/icons.svg#discord-icon/use /svg Discord /a /li li a hrefhttps://x.com/vite_js target_blank svg classNamebutton-icon rolepresentation aria-hiddentrue use href/icons.svg#x-icon/use /svg X.com /a /li li a hrefhttps://bsky.app/profile/vite.dev target_blank svg classNamebutton-icon rolepresentation aria-hiddentrue use href/icons.svg#bluesky-icon/use /svg Bluesky /a /li /ul /div /section div classNameticks/div section idspacer/section / ) } export default App上面这个内容有点多不用太关注。本质和无ts是一样的3.2.4 package.json多了一些typescript的开发依赖3.2.5 运行3.2.6 查看运行结果这个图标看起来有点酷炫3.2.7 tsconfig.json这是 TypeScript 项目的根配置文件也是编译器首先寻找的文件。核心作用它通常不直接写具体的编译选项而是作为整个项目的“解决方案”入口。它的主要任务是通过references字段来引用其他配置文件也就是下面的两个文件从而将不同环境的配置分离开来。好处这种结构让项目结构更清晰避免了所有配置都堆在一个文件里也方便了构建工具进行增量编译{ files: [], references: [ { path: ./tsconfig.app.json }, { path: ./tsconfig.node.json } ] }引用了其他两个文件3.2.8 tsconfig.app.json这个文件专门用于配置前端应用代码的编译选项。适用代码你项目里所有的 React/Vue 组件、页面、工具函数等最终会在浏览器中运行的代码。典型配置target: 设置编译后的 JavaScript 版本例如 ESNext 或 ES2015以兼容现代浏览器。lib: 指定项目需要包含的库定义文件如 [DOM, ESNext]告诉 TypeScript 代码会运行在浏览器环境中。jsx: 配置如何处理 JSX 语法例如 react-jsx。3.2.9 tsconfig.node.json这个文件专门用于配置Node.js 环境下代码的编译选项。适用代码你的项目构建脚本和配置文件最典型的就是vite.config.ts。这些文件不是给浏览器运行的而是给 Node.js 执行的。典型配置target: 通常设置为ESNext或一个较高的 Node.js 版本因为现代 Node.js 支持很多新特性。module: 设置模块系统如ESNext或CommonJS以适应 Node.js 的模块加载方式。types: 可能会包含[node]以引入 Node.js 的全局类型定义如process,__dirname等。文件名角色定位负责范围tsconfig.json总指挥引用其他配置管理整个项目tsconfig.app.json前端专员浏览器环境代码 (React/Vue 组件等)tsconfig.node.json后端/工具专员Node.js 环境代码 (vite.config.ts 等)3.3 .vscode目录这个目录其实是一个重要目录因为里面一些配置在开发中通常会用到。创建项目时并不会创建该这个目录作用和产生机制可以参看文章总结加了TypeScript的之后整个框架会复杂很多越复杂的东西以后迁移维护成本越高。TypeScript从目前趋势来看都是要集成的。所有弱类型变成强类型之后AI和代码提示等工具就会智能化很多包括Python类型提示。动态类型、非面向对象语言严重缺点也暴露了出来。没有类型提示光看函数签名估计什么信息也看不出来。现在前端框架演变的越来越复杂了光各种插件和配置文件就够得搞半天。那些要求全栈、PC、手机、还要懂硬件的公司比周扒皮还强上十倍不止。4 框架重构框架布局估计会按vue的调整的目录结构重构默认的目录结果分类不太好。5 开发调试开发调试和vue差不多的请参看前面写过文章6 格式化插件可以参看前面写过文章。使用Prettier。最好不要配置成保存就自动格式化使用手动选择格式化保存不然遇到幺蛾子很麻烦。7 路由集成8 状态管理react Props存在Props Drilling问题也就是层层传递有几种非常成熟的方案可以完美避开这个Props Drilling问题Context API官方原生方案React 自带的“任意门”。你可以把数据放在顶层的Context里深层组件直接通过useContext获取完全不需要中间组件参与传递。非常适合传递主题、用户登录状态等全局数据。组件组合Component Composition利用children属性。有时候不需要传数据直接把需要数据的深层组件作为children传给顶层由顶层包一层渲染出来也能绕过中间层级。状态管理库如 Zustand, Redux, Jotai 等当应用变得非常复杂时可以使用专门的状态管理库。它们相当于在组件树之外建了一个“公共仓库”任何组件都可以随时去仓库里存取数据彻底摆脱层层传递的束缚。9 请求组件10 PC UI组件11 移动端UI 组件总结