1.语法上的区别: 类组件需要继承自Component,内部必须有render方法,必须return一个react元素, 类组件在语法上比函数式组件复杂.
2.类组件需要实例化然后才能通过this关键字操作组件本身. 函数式组件无需实例化. 函数式的渲染只需要调用函数即可, 执行效率较高.
3.类组件有自己的状态, 函数式组件没有自己的状态( 我们也讲这样的组件称为"无状态组件" ).
4.类组件有自己的生命周期, 函数式组件没有自己的生命周期.
5.类组件会实例化,效率低,有this,函数式组件没有this
//通过Hooks可以给函数式组件引入组件状态.
//通过Hooks可以给函数式组件引入生命周期.
//所谓的Hooks就是一系列的函数( api ), Hook叫做钩子函数, 所有钩子函数都是以use开头的
函数式组件不能使用 state:函数式组件比类组件更简洁好用,而 Hooks 让它更加丰
富强大;
副作用问题:诸如数据获取、订阅、定时执行任务、手动修改 ReactDOM 这些行为
都可以称为副作用;而 Hooks 的出现可以使用 useEffect 来处理这些副作用;
有状态的逻辑重用组件
复杂的状态管理:之前通常使用 redux、dva、mobx 这些第三方状态管理器来管理
复杂的状态,而 Hooks 可以使用 useReducer、useContext 配合实现复杂的状态管理;
开发效率和质量问题:函数式组件比类组件简洁,效率高,性能也好。
useState 用来管理组件的状态, 当下主要用来给函数式组件添加状态.
通过多次调用 useState(),一个函数组件可以拥有多个状态。
//useState 用来给函数式组件引入组件状态, 返回值是个数组, 数组的第一个元素是组件状态, 第二个元素是用来更新组件状态的函数, 调用该函数可以让当前组件更新
//useState 可以多次调用,给组件引入多个组件状态.
var [ count, setCount ] = useState(0);//setCount函数的参数是count变量的最新值
var [ str, setStr ] = useState('');//setStr函数的参数是str变量的最新值
useEffect 用来给函数式组件添加生命周期方法。
函数组件中没有生命周期,那么可以使用 useEffect 来替代。如果你熟悉 React class
的生命周期函数,你可以把 useEffect Hook 看做(类似于)
componentDidMount,
componentDidUpdate 和
componentWillUnmount 这三个函数的组合.
import './App.css';
import { useState, useEffect } from 'react';
function App() {
//useState 给组件添加状态
var [count, setcount] = useState(99)
// console.log(count);
//useEffect相当于给组件添加生命周期的
useEffect( //1.这种相当于componentDidMount和componentDidUpdate //挂载执行一次并且只要组件更新就会执行
() => {
console.log('组件更新了','wu');
},
)
useEffect( //2.这种相当于componentDidMount 只在初始挂载时执行
() => {
console.log('组件更新了','[]');
所以可以在这里发请求,创建定时器,事件监听
}, []
)
useEffect( //3.这种带return的这种相当于componentDidMount
() => {
console.log('组件挂载了');
// 可以在这里发请求,创建定时器,事件监听
var timerid = setInterval(() => {
console.log('定时器执行了');
}, 5000)
//相当于类组件的 componentWillUnmount
return () => {
//可以在这里取消请求,清除定时器,取消监听,进而避免内存泄漏
clearInterval(timerid)
}
}, []
)
useEffect( //4.这种可以监听[]中的数据变化,然后执行回调函数,count变化时执行
() => {
console.log('count变了',count); //这里可以拿到监听数据更新后的最新值
},[count]
)
var Change = () => {
setcount(count + 1)
console.log(count);//这里获取不到最新的count值,因为setcount是异步的
}
return (
{count}
);
}
export default App;
创建 ref,方便访问操作 DOM 节点,无法获取子组件实例(undefined)
import { useEffect, useRef, useState } from 'react';
import './App.css';
import Child from './components/Child'
function App() {
//使用useRef定义一个变量
var titleRef = useRef();
var childRef = useRef();
//只能在事件处理函数中,组件挂载完成的生命周期函数中获取dom节点
useEffect(()=>{
console.log( titleRef.current );
},[])
var handleClick = ()=>{
console.log( titleRef.current );//获取标签的dom节点
console.log( childRef.current );//获取子组件实例( 获取不到, 因为函数式组件不会实例化, 没有实例 )
}
return (
{/* 将useRef定义的变量绑定在标签的ref属性值的位置 */}
);
}
export default App;
只在顶层调用 Hooks : Hooks 的调用尽量只在顶层作用域
不要在循环、条件或嵌套函数中调用 Hook,否则可能会无法确保每次组件渲染时都
以相同的顺序调用 Hook
只在函数组件调用 Hooks:目前只支持函数式组件,未来版本 Hooks 会扩展到 class
类组件;
React Hooks 的应用场景:函数式组件、自定义 Hooks
用来解决同一个父组件的后代组件之间的数据共享问题, (跨组件通信) 同一个父组件的所有后代
组件都可以用 useContext() 从最近的 context 中获取.(函数式组件没有this所以要用这个钩子)
通信对象:
外层组件:
函数式组件内层组件想接收:
类组件里内层是这样接收的
性能优化,利用了闭包的特性,会返回一个记忆值, 通过记忆值来避免在每个渲染上都执行高开销的计算(计算缓存);适用于复杂的计算场景,
如复杂的列表渲染,对象深拷贝...
useMemo() 接收 2 个参数:一个计算函数 和 依赖数组。
计算函数: 返回一个函数
依赖数组: 放置依赖的数据
//useMemo的返回值是一个记忆值( 计算得到的值 ), useMemo会监听[]中值的变化
//组件更新时, 执行到useMemo函数的位置, 如果监听的值没有发生改变,
则会跳过回调函数的执行,直接返回上次计算的记忆值.
//组件更新时, 执行到useMemo函数的位置, 如果监听的值改变了,
则重新执行回调函数,计算得到一个新的记忆值并返回.
import { useEffect, useMemo, useRef, useState } from 'react';
import './App.css';
function App() {
var [num,setNum] = useState(0)
var [count,setCount] = useState(0)
var handleChange = (e)=>{
setNum(e.target.value);
}
var handleClick = ()=>{
setCount(count+1);
}
// var calc = ()=>{
// console.log('calc');
// var sum = 0;
// //计算从1到num 每个整数的和
// for(var i = 1;i <= num;i++){
// sum += i;
// }
// return sum;
// }
//useMemo的返回值是一个记忆值( 计算得到的值 ), useMemo会监听[]中值的变化
//组件更新时, 执行到useMemo函数的位置, 如果监听的值没有发生改变,则会跳过回调函数的执行,直接返回上次计算的记忆值.
//组件更新时, 执行到useMemo函数的位置, 如果监听的值发生改变,则重新执行回调函数,计算得到一个新的记忆值并返回.
var calc = useMemo(()=>{
console.log('calc');
var sum = 0;
//计算从1到num 每个整数的和
for(var i = 1;i <= num;i++){
sum += i;
}
return sum;
},[num])
return (
);
}
export default App;
useCallback(callback, array):也是用于性能优化,与 useMemo()不同的是,返回值是
callback 本身;
//useCallback 的返回值是一个记忆的函数( 就是他的第1个参数 ), useCallback会监听[]中值的变化
//组件更新时, 执行到useCallback函数的位置, 如果监听的值没有发生改变,
则会忽略第一个参数,直接返回上次生成的回调函数.
//组件更新时, 执行到useCallback函数的位置, 如果监听的值改变,
则重新生成一个新的回调函数,并返回该函数.
//注意: useCallback 需要配合子组件的memo高阶组件一起使用, 才能起到控制子组件更新的作用( 避免子组件的无效更新, 实现性能优化 ).
父组件:使用useCallback 配合子组件memo
父组件更新时传入子组件props变化时子组件再更新不然不更新
子组件 用memo配合父组件
路由相关的hooks:
import {useNavigate, useLocation, useParams, useSearchParams } from "react-router-dom"
配合函数式组件配置路由(负责动态生成整个应用的路由规则)
调用useNavigate得到一个用来跳路由的api(编程式导航:通过执行js代码跳转路由)
(之前类组件配合使用的是v5路由)
1.安装
npm i react-router-dom –save
路由管理组件
react-router-dom包含多种路由管理组件: BrowserRouter , HashRouter , MemoryRouter , NativeRouter , StaticRouter ;
打开入口文件,导入路由管理组件,包裹根组件
(先导入相关组件)
v6中 Switch 换成了 Routes
v6中 Redirect 组件没有了,重定向用 Navigate组件
v6中 路由规则无需添加exact精确匹配
v6中 嵌套路由规则是直接写在指定父路由规则内部
图示包含嵌套路由(二级路由)
二级路由对应得组件渲染位置需要写
①先在router文件里配置路由表
//配置路由表
export default [
{
path:'/index',
element:
children:[ //嵌套路由
{
path:'/index/home',
element:
},
{
path:'/index/cate',
element:
},
{
path:'/index/mine',
element:
},
{
path:'/index',
element:
},
{
path:'*',
element:
},
]
},
{
path:'/login',
element:
},
{
path:'/register',
element:
},
{
// path:'/detail/:id',
path:'/detail',
element:
},
{
path:'/',
element:
},
{
path:'*',
element:
},
]
②然后再导出路由表,在路由规则处用useRoutes如下:
import routes from '../router/routes'
{
useRoutes(routes)
}
或者
把这段代码写成一个组件如下:
在再路由规则处写:
组件渲染位置要用
{/* OutLet是个占位组件( 动态组件 ), 类似于vue-router中的router-view */}
useNavigate
import { useNavigate} from 'react-router-dom'
var navigate = useNavigate()
// navigate('/index/cate');
// navigate({ pathname:'/index/cate' });
// navigate(-1);
动态路由: 路由地址中带有 :xx 这样的路由地址就是动态路由. ( 刷新页面, 参数不消失,参数会在地址栏显示)
定义动态路由
跳路由
详情
或者
navigate({ pathname:'/detail/'+88888 });
navigate( "/detail/8888" )
获取路由参数
组件内, 通过 useParams() 接收传递过来的参数
var params=useParams()
console.log(params);
var { id } = useParams()
console.log(id);
定义路由
跳路由
详情
或者
navigate( "/detail?id=8888" )
navigate( {pathname:'/detail' , search : 'id=8888&name=abc'} );
获取路由参数
组件内, 通过 useSearchParams( ) 接收传递过来的参数
var [ params, setParams ] = useSearchParams( )
console.log(params.getall())
console.log(params.get('id'));
console.log( params.get('name') );
定义路由
跳路由
XX
获取路由参数
组件内, 通过 useLocation() 接收传递过来的参数
var location = useLocation( )
console.log(location.state);
state传参, 刷新页面后参数不消失,state传的参数是加密的
示例: