写在前面:
1.本文是个人课上学习内容的总结和梳理,主要知识点来自于“开课吧”线上直播课程,以及 React 官方文档。
2.目前尚处于 React 乃至前端入门阶段,因此很多内容理解的不是很透彻,文章更多是用来学习记录而非干货分享。
因此,建议如果需要解决项目问题,还是去看一下其他大佬的文档以及 React 官方文档(一手资料)
函数式组件
定义和声明一个函数是组件十分简单,只需要正常的声明并导出一个JavaScript函数即可,并且,声明函数式组件时,函数可以接受props参数,也就是父级组件传入的props:
// 父级组件 App.js
import { Component } from "react";
import Child from "./child";
class App extends Component {
state = {
count: 1
}
setCount = (count)=>{
this.setState({
count
});
}
render(){
const {count} = this.state;
return <div>
<Child count = {count} setCount = {this.setCount} />
</div>
}
}
export default App;
// 子组件-函数式组件 Child.js
export default function Child(props) {
const {count,setCount} = props;
return <div>
<div>{count}</div>
<button onClick={()=>{
setCount(count + 1);
}}>递增</button>
</div>
}
在函数式组件中使用hooks
要知道state和setState都是component类的属性和方法,所以不能在函数式组件中直接使用state和setState。因此在React 16.8 之后新增了一个新特性:Hook。Hook可让我们在不编写class的情况下使用state和其他React特性:
- 基础 Hook
- useState
- useEffect
- useContext
- 额外的 Hook
- useReducer
- useCallback
- useMemo
- useRef
- useImperativeHandle
- useLayoutEffect
- useDebugValue
要注意的一点:==hooks必须在函数组件的最外层中调用,不可以嵌套在for循环、if中,也不可以在嵌套的子函数中调用==,在这篇笔记里,简介一下课上学到的三个Hooks(上方已经标注了出来)
useState
const [state, setState] = useState(initialState):useState用来返回一个state状态和修改和更新该state状态的函数。在渲染初始阶段,返回的state状态与传入的第一个参数值initialState相同。
这个setState函数用来更新state状态,他接受一个新的state值或者一个回调函数并将组件进行重新的更新和渲染
function App() {
const [count,setCount] = useState(1);
const [name,setName] = useState("");
return <div>
<p>{count}</p>
<button onClick={()=>{ setCount(count * 2); }}>递增</button>
<button onClick={()=>{ setCount(1); }}>重置</button>
<br />
<input type="text" value={name} onChange={({target})=>{ setName(target.value); }} />
<p>{name}</p>
</div>
}
useEffect
useEffect用来替代生命周期函数,通过如下方式声明:
useEffect(function{
---> effect 函数
return ()=>{
---> 返还函数
}
},[依赖参数])
useEffect是用来处理各类副作用的:componentDidMount、componentDidUpdate和 componentWillUnmount,执行顺序如下:
componentDidMount组件挂载阶段:
-
执行
useEffect将effect 函数存入队列 -
挂载完成之后,执行
effect 函数队列,并获取返回函数存入队列
componentDidUpdate组件更新阶段:
-
执行
useEffect将effect 函数存入队列 -
更新完成之后,先将返回函数队列执行: 在执行时会观察该
effect是否有依赖参数,无依赖数据,直接执行,有依赖则追踪依赖是否改变,改变才执行,不变则不执行 -
执行新的
effect函数存入队列: 在执行时会观察该effect是否有依赖参数,无依赖数据,直接执行,有依赖则追踪依赖是否改变,改变才执行,不变则不执行
componentWillUnmount卸载阶段:
- 将返回函数队列执行
// 挂载时 和 更新时 都执行
export default function Child(){
const [count,setCount] = useState(0);
const [name,setName] = useState("a");
const isMount = useRef(true);
// 组件挂载时和count有变化时执行
useEffect(()=>{
console.log("请求数据","组件挂载时和count有变化时执行")
},[count]);
// 只在组件挂载阶段执行
useEffect(()=>{
console.log("只在挂载阶段执行");
return ()=>{ // 只在组件卸载的时候
console.log("组件的即将卸载");
}
},[]);
// 当组件更新时
useEffect(()=>{
if(!isMount.current){
console.log("组件更新")
} else {
isMount.current = false;
}
})
return <div>
<p>{count}</p>
<button onClick={()=>{
setCount(count + 1);
}}>递增</button>
<p>{name}</p>
<button onClick={()=>{
setName(name + 1);
}}>递增</button>
</div>
}
useRef
useRef类似reactRef返回一个可变的ref对象,其.current属性被初始化为传入的参数initialValue。
- 可以获取 DOM 和 组件 的实例
- 当ref中保存的是数据的时候,可以进行跨更新阶段的数据传递,因为这个数据不会随着组件的更新自动更新了。
export default function Child(){
const [count,setCount] = useState(0);
const [name,setName] = useState("a");
const wrap = useRef();
const prevCount = useRef(count); // 当 Ref 中保存的是数据时,数据并不会随着组件的更新自动更新
useEffect(()=>{
console.log(prevCount.current);
prevCount.current = count;
})
return <div ref={wrap}>
<p>{count}</p>
<button onClick={()=>{
setCount(count + 1);
}}>递增</button>
<p>{name}</p>
<button onClick={()=>{
setName(name + 1);
}}>递增</button>
</div>
}
在函数式组件中使用router
路由:根据不同的url规则,给用户展示不同的视图(页面) 当应用变得复杂的时候,就需要分块的进行处理和展示,传统模式下,我们是把整个应用分成了多个页面,然后通过 URL 进行连接。但是这种方式也有一些问题,每次切换页面都需要重新发送所有请求和渲染整个页面,不止性能上会有影响,同时也会导致整个 JavaScript 重新执行,丢失状态。因此就催生了SPA——单页面应用,整个应用只加载一个页面(入口页面),后续在与用户的交互过程中,通过 DOM 操作在这个单页上动态生成结构和内容。
优点:
- 更好的用户体验,减少了请求和渲染时产生的页面跳转等待时间和空白页面,页面切换快
- 重前端,数据和页面内容由异步请求(ajax)+DOM操作完成,前端处理更多的业务逻辑
缺点:
- 首次进入处理慢
- 不利于SEO
前端路由
前端路由只是改变了 URL 或 URL 中的某一部分,但一定不会直接发送请求,可以认为仅仅只是改变了浏览器地址栏上的 URL 而已,JavaScript 通过各种手段处理这种 URL 的变化,然后通过 DOM 操作动态的改变当前页面的结构。
React Router提供了多种不同环境下的路由库,基于web环境下的为:react-router-dom
安装
npm i -S react-router-dom
选择路由模式的组件
该路由库提供了两类选择路由模式的组件:
BrowserRouter组件 ==> history模式 ==> 继续 HTML5 History API 的路由组件HashRouter组件 ==> hash模式 ==> 基于 URL Hash 的路由组件
用哪种类型的组件,index.js中就在最外层用它把真实DOM组件包裹起来:
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter> ,
document.getElementById('root')
);
Route 组件
通过该组件来设置应用单个路由信息,Route 组件所在的区域就是就是当 URL 与当前 Route 设置的 path 属性匹配的时候,后面 component 将要显示的区域。
即设置url匹配规则,以及组件的渲染出口。
<Route path={["/","/home","/index"]} component={IndexPage} />
url匹配模式有多种,如果不加以设置,则默认为模糊匹配:
- 默认:默认情况 path 是 模糊匹配:url 以 path 为开始时 则匹配成功
- exact: 精确匹配: url 为 path 或 path/ 时匹配
- strict 严格匹配: url === path 可以匹配,设置严格匹配时必须先设置 exact
- 多路径匹配
<Route path={["/","/home","/index"]} exact strict component={IndexPage} />
<Route path="/about" exact component={AboutPage} />
<Route path="/about/details" component={AboutDetailsPage} />
Link 组件
Link 组件用来处理 a 标签链接类似的功能(它会在页面中生成一个 a 标签),但设置这里需要注意的,react-router-dom 拦截了实际 a 标签的默认动作,然后根据所有使用的路由模式(Hash 或者 HTML5)来进行处理,改变了 URL,但不会发生请求,同时根据 Route 中的设置把对应的组件显示在指定的位置。因此设置单页面路由要是用Link标签,设置外链跳转时要用a标签
import { Link } from "react-router-dom";
function Nav() {
return <nav>
<Link to="/">首页</Link>
<span> | </span>
<Link to="/about">关于</Link>
<span> | </span>
<Link to="/about/details">关于-详情</Link>
<span> | </span>
<a href="https://wwww.baidu.com">百度</a>
</nav>
}
export default Nav;