react从不会到入门
- 1_react初识
- 1.1_react基础环境搭建
- 1.2_文件目录介绍
- 1.2_JSX基础
- 1.2.1_JSX介绍
- 1.2.2_JSX表达式
- 1.2.3_列表渲染
- 1.2.4_条件渲染
- 1.2.5_函数调用
- 1.2.6_样式控制
- 2_组件基础
- 2.1_函数组件
- 2.2_点击事件
- 3_组件通讯
- 3.1_父子关系
- 4_生命周期
- 4.1_挂载阶段
- 4.2_更新阶段
- 5_Hook基础
- 5.1_useState(用于参数的更新和渲染)
- 5.2_组件的更新过程
- 5.3_useEffect
- 5.4_useRef
- 5.5_useContext
- 6_react-router
1_react初识
react教程
1.1_react基础环境搭建
方法一:使用npm安装react脚手架
npm install -g create-react-app
,然后create-react-app app-name
方法二:直接使用react脚手架搭建项目结构
npx create-react-app app-name
,其中app-name的“-”不能省略,npx是临时安装脚手架,构建完项目后会自动删除。
1.2_文件目录介绍
package.json
{
//项目依赖目录"dependencies": {"@testing-library/jest-dom": "^5.16.5","@testing-library/react": "^13.4.0","@testing-library/user-event": "^13.5.0","react": "^18.2.0","react-dom": "^18.2.0","react-scripts": "5.0.1","web-vitals": "^2.1.4"},
//命令列表"scripts": {"start": "react-scripts start","build": "react-scripts build","test": "react-scripts test","eject": "react-scripts eject"},"eslintConfig": {"extends": ["react-app","react-app/jest"]}
}
index.js
//React:框架的核心包
//ReactDOM:专门做渲染相关的包
import React from 'react';
import ReactDOM from 'react-dom/client';
//引入全局的样式文件
import './index.css'
//项目的根组件
import App from './App';
//获取root元素,root元素位于public目录下index.html中
const root = ReactDOM.createRoot(document.getElementById('root'));
//对root进行渲染
root.render(//如果是react18版本需要去掉<React.StrictMode>标签<React.StrictMode><App /></React.StrictMode>
);
1.2_JSX基础
1.2.1_JSX介绍
概念:JSX是JavaScript XML (HTML)的缩写,表示在JS代码中书写HTML结构
作用:在React中创建HTML结构(页面UI结构)
原理:JSX并不是标准的JS语法,是JS的语法扩展,浏览器默认是不识别的,脚手架中内置的@babel/plugin-transform-react-jsx包,用来解析该语法
1.2.2_JSX表达式
语法:
{JS表达式}//将此式放入html语法中即可,注意只能是表达式不能是语句(如if等语句则报错)
const name = '佳佳'
const flag = true
function App() {return (<div className="App">{name}{flag?'周周':'蛇蛇'}</div>);
}
export default App;
1.2.3_列表渲染
const songs = [{ id: 1, name: '痴心绝对' },{ id: 2, name: '像我这样的人' },{ id: 3, name: '南山南' }
]
function App() {return (<div className="App">//使用map进行逐个渲染{songs.map(itemName => <li key={songs.id}>{itemName.name}</li>)}//等价于<li key={songs[0].id}>{songs[0].name}</li><li key={songs[1].id}>{songs[1].name}</li><li key={songs[2].id}>{songs[2].name}</li></div>);
}
export default App;
1.2.4_条件渲染
// 来个布尔值
const flag = true
function App() {return (<div className="App">{/* 条件渲染字符串 */}{flag ? 'react真有趣' : 'vue真有趣'}{/* 条件渲染标签/组件 */}{flag ? <span>this is span</span> : null}</div>)
}
export default App
1.2.5_函数调用
const fun = (type)=>{if (type === 1)return <h1>蛇蛇</h1>else return <h1>周周</h1>
}
function App() {return (<div className="App">{fun(1)}</div>);
}
export default App;
1.2.6_样式控制
- 行内样式
const styleObj = {color:red
}
function App() {return (<div className="App"><div style={ styleObj }>this is a div</div></div>)
}
export default App
- 类名样式
创建一个css文件,然后在本文件导入即可使用css中已声明好的样式
app.css
.title {font-size: 30px;color: blue;
}
app.js
import './app.css'
function App() {return (<div className="App"><div className='title'>this is a div</div></div>)
}
export default App
2_组件基础
2.1_函数组件
- 组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
- 函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null
2.2_点击事件
function App() {//e是点击事件信息const onclickEvent=(e,str)=>{console.log(e,str)}return (<div className="App" onClick={(e)=>{onclickEvent(e,"蛇蛇")}}>点击一下</div>);
}
export default App;
3_组件通讯
组件是独立且封闭的单元,默认情况下组件只能使用自己的数据(state)
组件化开发的过程中,完整的功能会拆分多个组件,在这个过程中不可避免的需要互相传递一些数据
为了能让各组件之间可以进行互相沟通,数据传递,这个过程就是组件通信
- 父子关系 - 最重要的
- 兄弟关系 - 自定义事件模式产生技术方法 eventBus / 通过共同的父组件通信
- 其它关系 - mobx / redux / zustand
3.1_父子关系
父传子:
1. props是只读对象(readonly)
根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改
2. props可以传递任意数据
数字、字符串、布尔值、数组、对象、函数、JSX
import React from 'react'// 函数式子组件
function FSon(props) {console.log(props)return (<div>子组件1{props.msg}</div>)
}
// 父组件
class App extends React.Component {state = {message: 'this is message'}render() {return (<div><div>父组件</div><FSon msg={this.state.message} /></div>)}
}export default App
子传父:
子组件调用父组件的函数进行传值;
import React from 'react'// 子组件
function Son(props) {function handleClick() {// 调用父组件传递过来的回调函数 并注入参数props.changeMsg('this is newMessage')}return (<div><button onClick={handleClick}>change</button></div>)
}class App extends React.Component {state = {message: 'this is message'}// 提供回调函数changeMessage = (newMsg) => {console.log('子组件传过来的数据:',newMsg)this.setState({message: newMsg})}render() {return (<div><Sonmsg={this.state.message}// 传递给子组件changeMsg={this.changeMessage}/></div>)}
}export default App
4_生命周期
react教程
4.1_挂载阶段
钩子 函数 | 触发时机 | 作用 |
---|---|---|
constructor | 创建组件时,最先执行,初始化的时候只执行一次 | 1. 初始化state 2. 创建 Ref 3. 使用 bind 解决 this 指向问题等 |
render | 每次组件渲染都会触发 | 只用于渲染UI(注意: 不能在里面调用setState() ) (与 挂载阶段 是同一个render) |
componentDidMount | 组件挂载(完成DOM渲染)后执行,初始化的时候执行一次 | 1. 发送网络请求 2.DOM操作 |
4.2_更新阶段
钩子函数 | 触发时机 | 作用 |
---|---|---|
render | 每次组件渲染都会触发 | 只用于渲染UI(与 挂载阶段 是同一个render) |
componentDidUpdate | 组件更新后(DOM渲染完毕) | DOM操作,可以获取到更新后的DOM内容,不要直接调用setState |
5_Hook基础
Hooks的出现解决了俩个问题 1. 组件的状态逻辑复用 2.class组件自身的问题
- 组件的逻辑复用
在hooks出现之前,react先后尝试了 mixins混入,HOC高阶组件,render-props等模式
但是都有各自的问题,比如mixin的数据来源不清晰,高阶组件的嵌套问题等等 - class组件自身的问题
class组件就像一个厚重的‘战舰’ 一样,大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this指向问题等等,而我们更多时候需要的是一个轻快灵活的’快艇’
5.1_useState(用于参数的更新和渲染)
使用步骤
- 导入
useState
函数 - 调用
useState
函数,并传入状态的初始值 - 从
useState
函数的返回值中,拿到状态和修改状态的方法 - 在JSX中展示状态
- 调用修改状态的方法更新状态
import { useState } from 'react'function App() {// 参数:状态初始值比如,传入 0 表示该状态的初始值为 0// 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState),名字可以自定义const [count, setCount] = useState(0)return (<button onClick={() => { setCount(count + 1) }}>{count}</button>)
}
export default App
5.2_组件的更新过程
- 函数组件第一次渲染
-
- 从头开始执行该函数组件中的代码逻辑
- 调用
useState(0)
将传入的参数作为状态初始值,即:0 - 渲染组件,此时,获取到的状态 count 值为: 0
- 函数组件第二次渲染 (useState会被自动监听,所以当count改变时,就会重新渲染)
-
- 点击按钮,调用
setCount(count + 1)
修改状态,因为状态发生改变,所以,该组件会重新渲染 - 组件重新渲染时,会再次执行该组件中的代码逻辑
- 再次调用
useState(0)
,此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1 - 再次渲染组件,此时,获取到的状态 count 值为:1
- 点击按钮,调用
5.3_useEffect
1、什么是副作用
副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)
常见的副作用
- 数据请求 ajax发送
- 手动修改dom
- localstorage操作
useEffect函数的作用就是为react函数组件提供副作用处理的!
2、监听变量变化
useEffect注意事项(useEffect是在dom渲染之后才执行的)
-
不加依赖项:初始化+重新渲染
-
加[]:初始化执行一次
-
加特定的依赖项[count , name]就会监听这两种属性
function App() { const [count, setCount] = useState(0) const [name, setName] = useState('str') useEffect(() => { console.log('副作用执行了') }, [count],[name]) return ( <> <button onClick={() => { setCount(count + 1) }}>{count}</button> <button onClick={() => { setName('cp') }}>{name}</button> </> )
}
3、在函数组件销毁的时候清除副作用
import { useEffect, useState } from "react"function Test(){useEffect(() => {const timerId = setInterval(() => {console.log("1s打印一次")}, 1000)return () => {// 用来清理副作用的事情,当App组件被销毁的时候clearInterval(timerId)}})return (<div>{'test'}</div>)
}const App = () => {const [flag, setFlag] = useState(true)return (<div>{flag?<Test/>:null}//点击按钮之后,flag变false,组件Test销毁<button onClick={() => setFlag(!flag)}>swicth</button></div>)
}export default App
5.4_useRef
使用场景
在函数组件中获取真实的dom元素对象或者是类组件对象
使用步骤
- 导入
useRef
函数 - 执行
useRef
函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例) - 通过ref 绑定 要获取的元素或者组件
import { useEffect, useRef } from 'react'function App() { const h1Ref = useRef(null) useEffect(() => { console.log(h1Ref) },[]) return ( <div> <h1 ref={ h1Ref }>this is h1</h1> </div> )
}
export default App
5.5_useContext
作用:将父组件的值可以直接传给孙子组件
import { createContext, useContext } from 'react'
// 创建Context对象
const Context = createContext()function Foo() { return <div>Foo <Bar/></div>
}function Bar() { // 底层组件通过useContext函数获取数据 const name = useContext(Context) return <div>Bar {name}</div>
}function App() { return ( // 顶层组件通过Provider 提供数据 <Context.Provider value={'this is name'}> <div><Foo/></div> </Context.Provider> )
}export default App