一、新建项目
通过以下代码构建全新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",// 解析器设置为 TypeScriptparser: "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} = userreturn <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(// 新增 concatquestionList.concat({id: "q" + r,title: "问卷" + r,isPublished: false,}));}
删:fitter
function deleteQuestion(id: string) {// // 不可变数据setQuestionList(// 删除 filterquestionList.filter(q => {if (q.id === id) return false;else return true;}));}
改:map
function publishQuestion(id: string) {setQuestionList(// 修改 mapquestionList.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对比参考:
https://medium.com/@ksshravan667/14-days-of-react-day-5-react-hooks-usestate-useref-useeffect-usememo-usecallback-8599a14c4e2b
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传递参数对比
父组件向子组件传递参数
子组件向父组件传递参数
子组件互相传递参数