react 安装react
项目:
1 npx create-react-app your-project-name
启动项目:
jsx
:
JSX(JavaScript XML)是一种在React中使用的语法扩展,它看起来很像HTML,但实际上是JavaScript的一种语法糖。JSX使得编写和读取组件的结构更加直观,就是JavaScript和html的缩写.
可以在js代码中编写html代码
jsx不能在浏览器中进行运行,而是需要一个解析工具进行解析之后才能够进行运行
语法通过{}的形式来展示数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const username = "admin"; const numbwe = 123 function App() { return ( <div className="App"> {username} {numbwe} {'这个是react'} <div style={{color:'red'}}>这个是一个红色的div</div> </div> ); } export default App;
实现列表渲染
渲染数据用循环的语法,也就是相当于用了js原生的语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 const list = [ { id: 1, name: '张三' }, { id: 2, name: '李四' }, { id: 3, name: '王五' } ] function App() { return ( <div className="App"> <ul> {list.map(item =><li key={item.id}>{item.name}</li> )} </ul> </div> ); } export default App;
实现条件渲染
通过逻辑运算符&&或者三元表达式实现条件渲染
1 2 3 4 5 6 7 8 9 10 11 12 13 let flag = true; let loading = true; function App() { return ( <div className="App"> {flag && <h1>This is a heading</h1>} {loading ? <div>Loading...</div> : <div>Content</div>} </div> ); } export default App;
对于复杂模式的条件渲染,可以通过定于一个函数来实现这个代码需求,用if判断不同情况下的渲染模式来实现
时间处理函数,点击事件处理函数,使用onClick这个点击事件处理函数来实现这个功能
对于需要传递参数的事件而言,必须需要使用 (event) =>function_name(event)
这个格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function click_event(){ alert("clicked") } function click_event_event(event){ alert("clicked"+event) } function App() { return ( <div className="App"> <button onClick={click_event}>click me</button> <button onClick={(event) => click_event_event(event)}>click me,event</button> </div> ); } export default App;
组件
组件可以互相嵌套,也可以重复使用多次
要求:组件就是一个函数,而且这个函数名字的首字母还是需要大写的函数名字,渲染`组件就是把组件当作标签来使用就可以了
1 2 3 4 5 6 7 8 9 10 11 // 定义组件 function Button(){ return <button>click me,event</button> } function App() { return ( <Button></Button> ); } export default App;
数据驱动视图(相当于vue中的ref()
的响应式数据)
1 2 3 4 5 6 7 8 9 import { useState} from 'react'; function App() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}>{count}</button> ); } export default App;
状态不可变原则,不能用count++这样的语法来实现,而是需要使用一个新的值来替换原本的值这样的方法来实现值的变化
修改对象这样的数据结构,需要传入一个全新的对象进入进行修改,不能对对象进行赋值,简单的方法就是使用…语法来解决这个,后面加上要修改的属性及其值,会覆盖原有对象里面所含有的数据
组件中的基础样式处理
支持行内样式与从外部导入样式的两种写法
对于类名必须要使用className
的名字,不是class
1 2 3 4 5 6 7 function App() { return ( <div style={{color: 'blue'}}>这个是行内样式</div> ); } export default App;
也可以写成这样
1 2 3 4 5 6 7 8 9 10 const style = { color: 'blue' } function App() { return ( <div style={style}>这个是行内样式</div> ); } export default App;
从外面导入css文件
1 2 3 4 5 6 7 8 import './index.css' function App() { return ( <div className="information">红褐色字体</div> ); } export default App;
index.css
文件
1 2 3 .information { color : brown; }
双向数据绑定
受控绑定数据就是通过React的useState与输入的onChange事件获得的数据进行双向数据绑定,实现双向修改数据的功能
获取DOM
导入useRef并且使用这个东西
通过ref绑定在一些标签上面获取DOM,
为什么要获取dom,因为在一些情况下,需要获取dom来进行操作,就列如像输入框的聚焦效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { useRef } from "react"; const App = () => { const userref_infor_dom = useRef(null); const showdom = () => { console.log(userref_infor_dom.current); } return ( <div className="app"> <input ref={userref_infor_dom} type="text" placeholder="请输入密码"></input> <button onClick={showdom}>点击一下</button> </div> ); }; export default App;
组件通信(父传子)
和vue的语法很像
这个数据只能父组件才能修改,子组件不能修改父组件传递过来的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function Son(props) { // 父组件传递过来的是对象形式的 return <div className="app"> 我是子组件,这个是父组件传递过来的数据:{props.name} </div>; } function App() { const username = "黑马前端"; return <div className="app"> <Son name={username} /> </div>; } export default App;
父传子,特殊的传递数据的方式:在子组件中添加标签,会自动添加到prop的children
里面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function Son(prop) { // 父组件传递过来的是对象形式的 return ( <div> <div className="app"> 我是子组件,这个是父组件传递过来的数据:{prop.name} </div> <div>{prop.children}</div> </div> ); } function App() { const username = "黑马前端"; return ( <div className="app"> <Son name={username}> <span>我是子组件的子元素</span> </Son> </div> ); } export default App;
组件通信(子传父)
子传父的逻辑就是父组件创建一个方法,传递给子组件使用,子组件调用该方法就可以实现对应的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function Son(prop) { return ( <div> <div className="app"> 我是子组件,这个是父组件传递过来的数据:{prop.name} </div> <button onClick={() =>prop.ongetmessage()}>按钮</button> </div> ); } function App() { const username = "黑马前端"; const getmessage = () => { console.log("子组件的函数"); }; return ( <div className="app"> <Son name={username} ongetmessage={getmessage}></Son> </div> ); } export default App;
使用状态提升实现兄弟组件之间的数据传递通信
实际上就是通过父组件作为一个中间介来实现这个功能,就是复杂化了父子组件之间的传递数据
使用context机制夸层传递数据
createContext()方法创建一个上下文对象
在顶层组件通过Provider组件提供数据
在底层组件通过useContext钩子函数使用数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import { createContext, useContext } from "react"; const messagecontext = createContext(); function Father() { return ( <div className="app"> 我是父组件 <Son></Son> </div> ); } function Son() { const Message = useContext(messagecontext); return ( <div> <div className="app"> 我是son组件 <div>下面就是传递回来的数据:{Message}</div> </div> </div> ); } function App() { const Message = "我是APP传递的数据"; return ( <div className="app"> <messagecontext.Provider value={Message}> 我是APP <Father></Father> </messagecontext.Provider> </div> ); } export default App;
useEffect
相当于就是vue中的生命周期函数来使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import { useEffect } from 'react'; import { useState } from 'react'; const URL = 'http://geek.itheima.net/v1_0/channels' // 因为后端做了处理,所以前端不会出现跨域问题的错误 function App() { // 创建一个响应式的数据用来接受后端返回的数据 const [List_Data, setList_Data] = useState([]); useEffect(()=>{ const get_list_data = () => { fetch(URL).then(res => res.json()).then(res => { // console.log(res.data.channels); setList_Data(res.data.channels) }) } get_list_data() },[]) return ( <div> <ul> {List_Data.map((item, index) => (<li key={item.id}>{item.name}</li>))} </ul> </div> ); } export default App;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { useEffect } from 'react'; import { useState } from 'react'; function App() { const [count,setcontent] = useState(0); useEffect(()=>{ console.log('点击了一下') },[count]) // 相当于检测count数据的变化就会执行这个代码 return ( <div> <button onClick={()=>{setcontent(count+1)}}> {count} </button> </div> ); } export default App;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { useEffect } from 'react'; import { useState } from 'react'; function App() { const [count,setcontent] = useState(0); useEffect(()=>{ console.log('点击了一下') }) // 这个只要组件变化了就会执行这个代码 return ( <div> <button onClick={()=>{setcontent(count+1)}}> {count} </button> </div> ); } export default App;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { useEffect } from 'react'; import { useState } from 'react'; function App() { const [count,setcontent] = useState(0); useEffect(()=>{ console.log('点击了一下') },[]) // 只会执行一次代码,就是初始化的时候执行一次代码 return ( <div> <button onClick={()=>{setcontent(count+1)}}> {count} </button> </div> ); } export default App;
比如在useEffect函数内部有一个定时器,如果没有return一个回调函数的话,就会出现内存写了的问题,在这个组件不再调用时,就是这个组件卸载,就会执行这个回调函数
自定义Hook函数
就是创建一个函数,同时把需要传递出去的数据return出去,在别的函数中进行声明使用就可以了
自定义Hook函数需要注意的事项:
如何使用redux
:
1 2 npm install @reduxjs/toolkit npm install react-redux
使用这两个命令可以安装这个reduxil
store.modules.counterStore.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import { createSlice } from "@reduxjs/toolkit"; const createStore = createSlice({ name: "counter", // 初始化数据 initialState: { value: 0, }, // 修改数据的方法 reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, }, }); // 解析函数出来 const { increment, decrement } = createStore.actions; // 获取reducer const counterReducer = createStore.reducer; export { increment, decrement }; export default counterReducer;
store
1 2 3 4 5 6 7 8 9 10 import { configureStore } from "@reduxjs/toolkit"; import counterReducer from "./modules/counterStore"; const store = configureStore({ reducer: { counter: counterReducer, }, }); export default store;
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import reportWebVitals from "./reportWebVitals"; import store from "./store"; import { Provider } from "react-redux"; const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode> ); reportWebVitals();
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { useSelector } from "react-redux"; import { useDispatch } from "react-redux"; import { increment, decrement } from "./store/modules/counterStore"; function App() { const {value} =useSelector(state=>state.counter) const dispatch = useDispatch(); return ( <div className="App"> <button onClick={()=>dispatch(increment())}>+</button> {value} <button onClick={()=>dispatch(decrement())}>-</button> </div> ); } export default App;
接下来就是可以传入数据对管理中的数据进行修改
store.modules.counterStore.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import { createSlice } from "@reduxjs/toolkit"; const createStore = createSlice({ name: "counter", // 初始化数据 initialState: { value: 0, }, // 修改数据的方法 reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, ddnumber: (state, action) => { state.value += action.payload; }, }, }); // 解析函数出来 const { increment, decrement, ddnumber } = createStore.actions; // 获取reducer const counterReducer = createStore.reducer; export { increment, decrement, ddnumber }; export default counterReducer;
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { useSelector } from "react-redux"; import { useDispatch } from "react-redux"; import { increment, decrement, ddnumber } from "./store/modules/counterStore"; function App() { const {value} =useSelector(state=>state.counter) const dispatch = useDispatch(); return ( <div className="App"> <button onClick={()=>dispatch(increment())}>+</button> {value} <button onClick={()=>dispatch(decrement())}>-</button> <button onClick={()=>dispatch(ddnumber(10))}>加上10</button> </div> ); } export default App;
配置路由:
1 npm install react-router-dom
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { createBrowserRouter, RouterProvider } from "react-router-dom"; const router = createBrowserRouter([ { path: "/", element: <App /> }, { path:"/information", element: <h1>Information</h1> } ]) const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <React.StrictMode> <Provider store={store}> <RouterProvider router={router} /> </Provider> </React.StrictMode> );
两种路由跳转方法:(有两种)
在 React 应用中,使用 react-router-dom 进行路由跳转时,常见的路由跳转方式有以下几种:
1. 使用 组件进行声明式跳转 组件是 react-router-dom 提供的一个用于创建链接的组件,它会渲染成一个 标签,点击后会进行路由跳转。1 2 3 4 5 6 7 8 9 10 11 12 13 import { Link } from "react-router-dom" ;const InformationPage = ( ) => { return ( <div > <h1 > Information Page</h1 > {/* 使用 Link 组件进行跳转 */} <Link to ="/another-page" > Go to Another Page</Link > </div > ); }; export default InformationPage ;
2. 使用 useNavigate Hook 进行编程式跳转 useNavigate 是 react-router-dom v6 中提供的一个 Hook,用于在函数组件中进行编程式路由跳转。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { useNavigate } from "react-router-dom" ;const InformationPage = ( ) => { const navigate = useNavigate (); const handleClick = ( ) => { navigate ("/another-page" ); }; return ( <div > <h1 > Information Page</h1 > <button onClick ={handleClick} > Go to Another Page</button > </div > ); }; export default InformationPage ;
3. 使用 useHistory(适用于 react-router-dom v5) 在 react-router-dom v5 中,可以使用 useHistory Hook 进行编程式路由跳转。不过在 v6 中,useHistory 已被 useNavigate 替代。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import { useHistory } from "react-router-dom" ;const InformationPage = ( ) => { const history = useHistory (); const handleClick = ( ) => { history.push ("/another-page" ); }; return ( <div > <h1 > Information Page</h1 > <button onClick ={handleClick} > Go to Another Page</button > </div > ); }; export default InformationPage ;
总结 声明式跳转 :使用 组件,适合在界面上创建链接进行跳转。编程式跳转 :使用 useNavigate(v6)或 useHistory(v5),适合在事件处理函数中进行跳转,例如点击按钮后跳转。在实际开发中,你可以根据具体的需求选择合适的跳转方式。如果你使用的是 react-router-dom v6,建议使用 组件和 useNavigate Hook 进行路由跳转。
1. 通过 URL 参数传递 可以在 to 属性中添加查询字符串来传递参数。接收组件可以通过 useSearchParams
Hook 来获取这些参数
1 2 3 4 {} <Link to="/information?name=John&age=30" >跳转到information</Link >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { useSearchParams } from "react-router-dom" ;const Information = ( ) => { const [searchParams] = useSearchParams (); const name = searchParams.get ('name' ); const age = searchParams.get ('age' ); return ( <div > <p > Name: {name}</p > <p > Age: {age}</p > </div > ); }; export default Information ;
2. 通过状态对象传递 可以通过 state 属性传递一个对象,该对象包含你想要传递的参数。接收组件可以通过 useLocation Hook 来获取这些参数。 修改选中代码
1 2 3 4 {} <Link to="/information" state={{ name : 'John' , age : 30 }}>跳转到information</Link >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import { useLocation } from "react-router-dom" ;const Information = ( ) => { const location = useLocation (); const { name, age } = location.state || {}; return ( <div > <p > Name: {name}</p > <p > Age: {age}</p > </div > ); }; export default Information ;
以上两种方法都可以在 组件中传递参数,你可以根据具体需求选择合适的方法。
在 React Router 中,useNavigate 返回的 navigate 函数可以接收多个参数来实现不同的导航需求,下面详细介绍如何传递参数:
1. 传递查询参数 查询参数通常用于在 URL 中传递一些可选的信息,你可以通过在 navigate 函数的第二个参数中使用 search 字段来传递查询参数。
1 2 3 4 5 6 7 8 9 const navigate = useNavigate (); const handleClick = ( ) => { navigate ('/destination' , { search : '?param1=value1¶m2=value2' }); };
2. 传递状态参数 状态参数可以在导航过程中传递一些数据,这些数据不会显示在 URL 中,而是存储在浏览器的历史记录中。你可以通过在 navigate 函数的第二个参数中使用 state 字段来传递状态参数。
1 2 3 4 5 6 7 8 9 10 11 12 const navigate = useNavigate (); const handleClick = ( ) => { navigate ('/destination' , { state : { data : 'This is some data' , anotherData : { key : 'value' } } }); };
3. 结合使用查询参数和状态参数 你也可以同时传递查询参数和状态参数。
1 2 3 4 5 6 7 8 9 10 11 12 const navigate = useNavigate (); const handleClick = ( ) => { navigate ('/destination' , { search : '?param1=value1' , state : { data : 'This is some data' } }); };
在目标页面获取参数 获取查询参数 在目标页面,你可以使用 useSearchParams 来获取查询参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 import { useSearchParams } from 'react-router-dom' ;function Destination ( ) { const [searchParams] = useSearchParams (); const param1 = searchParams.get ('param1' ); return ( <div > <p > Param1: {param1}</p > </div > ); } export default Destination ;
获取状态参数 在目标页面,你可以使用 useLocation 来获取状态参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 import { useLocation } from 'react-router-dom' ;function Destination ( ) { const location = useLocation (); const data = location.state ?.data ; return ( <div > <p > Data: {data}</p > </div > ); } export default Destination ;
通过上述方法,你可以在使用 useNavigate 进行导航时传递不同类型的参数,并在目标页面获取这些参数
二级路由出口用
来表示二级路由出口
默认二级路由可以将path设置为inidex而且为true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const router = createBrowserRouter ([ { path : "/" , element : <App /> , children :[ { index :true , element : <Infor > </Infor > }, { path :"/user" , element : <User > </User > } ] }, { path :"/information" , element : <Infor > </Infor > }, { path :"/user" , element : <User > </User > } ])
配置404页面
在路由配置的末尾最后添加这个组件就可以实现访问到其他路由时就会显示404页面的功能
两种路由模式
配置别名路径:
在 React 项目中配置别名路径可以提高代码的可维护性和可读性,让你在引入模块时不必使用冗长的相对路径。下面以 create-react-app 创建的项目为例,介绍如何配置别名路径。
1. 使用 craco 或 react-app-rewired(针对 create-react-app 项目) create-react-app 默认不支持直接配置别名路径,需要借助 craco (Create React App Configuration Override)或者 react-app-rewired 来实现。这里以 craco 为例。
安装 craco 1 npm install @craco/craco --save-dev
修改 package.json 将 package.json 中的 scripts 部分替换为使用 craco:
1 2 3 4 5 6 7 { "scripts" : { "start" : "craco start" , "build" : "craco build" , "test" : "craco test" } }
创建 craco.config.js 文件 在项目根目录下创建 craco.config.js 文件,并配置别名路径:
1 2 3 4 5 6 7 8 9 10 11 const path = require ('path' );module .exports = { webpack : { alias : { '@components' : path.resolve (__dirname, 'src/components' ), '@pages' : path.resolve (__dirname, 'src/pages' ), '@utils' : path.resolve (__dirname, 'src/utils' ) } } };
上述代码中,我们定义了三个别名:
@components 指向 src/components 目录。 @pages 指向 src/pages 目录。 @utils 指向 src/utils 目录。 2. 在代码中使用别名路径 配置好别名后,就可以在代码中使用这些别名来引入模块了。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import React from 'react' ;import { BrowserRouter as Router , Link } from 'react-router-dom' ;import Home from '@pages/Home' ; const App = ( ) => { return ( <Router > <nav > <ul > <li > <Link to ="/" > Home</Link > </li > </ul > </nav > <Home /> </Router > ); }; export default App ;
3. 配置 TypeScript (如果项目使用 TypeScript) 如果你的项目使用 TypeScript,还需要在 tsconfig.json 中配置 compilerOptions.paths 以支持别名路径:
1 2 3 4 5 6 7 8 9 10 { "compilerOptions" : { "baseUrl" : "." , "paths" : { "@components/*" : ["src/components/*" ], "@pages/*" : ["src/pages/*" ], "@utils/*" : ["src/utils/*" ] } } }
配置提示信息:
若你使用的是 JavaScript 项目(而非 TypeScript 项目),可以借助 jsconfig.json 文件来配置路径别名,让编辑器能识别这些别名,提供代码提示和导航功能。下面为你详细介绍如何配置 jsconfig.json 文件。
1. 创建 jsconfig.json 文件 在项目根目录下创建 jsconfig.json 文件,若项目已存在该文件,可直接编辑。
2. 配置 jsconfig.json 在 jsconfig.json 中添加 compilerOptions 字段,用于配置路径别名。以下是一个示例:
1 2 3 4 5 6 7 8 9 10 11 { "compilerOptions" : { "baseUrl" : "." , "paths" : { "@components/*" : [ "src/components/*" ] , "@pages/*" : [ "src/pages/*" ] , "@utils/*" : [ "src/utils/*" ] } } , "include" : [ "src" ] }
baseUrl :指定基础路径,一般设为项目根目录( . )。 paths :定义路径别名,键为别名,值为对应的实际路径。 include :指定要包含的文件或目录,这里设置为 src ,表示只包含 src 目录下的文件。 3. 结合 craco 配置 如果你已经使用 craco 配置了 Webpack 的别名路径,那么 jsconfig.json 中的配置需要和 craco.config.js 中的配置保持一致。以下是完整的配置示例: craco.config.js
1 2 3 4 5 6 7 8 9 10 11 const path = require ('path' );module .exports = { webpack : { alias : { '@components' : path.resolve (__dirname, 'src/components' ), '@pages' : path.resolve (__dirname, 'src/pages' ), '@utils' : path.resolve (__dirname, 'src/utils' ) } } };
jsconfig.json
1 2 3 4 5 6 7 8 9 10 11 { "compilerOptions" : { "baseUrl" : "." , "paths" : { "@components/*" : [ "src/components/*" ] , "@pages/*" : [ "src/pages/*" ] , "@utils/*" : [ "src/utils/*" ] } } , "include" : [ "src" ] }