Vite+React+TypeScript+VsCode框架实战
引言
本文将开始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 的生态系统中,TSX(TypeScript + 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 会非常快,但能享受到更高的运行时性能。
这里就不得理解一下好奇的方式理解一下它为什么高性能了。vue,react本质上就是通过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 class="user-card ' + activeClass + '" data-id="' + user.id + '">' +
'<h3>' +
user.name +
' <small class="age">' + ageWarning + '</small>' +
'</h3>' +
// 5. 事件绑定的噩梦:需要在字符串里拼接函数调用,还得处理引号
'<button onclick="alert(\'确定要删除用户:' + user.name + '\')">删除</button>' +
// 6. 内容转义问题:如果 bio 里包含单引号,HTML 结构直接断裂
'<p class="bio">' + user.bio + '</p>' +
'</div>';
还有比这更长的,简直就是噩梦,这种可读性和维护性都很不好。所以都是禁止使用,要求请求使用后端模板。
现在我们来对比一下JSX和vue模板写法
JSX
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 App
VUE
<template>
<ul>
<li v-for="item in items" :key="item.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-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
</template>
1.8 JSX组件名字
Reat要求组件首字母大写,以区别自定义组件的名字和原始html组件。实际上你会发现有很多HTML标签都是纯大写的。这个形同虚设,关于Vue组件命名规范也有类似问题。所有这种脚本感觉在规范性这方面做得都很差,工程化程度不友好。
2 基础框架搭建
项目架构都使用vite了,依赖管理使用pnpm ,如果不太了解的,可以看我前面的文章。
2.1 无ts
pnpm create vite@latest test-react-app1 -- --template react
2.2 有ts
pnpm create vite@latest my-react-app -- --template react-ts

创建的时候还自动给你跑起来了,不像vue需要你选组件。
2.3 导入
创建后用vs code导入项目就行了。
3 框架结构分析
3.1 无ts结构
我们先看看无生成的工程结构

3.1.1 index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>test-react-app1</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>
入口文件,引入了main.jsx。<div id="root"></div>是渲染容器.这可以说是个模板文件,内容基本需要动。
3.1.2 main.jsx
import { 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.jsx
import { 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 href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
</div>
<p className="read-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 lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>test-react-app2</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
注意文件后缀变成了tsx
3.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.tsx
import { 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 id="center">
<div className="hero">
<img src={heroImg} className="base" width="170" height="179" alt="" />
<img src={reactLogo} className="framework" alt="React logo" />
<img src={viteLogo} className="vite" alt="Vite logo" />
</div>
<div>
<h1>Get started</h1>
<p>
Edit <code>src/App.tsx</code> and save to test <code>HMR</code>
</p>
</div>
<button
type="button"
className="counter"
onClick={() => setCount((count) => count + 1)}
>
Count is {count}
</button>
</section>
<div className="ticks"></div>
<section id="next-steps">
<div id="docs">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#documentation-icon"></use>
</svg>
<h2>Documentation</h2>
<p>Your questions, answered</p>
<ul>
<li>
<a href="https://vite.dev/" target="_blank">
<img className="logo" src={viteLogo} alt="" />
Explore Vite
</a>
</li>
<li>
<a href="https://react.dev/" target="_blank">
<img className="button-icon" src={reactLogo} alt="" />
Learn more
</a>
</li>
</ul>
</div>
<div id="social">
<svg className="icon" role="presentation" aria-hidden="true">
<use href="/icons.svg#social-icon"></use>
</svg>
<h2>Connect with us</h2>
<p>Join the Vite community</p>
<ul>
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>
<li>
<a href="https://chat.vite.dev/" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#discord-icon"></use>
</svg>
Discord
</a>
</li>
<li>
<a href="https://x.com/vite_js" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#x-icon"></use>
</svg>
X.com
</a>
</li>
<li>
<a href="https://bsky.app/profile/vite.dev" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#bluesky-icon"></use>
</svg>
Bluesky
</a>
</li>
</ul>
</div>
</section>
<div className="ticks"></div>
<section id="spacer"></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 路由集成
React Router 其实有几个不同的包:
react-router:提供路由的核心逻辑,不依赖特定平台。
react-router-dom:基于核心包,专门为 Web 浏览器环境增加了 DOM 相关的功能(如 <BrowserRouter>、<Link> 等),是我们日常写网页最常用的。
react-router-native:专门为 React Native 移动端应用提供的路由支持。
所以,如果你在开发 React 网页应用,放心使用 react-router-dom,它就是目前最正统的官方路由选择!
7.1 安装包
pnpm install react-router-dom
7.2 一级路由
import React from 'react';
import ReactDOM from 'react-dom/client';
import {BrowserRouter as Router,
Routes,Route,Link
} from 'react-router-dom';
import Home from '@/components/route_test/Home.jsx';
import About from '@/components/route_test/About.jsx';
const App = () => {
return (
<Router>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
};
export default App;
7.3 嵌套路由
import React from 'react';
import ReactDOM from 'react-dom/client';
import {
BrowserRouter as Router,
Routes,
Route,
Link
} from 'react-router-dom';
import Home from '@/components/route_test/Home.jsx';
import About from '@/components/route_test/About.jsx';
import Profile from '@/components/route_test/Profile.jsx';
import Dashboard from '@/components/route_test/Dashboard.jsx';
const App = () => {
return (
<Router>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
</Route>
</Routes>
</Router>
);
};
export default App;
7.3 路由传参
// User.js
import React from 'react';
import { useParams } from 'react-router-dom';
const User = () => {
const { userId } = useParams();
return <h2>User ID: {userId}</h2>;
};
export default User;
#App.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import {
BrowserRouter as Router,
Routes,
Route,
Link
} from 'react-router-dom';
import Home from '@/components/route_test/Home.jsx';
import About from '@/components/route_test/About.jsx';
import Profile from '@/components/route_test/Profile.jsx';
import Dashboard from '@/components/route_test/Dashboard.jsx';
import User from '@/components/route_test/User.jsx';
const App = () => {
return (
<Router>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
<li>
<Link to="/user/3">User</Link>
</li>
</ul>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
</Route>
<Route path="/user/:userId" element={<User />} />
</Routes>
</Router>
);
};
export default App;
基本就是这样,也类似vue一样,可以独立出一个路由配置文件,方便工程化。
8 状态管理
react Props存在Props Drilling问题,也就是层层传递有几种非常成熟的方案可以完美避开这个Props Drilling问题:
- Context API(官方原生方案)
React 自带的“任意门”。你可以把数据放在顶层的Context里,深层组件直接通过useContext获取,完全不需要中间组件参与传递。非常适合传递主题、用户登录状态等全局数据。 - 组件组合(Component Composition)
利用children属性。有时候不需要传数据,直接把需要数据的深层组件作为children传给顶层,由顶层包一层渲染出来,也能绕过中间层级。 - 状态管理库(如 Zustand, Redux, Jotai 等)
当应用变得非常复杂时,可以使用专门的状态管理库。它们相当于在组件树之外建了一个“公共仓库”,任何组件都可以随时去仓库里存取数据,彻底摆脱层层传递的束缚。
9 请求组件
请求组件用axios,可以参看中文网和npm英文网。除了做请求之外,用的得最多的还有拦截器功能。
10 PC UI组件
11 移动端UI 组件
总结
更多推荐



所有评论(0)