一、新建项目
通过以下代码构建全新react+vite项目:
npm create vite@latest my-react-app -- --template react-ts
新建完后将react与react-dom改为19.0.0

执行以下代码加载包:
npm install
二、配置项目
1.eslint+prettier
参考vscode------Prettier插件保存自动格式化-腾讯云开发者社区-腾讯云
默认vite安装后都有eslint,然后开启eslint和prettier插件,然后在设置里edit format选prettier
新增
"format": "prettier --write \"src/**/*.+(js|ts|jsx|tsx)\""
package.json如下
{
"name": "flinkopsui",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"format": "prettier --write \"src/**/*.+(js|ts|jsx|tsx)\""
},
"dependencies": {
"flinkopsui": "file:",
"prettier": "^3.4.2",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.19.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.18",
"globals": "^15.14.0",
"typescript": "~5.7.2",
"typescript-eslint": "^8.22.0",
"vite": "^6.1.0"
}
}
然后在根目录新建cjs文件配置prettier
module.exports = {
// 箭头函数只有一个参数时,可以省略参数的括号
arrowParens: "avoid",
// 对象字面量中括号与内部内容之间不加空格
bracketSpacing: true,
// 使用 Unix 格式的行结束符 (\n)
endOfLine: "lf",
// 每行的最大长度限制为 100 个字符
printWidth: 100,
// Markdown 文件中的文本是否换行,`preserve` 表示保持现状
proseWrap: "preserve",
// 在语句末尾添加分号
semi: false,
// 使用双引号而不是单引号
singleQuote: false,
// 缩进时每级使用 2 个空格
tabWidth: 2,
// 使用空格而不是制表符进行缩进
useTabs: false,
// 在多行对象或数组的最后一项后添加逗号(es5 支持的范围内)
trailingComma: "es5",
// 解析器设置为 TypeScript
parser: "typescript",
plugins: ["prettier-plugin-tailwindcss"],
};
如果一直报错执行底下:
mv .prettierrc.js .prettierrc.cjs
设置完记得重启vscode
2.tailwind css
vite安装:参考Install Tailwind CSS with Vite - Tailwind CSS
3.ant design
三、react与JavaScript与JSX基础知识

1.标签
1.html标签必须闭合。
2.JSX片段必须首字母大写。
3.每段JSX只能有一个根节点,也就是必须有且要有一个<div></div>进行包裹。
3.Fragment:通过<></>可以不声明最外层div,避免块级元素重复使用。
2.属性
1.定义样式的class需要改为className。
2.style要使用JS对象,不能直接采用string形式,并且key要用驼峰命名法。

3.for要改为htmlFor。
3.事件
1.JSX监听事件分为点击事件、鼠标事件等等监听,onClick,采用onXXX的形式。
2.必须传入一个函数(还须注意在JSX里引用JSX需要{})=>形式为fn而不是fn(),若使用fn()则表示直接调用此函数,我们采用fn的形式类似于挂载。
3.传参如下:声明类型、在JSX中使用箭头函数进行传参,在(event)可以理解为 事件发生时 ,浏览器自动创建并传递的一个 事件对象,然后传递给方法。
import type {MouseEvent} from "react";
const fn = (event: MouseEvent<HTMLButtonElement>)=>{
event.preventDefault();
console.log("123")
}
<button onClick={(event) => fn(event)}>Click me</button>
4.JS变量或表达式
1.通过{/* */}在JSX中写注释。
2.通过{***}可以插入JS变量、函数、表达式。
5.条件判断
条件判断有三种:
1.通过&&进行判断=>适用于数据隐藏、显示。
2.三元表达式进行判断=>适用于数据切换显示。
3.函数进行判断=>通过显示函数的方式进行判断(注意:首字母要大写,函数与html都要大写)。
function HelloIf(){
if(flag) return "hello"
return "helloelse"
}
{flag && <p>123456</p>}
{flag ? <p>123</p> : <p>321</p>}
<HelloIf></HelloIf>
6.循环
在JSX中使用map进行循环,并且要指定key,key必须是唯一的。
示例代码解释:首先新建list数组、通过map进行遍历,user为每一个遍历出来的元素,通过箭头函数进行返回,将其解构为username、name,key必须采用{}这样才使用的是对象而不是静态字符串。
注:在JSX里只要使用JavaScript就得先提前加上{}。
const list = [
{username:"1",name:"123"},
{username:"2",name:321}
]
<ul>
{list.map(user=>{
const {username,name} = user
return <li key={username}>{name}</li>
})}
</ul>
四、react传参
五、react hooks
1.useState

useState当JSX中未使用的时候则不需要用useState,因为组件是个函数,并且useState会触发组件的更新,替代方法为useRef,并且useState为异步更新,也就是无法在JS中拿到最新的值(因为其实自己已经知道这个值)。

使用函数可以防止异步更新不合并。

1.2 useState值不可变
useState的值为不可变,也就是不能直接修改里面的值,要通过传入新的值的方式,通过新值或函数返回新值的方式更新,可以使用解构语法简化。
数组改变方式,用concat不能用push,因为concat返回一个数组新值。
1.3useState使用情况

所有组件的定义一定要首字母大写,要不然useState会报错。

1.4useState改变方法
1.4.1原始修改方法
增:concat
function add() {
// setCount(count + 1)
const r = Math.random().toString().slice(-3);
setQuestionList(
// 新增 concat
questionList.concat({
id: "q" + r,
title: "问卷" + r,
isPublished: false,
})
);
}
删:fitter
function deleteQuestion(id: string) {
// // 不可变数据
setQuestionList(
// 删除 filter
questionList.filter(q => {
if (q.id === id) return false;
else return true;
})
);
}
改:map
function publishQuestion(id: string) {
setQuestionList(
// 修改 map
questionList.map(q => {
if (q.id !== id) return q;
return {
...q,
isPublished: true,
};
})
);
}
1.4.2 immer修改方法
function add() {
// setCount(count + 1)
const r = Math.random().toString().slice(-3);
// setQuestionList(
// // 新增 concat
// questionList.concat({
// id: "q" + r,
// title: "问卷" + r,
// isPublished: false,
// })
// );
// immer 的方式
setQuestionList(
produce(draft => {
draft.push({
id: "q" + r,
title: "问卷" + r,
isPublished: false,
});
})
);
}
function deleteQuestion(id: string) {
// // 不可变数据
// immer 的方式
setQuestionList(
produce(draft => {
const index = draft.findIndex(q => q.id === id);
draft.splice(index, 1);
})
);
}
function publishQuestion(id: string) {
// immer 的方式
setQuestionList(
produce(draft => {
const q = draft.find(item => item.id === id);
if (q) q.isPublished = true;
})
);
}
2.useEffect
可以理解为监听,组件销毁时,组件特定值变化时([]里进行填写)

useEffect(() => {
console.log("Component mounted");
return () => {
console.log("Component unmounted");
};
}, []); // 空依赖数组,表示仅在组件挂载和卸载时触发
3.useRef
跟Vue3的ref不同,Vue3的ref是操作响应式数据,而react的useRef是操作dom节点的。
去保存一个值,不影响页面显示,用useRef,因为useRef不会导致rerender,也就是页面更新,如果需要改变页面,则用useState。

import { useRef } from "react";
const RefTest = () => {
const select = useRef<HTMLInputElement>(null);
function changeSelect() {
const inputElem = select.current;
if (inputElem) inputElem.select();
}
return (
<div>
<input ref={select} defaultValue={"Hello world"}></input>
<button onClick={changeSelect}>改变数据</button>
</div>
);
};
export default RefTest;
4.useMemo
5.useCallback
6.自定义hook
首先自定义hook需要以use开头。
第一步:在src目录下新建hooks文件夹,并创建以use开头的hook的ts文件,由于没有使用JSX语法,所以只需要ts文件即可。

