使用{}识别变量
const name = 'ashin'
const getAge = ()=>{
return 22
}
const flag = true
function App() {
return (
<div className="App">
{name}
{getAge()}
{flag?'很厉害':'很菜'}
</div>
);
}
技术方案 map-------------------vue里面使用v-for
const songs = [
{id:1,name:'ashin',age:22,},
{id:2,name:'john',age:23,},
{id:3,name:'Tom',age:24,}
]
<ul>
{songs.map(song=><li key={song.id}>{song.name}</li>)}
</ul>
技术方案:三元表达式或者逻辑&&运算----- vue里面使用v-if或v-show
const flag = true
{ flag ? <div><span>三元表达式实现条件渲染</span></div>:null }
{ flag && <div><span>逻辑与实现条件渲染 </span></div> }
const getHdom = (type)=>{
if(type===1) return <h1>this is h1</h1>
if(type===2) return <h2>this is h2</h2>
if(type ===3 ) return <h3>this is h3</h3>
}
function App() {
return (
<div className="App">
{ getHdom(2) }
</div>
);
}
1.行内样式—在元素身上绑定一个style属性即可
2.类名样式—在元素身上绑定className属性
// 1.直接写在行内
<span style={{color:'red',fontSize:'30px'}}>行内样式控制</span>
//2.把样式抽离出来
const sty = {
color:'red',
fontSize:'30px'
}
<span style={sty}>行内样式控制</span>
// 3.类名样式
<span className='active'>类名样式</span>
const activeFlag = true
<span className={activeFlag?'active':''}>动态控制类名样式</span>
1. JSX必须只有一个根节点 或者使用幽灵节点<>>包裹
2.所以标签必须形成闭合,成对闭合或者自闭合都可以
3.JSX中的语法更加贴近JS语法,属性名采用驼峰命名法 class–>className 、for–>htmlFor
4.JSX支持多行(换行),如果需要换行,需使用()包裹,防止bug出现
约定说明:
1.组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
2.函数组件必须有返回值,表示该组件的UI结构;如果不需要渲染任何内容,则返回null
3.组件就像HTML标签一样可以被渲染到页面中,组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
4.使用函数名称作为组件标签名称,可以成对出现也可以自闭合
function Hello(){
return <div>Hello</div>
}
function App(){
return (
<div>
<Hello/>
</div>
)
}
class HelloComponent extends React.component{
render(){
return <div>this is class component</div>
}
}
function App(){
return (
<div>
<HelloComponent/>
</div>
)
}
1.函数组件:没有状态,也叫无状态组件;负责静态结构的展示
2.类组件:有状态,也叫有状态组件;提供状态,提供交互
3.状态也叫数据,是私有的。
4.状态也就像vue2里面的data,vue3里面的ref、reactive
语法: on + 事件名称 = {事件处理程序}
// 1.函数组件
function HelloFn(){
const clickHandle = ()=>{
console.log('被点击了!')
}
return <div onClick={clickHandle}>点击我</div>
}
// 2.类组件
class HelloComponent extends React.component{
const clickHandle = ()=>{
console.log('被点击了!')
}
render(){
return <div onClick={this.clickHandle}>this is class component</div>
}
}
function HelloFn(){
const clickHandle = (e)=>{
console.log(e)
console.log('被点击了!')
e.preventDefault() // 阻止默认行为
}
return <div><a onClick={clickHandle} href="http://www.baidu.com">百度</a></div>
}
1.只需要一个额外参数:{}里改变为箭头函数即可
{()=>clickHandle(‘自定义参数’)}
2.既需要事件对象e又需要额外参数
{(e)=>clickHandle(e,‘自定义参数’)}
import React from 'react'
class TestComponent extends React.component{
// 1.定义组件状态
state = {
// 在这里可以定义各种属性,全部都是当前组件的状态
name:'ashin'
}
changeName = ()=>{
// 2.修改state中的属性
this.setState({
name:'阿信'
})
}
render(){
return (
<div>
// 3.使用状态
当前的name为:{this.state.name}
<button onClick={this.changeName}>修改</button>
</div>
)
}
}
0.state相当于vue中的data,也就是用于存放数据的,但是在使用上和data很不一样
1.react编写组件其实就是写原生js类或者函数
2.定义状态必须通过state实例属性的方法提供一个对象,名称是固定的就叫做state
3.修改state中的任何属性都不可以直接赋值,必须使用setState方法。这个方法来自继承得到
4.这里的this关键词,很容易出现指向错误的问题,需要规范书写
第一种写法 箭头函数写法 推荐写法
class TestComponent extends React.Component {
state = {
name: 'ashin'
}
changeName = () => {
this.setState({
name: '阿信'
})
}
render () {
return (
<div>
当前的name为{this.state.name}
<button onClick={this.changeName}>修改</button>
</div>
)
}
}
第二种写法 使用bind改变this指向-- 了解即可
class TestComponent extends React.Component {
constructor(){
super()
this.changeName = this.changeName.bind(this)
}
state = {
name: 'ashin'
}
changeName() {
this.setState({
name: '阿信'
})
}
render () {
return (
<div>
当前的name为{this.state.name}
<button onClick={this.changeName}>修改</button>
</div>
)
}
}
第三种写法 在模板调用函数的时候使用箭头函数
class TestComponent extends React.Component {
state = {
name: 'ashin'
}
changeName () {
this.setState({
name: '阿信'
})
}
render () {
return (
<div>
当前的name为{this.state.name}
<button onClick={()=>this.changeName()}>修改</button>
</div>
)
}
}
步骤:
1.声明用来控制input value的react组件的状态
2.给value绑定react state
3.给input绑定change事件,为了拿到当前输入的数据
4.写回调函数修改state中绑定的value的值
class InputHand extends React.Component {
// 1.声明用来控制input value的react组件的状态
state = {
message: 'ashin'
}
// 4.写回调函数修改state中绑定的value的值
inputChange = (e) => {
console.log('触发了', e, this)
this.setState({
message: e.target.value
})
}
render () {
return (
// 2. 给value绑定react state
// 3.给input绑定change事件,为了拿到当前输入的数据
<div>
<input type="text" value={this.state.message} onChange={this.inputChange} />
</div>
)
}
}
1.导入createRef函数
2.调用createRef函数,创建一个ref对象,存储名为msgRef的实例属性中
3.为input添加ref属性,值为msgRef
4.在按钮的事件处理函数中,通过msgRef.current即可拿到input对应的dom元素,而其中msgRef.current.value拿到的就是文本框的值
import React,{createRef} from 'react'
class Input extends React.Component{
msgref = create()
getValue = () => {
console.log(this.msgref.current.value)
}
render () {
return (
<div>
<input type="text" ref={this.msgref} />
<button onClick={this.getValue}>获取</button>
</div>
)
}
}
父传子:props
// 函数式的Son
function SonF (props) {
// peops是一个对象,里面存着通过父组件传递过来的所以数据
return (
<div>this is Sonc: {props.msg}</div>
)
}
// 类组件的Son
class SonC extends React.Component {
// 类组件必须通过this关键词获取这里的props是固定的
render () {
return (
<div>this is SonC:{this.props.msg}</div>
)
}
}
class Father extends React.Component {
state = {
message: 'data'
}
render () {
return (
<div>
<SonC msg={this.state.message} />
<SonF msg={this.state.message} />
</div>
)
}
}
子传父: 子组件调用父组件传递过来的参数,并且把想要传递的数据当成函数的实参传入即可
function Son(props){
const { getMsg } = props
return (
<div>
<span>this is Son:</span>
<button onClick={()=>getMsg('子组件传递的数据')}>click</button>
</div>
)
}
class Father extends React.Component{
getMessage = (sonMsg)=>{
console.log(sonMsg)
}
<Son getMsg={getMessage} />
}
兄弟组件的通信 先传给父组件,在由父组件传给兄弟组件
// SonA 传递数据给 SonB
function SonA (props) {
return (
<>
<div>this is SonA:</div>
<button onClick={() => props.getAMsg('A发送过去的数据')}>发送数据</button>
</>
)
}
function SonB (props) {
return (
<div>this is SonB:{props.sendBMsg}</div>
)
}
class App extends React.Component{
state = {
msg: '默认数据'
}
getA = (data) => {
this.setState({
msg: data
})
}
<SonA getAMsg={this.getA}></SonA>
<SonB sendMsg={this.state.msg}></SonB>
}
例如: App --> A–> C
App数据 --> C
注意事项:
1.上层组件和下层组件是相对的,只要存在就可以使用,通常我们会通过App作为数据提供方
2.这里涉及到的语法都是固定的,有两处,第一处是提供方:必须是value={传递的数据};第二处是接收方:必须是{value=>使用value做什么都可以}
// 1.导入createContext方法并执行,结构是提供者Provider和消费者Comsumer分别包裹,与vue中的provider和inject类似
const {Provider,Consumer} = createContext()
function C(){
return (
<>
<div>this is C</div>
<Consumer>
{value=><span>{value}</span>}
</Consumer>
</>
)
}
function A(){
return (
<div>
this is A
<C />
</div>
)
}
class App extends React.Component{
state = {
msg:'this is data'
}
render(){
return (
<Provider value={this.state.msg}>
<div>
<A />
</div>
</Provider>
)
}
}
在子组件中写的东西都被存储在children中,可以写普通文本、普通标签元素、函数、JSX
function ListItem({children}){
return (
<div>ListItem,{children}</div>
)
}
class App extends React.Component{
render(){
return (
<div>
<ListItem>
this is children
</ListItem>
</div>
)
}
}
function List({pageSize = 10}) {
return (
<div>
此处展示props的默认值:{ pageSize }
</div>
)
}
// 不传入pageSize属性
<List />
class List extends Component {
static defaultProps = {
pageSize: 10
}
render() {
return (
<div>
此处展示props的默认值:{this.props.pageSize}
</div>
)
}
}
<List />
只有类组件才有生命周期,函数组件是没有的(类组件实例化,函数组件不需要实例化)
挂载阶段:
卸载阶段:
componentWillUnmount:组件卸载(从页面中消失),作用:执行清理工作(比如:清理定时器)
React的设计理念:UI = f(data)
1.什么是hook?
Hook的本质:一套能够使函数组件更加强大,更灵活的钩子
2.Hook解决了什么问题?
1.解决了状态逻辑复用。2.class组件自身的问题
3.Hook的优势:
1.告别难以理解的class 2.解决业务逻辑难以拆分的问题 3.使状态逻辑复用变的简单可行 4.函数组件在设计思想上,更加契合React的设计理念
注意事项:1.只能出现在函数组件中;2.不能嵌套在if/for/其他函数中(react按照hooks的调用顺序识别每一个hook)
1.导入useState函数
2.执行这个函数并且传入初始值,必须在函数组件中
3.[数据,修改数据的方法]
4.使用数据 修改数据
import {useState} from 'react'
function App(){
const [count.setCount] = useState(0) //解构赋值写法,名字可以自定义,setCount函数作用用来修改count 不能修改原值而是生成新值替换
return (
<div>
<button onClick={()=>setCount(count++)}>{count}</button>
</div>
)
}
副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。对于React组件来说,主作用就是根据数据渲染UI。除此之外都是副作用(比如:数据请求ajax,手动修改dom,localstorage操作);useEffect函数就是为函数组件提供副作用处理的
使用:
1.导入useEffect函数
2.在函数组件中执行 传入回调 并且定义副作用
3.当我们通过修改状态更新组件时,副作用也会不断执行
import {useState,useEffect} from 'react'
function App(){
const [count,setCount] = useState(0)
useEffect(()=>{
// 定义副作用
document.title = count
})
return (
<div>
<button onClick={()=>setCount(count+1)>{count}</button>
</div>
)
}
1.不添加依赖项: 组件首次渲染执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行
useEffect(()=>{
console.log('副作用执行了')
})
2.添加空数组: 组件只在首次渲染时执行一次
useEffect(()=>{
console.log('副作用执行了')
},[])
3.添加特定依赖项: 首次渲染时执行,在依赖项发生变化时重新执行
function App() {
const [count, setCount] = useState(0)
const [name, setName] = useState('zs')
useEffect(() => {
console.log('副作用执行了')
}, [count])
return (
<>
<button onClick={() => { setCount(count + 1) }}>{count}</button>
<button onClick={() => { setName('cp') }}>{name}</button>
</>
)
}
import { useState } from "react"
export function useWindowScroll () {
const [y, setY] = useState(0)
window.addEventListener('scroll', () => {
const h = document.documentElement.scrollTop
setY(h)
})
return [y]
}
import {useState,useEffect} from 'react'
export function useLocalStorage(key,defaultValue){
const [message,setMessage] = useState(defaultValue)
useEffect(()=>{
window.localStorage.setItem(key,message)
},[key,message])
return [message,setMessage]
}
import {useLocalStorage} from './'
function App(){
const [message,setMessage] = useLocalStorage('hook-key','ashin')
setTimeOut(()=>{
setMessage('阿信')
},3000)
return (
<div>{message}</div>
)
}
如果要初始化的数据无法直接得到需要通过计算才能获取到,使用useState(()=>{})
import { useState } from 'react'
function Counter(props) {
const [count, setCount] = useState(() => {
return props.count
})
return (
<div>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
)
}
function App() {
return (
<>
<Counter count={10} />
<Counter count={20} />
</>
)
}
export default App
例如清除定时器:如果不清理,组件虽然已经不显示了,但是定时器依旧在运行,在return函数中清理副作用
function Test(){
useEffect(()=>{
let timer = setInterval(()=>{
console.log('定时器开启')
},1000)
return ()=>{
//添加清除副作用函数
clearInterval(timer)
}
})
}
在类组件中是在生命周期componentUnMounted里发送请求,而在hook中,是用useEffect中
function App(){
useEffect(()=>{
async function getData(){
const data = await.axios.get('http://geek.itheima.net/v1_0/channels')
},[])
}
错误写法
function App(){
useEffect(async ()=>{
const data = await.axios.get('http://geek.itheima.net/v1_0/channels')
},[])
}
在函数组件中获取真实dom元素对象或者组件对象
使用步骤
1.导入useRef函数
2.执行useRef函数并且传入null值,返回值为一个对象,里面有一个current属性存放拿到的dom对象
3.通过ref绑定 要获取的元素或组件
const {useRef} from 'react'
function App(){
const testRef = useRef(null)
useEffect(()=>{
// 拿到了dom元素
console.log('testRef.current')
},[])
return (
<div ref={testRef}>this is test</div>
)
}
使用步骤:
1.使用createContext创建Context对象
2.在顶层组件中使用Provider提供数据
3.在底层组件中通过useContext函数获取数据
import { useState, useContext, createContext } from 'react'
const Context = createContext()
function ComA () {
const num = useContext(Context)
return (
<div>
this is ComA
App组件传过来的数据:{num}
<ComB />
</div>
)
}
function ComB () {
const num = useContext(Context)
return (
<div>
this is ComB
App组件传过来的数据:{num}
</div>
)
}
function App(){
const [num,setNum] = useState(10)
<Context.Provider value={num}>
<div>
<ComA></ComA>
<button onClick={()=>{setNum(num+1)}}>+</button>
</div>
</Context.Provider>
}
基础使用:
1.安装 yarn add react-router-dom@6
2.核心组件BrowerRouter
两种常用Router:HashRouter和BrowseRouter;
import {HashRouter} from 'react'
function App(){
return (
// 声明当前要用一个hash模式的路由
<HashRouter>
// 指定跳转的组件 to用来匹配路由的地址
<link to="/">首页</link>
<link to="/about">关于</link>
// 路由出口 路由对应的组件会在这里进行渲染 相当于vue中router-view
<Routes>
// 指定路径和组件的对应关系 path代表路径 element代表组件
<Route path='/' element={<Home />} >
<Route path='/' element={<About />} >
</Routes>
</HashRouter>
)
}
编程式导航—路由跳转
使用js编程的方式实现路由的跳转
语法使用:
1.导入useNavigate钩子函数
2.执行钩子函数得到跳转函数
3.执行跳转函数完成跳转
4.如果在跳转时不想加历史记录,可以添加额外参数replace为true
import {useNavigate} from 'react-router-dom'
const login = ()=>{
const navigate = useNavigate()
const getAbout = ()=>{
navigate('/about',{replace:true})
}
return (
<div>
login
<button onClick={getAbout}>切换到关于页</button>
</div>
)
}
两种传参数方式:
1.searchParams传参
// 传参
navigate('/about?id=101')
// 取参
let [params] = useSearchParams()
let id = params.get('id')
2.params传参
// 传参
navigate('/about/101')
// 取参
let params = useParams()
let id = params.id
function App(){
return (
<HashRouter>
<link to="/">首页</link>
<link to="/about">关于</link>
// 路由出口 路由对应的组件会在这里进行渲染 相当于vue中router-view
<Routes>
<Route path='/' element={<Home />} >
// 定义二级路由嵌套
<Route path='article' element={<Article />} > </Route>
<Route path='board' element={<Board />} > </Route>
</Route>
<Route path='/' element={<About />} ></Route>
</Routes>
</HashRouter>
)
}
// 在一级路由Home组件中定义出口
import {Outlet} from 'react-router-dom'
function Home(){
return (
<div>
Home
<Outlet/>
</div>
)
}
function App(){
return (
<HashRouter>
<link to="/">首页</link>
<link to="/about">关于</link>
// 路由出口 路由对应的组件会在这里进行渲染 相当于vue中router-view
<Routes>
<Route path='/' element={<Home />} >
// 定义二级路由嵌套
<Route index element={<Article />} > </Route>
<Route path='board' element={<Board />} > </Route>
</Route>
<Route path='/' element={<About />} ></Route>
</Routes>
</HashRouter>
)
}
<Routes>
<Route index element={<Article />} />
<Route path='board' element={<Board />}></Route>
// 当所有路径都没有匹配时渲染此路由
<Route path="*" element={<NotFound />}></Route>
<Routes>
1.安装mobx和mobx-react-lite
yarn add mobx mobx-react-lite
2 具体代码使用: 编写第一个mobx store小案例
import {makeAutoObservable} from 'mobx'
class CounterStore{
// 1.定义数据
count = 0
constructor(){
// 2.把数据弄成响应式
makeAutoObservable(this)
}
//3.定义action函数(修改数据)
addCount = ()=>{
this.count++
}
}
// 4.实例化
const counterStore = new CounterStore()
export { counterStore }
在App组件中使用mobx数据
import {counterStore} from ''
import {observer} from 'mobx-react-lite' //导入中间件连接mobx react完成响应式
function App(){
return (
<div>
// 渲染store中的count
{counterStore.count}
// 点击事件触发action函数修改count
<button onClick={counterStore.addcount}>+</button>
</div>
)
}
// 用observer包裹app
export default observer(App)
import {makeAutoObservable} from 'mobx'
class CounterStore{
// 1.定义数据
list = [1,2,3,4,5]
constructor(){
// 2.把数据弄成响应式
makeAutoObservable(this)
}
//3.定义计算属性
get filterList(){
return this.list.filter(item=>item>2) 计算item大于2的值
}
}
// 4.实例化
const counterStore = new CounterStore()
export { counterStore }
创建一个index.js文件合并所有子模块
// 1.导入子模块
import {CounterStore} from ''
import {ListStore} from ''
class RootStore {
constructor(){
// 2.对子模块进行实例化
this.counterStore = new CounterStore()
this.listStore = new ListStore()
}
}
// 实例化操作
// 使用react context机制 完成统一方法封装
const rootStore = new RootStore()
// useContext查找机制:优先从Provider value查找,如果找不到就找从createContext方法传递过来的默认参数
const context = React.CreateContext(rootStore)
const useStore = ()=> React.useContext(context)
export {useStore}
在组件中使用数据时
function App(){
const rootStore = useStore() // 或者使用解构
return (
<div>
{rootStore.counterStore.count}
</div>
)
}
1.初始化Mobx的过程是怎样的?
声明数据–》响应式处理–》定义action函数–》实例化导出
2.Mobx如何配合react,需要依赖什么包?
mobx-react-list作为连接react,导入observer方法,包裹组件(只能和函数组件配合);如果是类组件,使用mobx-react
3.模块化解决了什么问题?
维护性问题
4.如何实现mobx模块化?
按照功能拆分store模块,根模块组合子模块,利用context机制依赖注入(全局数据传递)
1.安装修改CRA配置的包:yarn add -D @craco/craco
2.在项目根目录中创建craco的配置文件:craco.config.js,并在配置文件中配置路径别名
// 添加自定义对于webpack的配置
const path = require('path')
module.exports = {
// webpack配置
webpack: {
// 配置别名
alias: {
// 约定使用‘@’表示src文件所有路径
'@': path.resolve(__dirname, 'src')
}
}
}
3.修改package.json中的脚本命令
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "craco eject"
},
4.在代码中,可以通过@来表示src目录下的相对路径
5.重启项目,让配置生效
能够让vscode识别@路径并给出提示
实现步骤:
1.创建jsconfig.json配置文件
2.代码实现
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": [
"src/*"
]
}
}
}
vscode会自动读取jsconfig.json中的配置,让vscode知道@就是src目录