第二步:声明函数,并记得处理销毁事件,防止内存泄漏。
import { useEffect, useState } from "react"
const useHooktest = () => {
const [x, setX] = useState(0)
const [y, setY] = useState(0)
const mouseMoveHandler=(event: MouseEvent) => {
setX(event.clientX)
setY(event.clientY)
}
useEffect(() => {
window.addEventListener('mousemove', mouseMoveHandler);
return () => {
window.removeEventListener('mousemove',mouseMoveHandler)
}
}, [])
return {x,y}
}
export default useHooktest
在父组件进行引用并结构,即可正常使用自定义hook。
// import List2 from "./list2";
// import RefTest from "./components/useRefTest";
import useHooktest from "./hooks/useHooktest";
function App() {
const { x, y } = useHooktest();
return (
<div>
<p>{x}</p>
<p>{y}</p>
</div>
);
}
export default App;
7.hooks对比参考:
8.Hooks使用规则

七、CSS
1.使用css module解决相同命名
解决问题:比如一个页面引用多个组件,那么各个组件调用的css有相同命名的,通过以下方法解决。
vite自带css module,所以在创建文件的时候采用,xxx.module.css
引用的时候使用styles
import styles from "./questionCard.module.css";
<div className={styles["list-item"]}>123</div>
css module会自行给class进行命名

2.避免使用内联样式,并且通过使用classnames或clsx代替if else进行样式判断


3.采用sass
使用sass进行编写可以直接嵌套
npm install sass

八、正式开始实战
1.新建pages页面、components组件

2.路由
2.1安装

npm install react-router-dom

3.outlet
react实现固定区域与变化区域,通过outlet实现变化区域,就比如管理员需要固定的侧边栏进行导航,然后通过outlet进行对应渲染。




个人理解:
就是首先我有个底层的父路由,就比如有个/对应的MainLayout,那么它是顶层渲染,然后子组件是否是必须设置为它的children,然后react就是通过outlet去通过不同路由定位显示不同的子组件,然后vue就是通过router-view去定位显示不同的子组件,您看我这个理解对不对
第一步创建路由配置

import { createBrowserRouter } from 'react-router-dom'
import MainLayout from '../layouts/MainLayout'
import Login from '../pages/Login'
import Home from '../pages/Home'
import NotFound from '../pages/404NotFound'
const router = createBrowserRouter([
{
path: 'login',
element: <Login />,
},
{
path: '/',
element: <MainLayout />,
children: [
{
path: 'home',
element: <Home />,
},
],
},
//*的意思是以上都不匹配执行 404 Not Found写在最后
{
path: '*',
element: <NotFound />,
},
])
export default router
第二步在app.tsx中引用

import { RouterProvider } from 'react-router-dom'
import routerConfig from './router'
function App() {
return <RouterProvider router={routerConfig}></RouterProvider>
}
export default App
3.渲染底层知识



4.路由导航
4.1useNavigate钩子
采用react router的useNavigate钩子进行导航
示例如下:
import { useNavigate } from 'react-router-dom';
const ViewsPage = () => {
const navigate = useNavigate();
const goToTest1 = () => {
navigate('/views/test1'); // 导航到 /views/test1 路由
};
return (
<div>
<button onClick={goToTest1}>Go to Test1</button>
</div>
);
};
export default ViewsPage;
4.2Link组件
import { Outlet, Link } from 'react-router-dom';
const MainLayout = () => {
return (
<div>
<header>
<nav>
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
</nav>
</header>
<aside>固定的侧边栏</aside>
<main>
<Outlet /> {/* 动态内容 */}
</main>
</div>
);
}
export default MainLayout;

4.3传递参数


4.4接收参数
采用useParams钩子进行参数的接收

采用useSearchParams钩子进行参数搜索


5.自定义标题与icon
全局在index.html中进行修改
单独修改使用ahooks(但是目前貌似还不支持react19)
第一步:
npm install --save ahooks

九、细节理解
1.React.FC声明与传参理解
1.1Vue3与React19传递参数对比
父组件向子组件传递参数


子组件向父组件传递参数


子组件互相传递参数

1.2React.FC理解
