看完这篇文章你的React就可以上简历了
作者的前言
- 本文章记录的为React基础虽然React更新很块但是理论上所有版本都是可用的请放心阅读
- 文章书写所使用的React版本:18.2,由于讲的比较基础不必担心出错和bug
- 管于React路由在这篇文章里面改就没有讲的很详细(主要是更新频繁,而且版本之间的差异大,所以以后会单独出篇文章来讲)
- 全文总计24000字(代码片约占30%,正文约占70%)预计阅读时间约为75分钟,代码已经经过详细注释,文章带实战demo,推荐收藏方便反复阅读
关于React
React是一个用于构建用户界面的JS库
(本质是一个MVVM框架)——不涉及HTML和CSS
中文官网:React中文官方文档
- 是FaceBook下的一个开源前端框架
- 这个网站上的react是最新的,最早的版本可以追溯到2013年,目前最新的版本为
18.2
React是一个用于构建用户界面
的JavaScript库(官方谦虚)
React的部分的库对比Vue
对比Vue的Vuex,Vuerouter,Vue-UI
React的特点
- 声明式:同为MVVM框架React使用创建交互式和Vue一样都是使用的虚拟DOM,当数据变动时就会刷新DOM(数据响应式)
- 组件化编程 :
- 兼容性好:
- Vue可创建的项目:
网站,H5,WebApp,混合式APP
- React可创建的项目:
网站,H5,WebApp,原生App(JS=>JAVA/OC)
- Vue可创建的项目:
使用React的方式
以脚本方式导入
具体的CSN地址如下:
// 编译
https://unpkg.com/[email protected]/babel.min.js
// react-dom
https://unpkg.com/react-dom@18/umd/react-dom.development.js
// react本体
https://unpkg.com/react@18/umd/react.development.js
以及排序好的导入script标签
如下
// 注意导入顺序小心被开dddd
<!-- 为页面提供虚拟的DOM元素 用于创建虚拟DOM-->
<script src="./js/react.development.js"></script>
<!-- 为页面提供ReactDOM对象用于把虚拟的DOM元素和真实DOM元素映射起来 -->
<script src="./js/react-dom.development.js"></script>
注意导入顺序
要不然会报错,例如:
Uncaught TypeError: Cannot read properties of undefined(reading'__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED')
补充链接
React官方的文档介绍:CDN 导入react的方式
React和原生DOM的对比
实现demo需求
我需要在#app中添加一个按钮效果如下
原生实现需求
- 创建新的DOM元素button
<script> let button = document.createElement("button"); // 添加title悬浮显示 button.title = "点击我一下试试"; // 添加className button.className = "btn btn-primary"; // 添加内容 button.innerHTML = "我是一个小按钮"; </script>
- 查找父容器DOM元素:div
<script> // - 查找父容器DOM元素:div let app = document.getElementById("app"); </script>
- 在父元素中添加子元素
<script> // - 在父元素中添加子元素 app.appendChild(button); </script>
原生实现需求的缺陷
dom标准中提供的DOM对象提供的元素都是重量级元素,如果每次页面变化都要操作页面的话就显得过于繁琐且会对页面的性能有影响(参考下图,DOM操作对象过于臃肿,会占用大量的RAM),其实我们打印一下app和button就会发现,上面我们只添加了几个属性但是他们的DOM对象却十分臃肿
console.dir(app);
解决方案:虚拟DOM
众所周知原生DOM过于臃肿,会大量消耗性能,所以虚拟DOM就营运而生
虚拟DOM和原生DOM对比
- 原生DOM:
DOM对象臃肿大量消耗空间
(创建DOM元素之后不管你用不用都会创建所有的DOM属性默认赋值为Null) - 虚拟DOM:
简化DOM
(简单说就是你写了多少DOM属性他对应的对象里面就有几个)
示例:React的虚拟DOM
React实现需求
正常实现需求
- 创建虚拟的button
<script> // - 创建虚拟的button // 1:标签名 2:属性 let vbutton = React.createElement( "button", // 标签名 { title: "我是React家的按钮", className: "button" }, // 对象:用于存储标签属性 "React button" // 内容:innerHTML ); </script>
- 找到父容器DOM元素:div转换为虚拟DOM容器
<script> // 创建虚拟的vapp容器(创建一颗虚拟的DOM树的根元素让其与app元素对应起来) let vapp = ReactDOM.createRoot(app); // 可以直接写id属性也可以getElementById获取之后再来获取 </script>
- 在虚拟DOM父元素中添加子标签
<script> // - 在虚拟DOM父元素中添加子标签 //渲染上子元素 render:渲染(React会经常用) vapp.render(vbutton); </script>
简化实现需求
操作虚拟DOM的消耗远低于真实DOM但是操作过于繁琐,所以我们来尝试简化一下
首先:我们来直接书写一下代码试试
let vbutton = (
<button
title="1"
class="button"
>
React button
</button>
);
结果
我们可以看到它报错了,但是别灰心,这个方法是可用的但是首先我们先要复习一下TypeScarip的JSX了
点击上面的链接即可看到关于JSX的简介了这里我就不做过多的概述了
JSX(Javascript XML)
Javascript XML : 使用XML的语法创建HTML元素,本身属于TypeScarip中的一部分
提示:由于浏览器不支持TS,也就不支持JSX语法所以需要TS编译器转换为JS代码—— BaBal
接下来我们就来试一试用babel这个编译器
首先打开babel在线编译器我们来编译一下react代码
- 如下图打开链接之后为下图所示
- 接下来我们来简单的写个标签试试
粘贴过去之后就会发现右边生成了代码<button title="我是React的button标签" class="react-button">React button</button>
现在再将其放到react代码中试试let vbutton = /*#__PURE__*/ React.createElement( "button",{ title: "\u6211\u662FReact\u7684button\u6807\u7B7E", className: "react-button", },"React button");
- 结果
我们可以看到成功的显示了出来
React/JSX语法规范
语法规范 [ 重点 ]
- JSX标签/组件名严格区分大小写
- HTML标签对应的组件必须采用大驼峰命名法
- 自定义组件名必须采用大驼峰命名法
- 一段JSX中只能有一个根元素
- 一段JSX可以书写在多行中,最外层可以用
()
括起来表示这是一整端JSX - JSX中的HTML标签必须闭合 ;例如
<hr>
可以写为<hr></hr>
- JSX中的标签属性不等同于HTML;如
class
属性在JSX中要为className
- JSX中可以使用
{ }
来进行数据绑定(内容绑定
,样式绑定
,属性绑定
,事件绑定
...
) - 在JSX中没有属性绑定的概念(类似于Vue的
v-model
,v-bind
)统一为{}
- 设置元素绑定可以定义一个对象然后再去绑定也可以直接把对象写在打括号里
- 事件绑定:
在Vue里面叫v-on/@click
在React中拥有一些特殊的API其他的基本和写原生无异只要更改事件名
即可 - 事件绑定传参:众所周知在原生中如果需要传参的话需要添加小括号,但是在React中添加小括号会立即执行所以就会造成点击无效的时候,所以解决方案就是使用
回调函数
和函数
的方式来解决
demo示例
经过上面的说明估计大家已经了解了JSX,React的基本操作和逻辑了,接下来我们来做几个小demo实验一下上面的规范
- 制作一些方法和声明函数
// 创建一个公共值 let num = 20; // 设置元素绑定可以定义一个对象 var style = { color: "red", backgroundColor: "#000000", }; // style={style} // 也可以写为 // style={ { // color: "red", // backgroundColor: "#000000", // }} // 定义事件 // 两种写法都是可行的 var numAdd = function () { num++; console.log(num); }; function numCount() { num++; console.log(num); }
- 创建虚拟的DOM元素
// - 创建虚拟的DOM元素 // 一段JSX中只能有一个根元素 // 在react中没有属性绑定的概念(v-model,v-bind) // 加上双引号就代表为字符串所以不能写 let buyCount = ( <div> <button onClick={ numAdd}>-</button> <span title={ num} style={ style} > { num} </span> <button onClick={ numCount}>+</button> </div> );
- 把真实的DOM父容器转换为虚拟的DOM容器
// - 把真实的DOM父容器转换为虚拟的DOM容器 let vapp = ReactDOM.createRoot(app);
- 在虚拟的DOM父容器里面渲染子元素
// - 在虚拟的DOM父容器里面渲染子元素 vapp.render(buyCount);
React中的自定义组件
早期的React设计之初是为了照顾写C和Java的程序员所以React中提供了两种自定义组件的语法
function语法
- 自定义组件语法1:函数式组件
// 自定义组件语法1:函数式组件 function Rating() { // 这是JSX return <div>❤❤❤❤❤❤</div>; }
- 创建虚拟dom
// 1,创建虚拟dom // Rating标签相当于span标签 let Rat = <Rating></Rating>;
- 把真实DOM容器转换为虚拟的DOM容器
// 2,把真实DOM容器转换为虚拟的DOM容器 let vapp = ReactDOM.createRoot(app);
- 在虚拟DOM中渲染虚拟的DOM元素
// 3,在虚拟DOM中渲染虚拟的DOM元素 vapp.render(Rat);
- 结果
- 为什么会显示
React 自定义组件传参
** 还是上面的列表**
<script type="text/babel">
// 自定义组件语法1:函数式组件
function Rating() {
// 这是JSX
return <div>❤❤❤❤❤❤</div>;
}
// 1,创建虚拟dom
// Rating标签相当于span标签
let Rat = (
<Rating
color="red"
count={
9}
/>
);
// 2,把真实DOM容器转换为虚拟的DOM容器
let vapp = ReactDOM.createRoot(app);
// 3,在虚拟DOM中渲染虚拟的DOM元素
vapp.render(Rat);
</script>
父组件传递给子组件,其实只要象Vue一样直接写上即可
子组件接受参数
而子组件接收参数只需要在回调函数中写一个形参
// 自定义组件语法1:函数式组件
function Rating(suibian) {
// 这是JSX
console.log(suibian);
return <div>❤❤❤❤❤❤</div>;
}
// 1,创建虚拟dom
// Rating标签相当于span标签
let Rat = (
<Rating
color="red"
count={
9}
/>
);
// 2,把真实DOM容器转换为虚拟的DOM容器
let vapp = ReactDOM.createRoot(app);
// 3,在虚拟DOM中渲染虚拟的DOM元素
vapp.render(Rat);
我们就可以看到
但是我如果没传参过去也写一个随便
呢?
<script type="text/babel">
// 自定义组件语法1:函数式组件
function Rating(suibian) {
// 这是JSX
console.log(suibian);
return <div>❤❤❤❤❤❤</div>;
}
// 1,创建虚拟dom
// Rating标签相当于span标签
let Rat = <Rating />;
// 2,把真实DOM容器转换为虚拟的DOM容器
let vapp = ReactDOM.createRoot(app);
// 3,在虚拟DOM中渲染虚拟的DOM元素
vapp.render(Rat);
</script>
答案是:空对象
接下来我们来完善一下(不想多啰嗦了,将就看注释吧)
<script type="text/babel">
// 自定义组件语法1:函数式组件
function Rating(suibian) {
// 这是JSX
console.log(suibian);
// suibian: 形参值为传入的参数
let hearts = "";
// 书写代码
for (let i = 0; i < suibian.count; i++) {
hearts += "❤";
}
// 返回代码
return <div style={
{
color: suibian.color }}>{
hearts}</div>;
}
// 1,创建虚拟dom
// Rating标签相当于span标签
let Rat = (
<Rating
color="red"
count={
20}
/>
);
// 2,把真实DOM容器转换为虚拟的DOM容器
let vapp = ReactDOM.createRoot(app);
// 3,在虚拟DOM中渲染虚拟的DOM元素
vapp.render(Rat);
</script>
效果如下
React 自定义组件总结
- 创建组件函数名就是组件名首字母必须大写
- 使用组件可以使用但标签也可以使用双标签(但标签要闭口)
React声明自定义组件的class语法
class式组件 —— 体现着面向对象的编程思想(90年代:Java语言)
//创建组件
class Rating extends React.Component {
render( ){
return JSX //通过this.props读取当前组件获得的属性
}
}
//使用组件
let e = <Rating 属性名=值/>
React脚手架(create-react-app)
react脚手架:类似于Vue Cli
官方提供的一个工具叫create-react-app
,可用于创建空白项目
安装脚手架的前提条件
首先查看你的node版本是否大于12.0
node -v
我的是16.16(如果是开发鸿蒙的话我需要卸载装个14.2左右的版本,这个在这里我们暂时不说后面会出鸿蒙的文章【暗示关注】)
确定你的npm仓库地址是否为国内镜像源否则下载可能失败(项目包约为200MB左右,国外不稳定断线只能重新下载)
npm config get registry
镜像源地址对照表
比如说我的(如下图就是美国默认源,为了一张截图重装了一次node)
设置为国内的npm仓库
npm config set registry https://registry.npmmirror.com/
create-react-app(React脚手架)的安装
官方文档:本地React开发环境的搭建
- 下载工具
npx create-react-app 项目名
NPX:Node Package Executor Node包执行器,是Node.js官方提供的一个Node.js包执行工具
下面是执行安装命令后的截图
- 运行工具
cd 项目名
- 运行开发服务器
npm start
安装完成之后就是
create-react-app(React脚手架)的介绍
React脚手架中文件夹和文件用途介绍
React页面的书写和方式
组件的使用和书写方式
关于this指向丢失
-
class中的方法只有事件处理函数是才可能出现this指向丢失
-
原因:
严格模式下this指向undefind
-
JS中的
function(){}
里面的this指向该函数的调用者(而看不到函数声明在哪里)——this不指向组件对象,JSX中的所有元素都是虚拟的DOM元素,不能作为HTML事件源对象
——this不指向HTML元素
。JSX中的事件处理函数会被编译为“严格模式”下的全局函数
——其中this指向undefinded
-
解决方案1:箭头函数——事件处理函数声明
设计为箭头函数
f1 = () => { // this指向上级 }; <button onClick={ () => { this.f1(); }}>-</button>
此方法不能写小括号(不能传递实参)
-
解决方案2:箭头函数——为事件指定一个箭头函数,函数执行是调用目标函数
f1 = (实参) => { // this指向上级 }; <button onClick={ 实参 => this.f1(实参);>-</button>
此方法可以传递实参
-
解决方案3:利用JS函数中的bind方法,用于返回一个自己的副本,唯一区别就是其中的this指向指定的对象
demo:【JavaScript高级】bind,call和apply的案例
参考文章:【JavaScript】 bind,call和apply解构和class构造函数f1 = (形参) => { // this指向上级 }; <button onClisk={ this.f1.bind(this)}></button> // bind:绑定/固定 将函数中的this指向固定指向特定对象
-
解决方案4:利用class构造函数中的constructor
参考文章:【JavaScript】 ES6 解构和class构造函数
官方文档:文档-给组件添加交互功能
这个方式其实也就是官方文档所用的方式可以去官方文档查看详细,日常使用中方案一和方案二中箭头函数
的方式就足够了
完善购物车计数模块的开发
state属性的使用与设置
这个属性相当于Vue
中.vue
页面文件中的 vdata属性
声明属性
state = {
uname: "某琨用过的篮球",
count: 10,
};
使用属性值
this.state.xxx
:找到属性值
所以我们就可以写为
<div>{this.state.count}</div>
同理我们也可以通过这种方式来操作它的值
修改State的值并加载到页面是
结合上面我们既然能获取的话那应该可以修改吧,我们来试试
numAdd = () => {
// 添加
this.state.count++;
console.log(this.state.count);
};
render() {
return (
<React.Fragment>
<div>{
this.state.count}</div>
<button onClick={
this.numAdd}>+</button>
</React.Fragment>
);
}
但是运行之后我们发现虽然打印了但是液面上是没有显示的这是为什么呢
其实区别就在下面
Vue和React属性刷新的区别[重点]
- Vue.js的数据响应式时
Push Based
,而React中的数据响应式是Pull Base
- Push Based :当修改数据之后就会通知渲染系统自动刷新
- Pull Based :当修改数据之后不会自动渲染页面所以需要去执行渲染
this.setState()
实现页面上数据的刷新
通过上面的说明,我们就会发现React中state中的数据的更新模式所以代码就可以这样写,注意注释!
import React, {
Component } from "react";
// 创建页面
export default class App extends Component {
// 声明属性
state = {
uname: "某琨用过的篮球",
count: 10,
};
numAdd = () => {
// 添加
// this.state.count++;
// console.log(this.state.count);
// 这样直接修改变量是不会去刷新而渲染的
this.setState({
count: this.state.count + 1,
});
};
numCount = () => {
// 减少
this.setState({
count: this.state.count - 1,
});
};
render() {
return (
<React.Fragment>
<button onClick={
this.numCount}>-</button>
<div>{
this.state.count}</div>
<button onClick={
this.numAdd}>+</button>
</React.Fragment>
);
}
}
我们发现现在是可以用的了
函数式组件
接下来我们先来写一个父子组件
生命周期钩子函数
react中拥有7个钩子函数
挂载阶段
- constructor:组件被创建了(相当于Vue中的creatactive)
- render:组件渲染内容
- componentDidMount:组件完成挂在(相当于Vue的Mounted)
更新阶段
- shouldComponentUpdate:此次属性更新或者状态更新的修改需要更新视图嘛?如果返回一个true就会去渲染
- reder:组件渲染
- componentDidUpdate:如果是允许渲染的话,就会在执行完之后调用它,反之则不会执行
卸载阶段
- componentWillUnmount:组件即将卸载
- 在这个阶段就可以开始关闭首页轮播图定时器啊websockio这种服务了
- 功能类似于Vue中的beforeDestroy
实践demo
新建一个组件然后倒入后粘入如下代码
import React, {
Component } from "react";
export default class Child extends Component {
constructor() {
super();
console.log("1-constructor当页面被加载了");
}
componentDidMount() {
console.log("3-componentDidMount组件被挂载");
}
// 应该让组件更新嘛?
shouldComponentUpdate(newProps, newState) {
// 如果想要获取到新的数据就要写上下面这两个参数
console.log(newProps, newState);
// 其实可以在这个上面添加一个if语句来判断一下
// console.log(this.state.num % 2);
if (this.state.num % 2) {
console.log("4-shouldComponentUpdate:应该让组件更新嘛?");
console.log("是的我希望组件更新");
return true;
} else {
console.log("4-shouldComponentUpdate:应该让组件更新嘛?");
console.log("我不希望更新组件");
return false;
}
}
// 完成更新之后
// 如果不更新的话下面的钩子函数将不会执行
componentDidUpdate() {
console.log("6-完成了更新");
}
componentWillUnmount() {
console.log("7-组件即将卸载");
}
// 组件数据
state = {
num: 1,
};
// 组件方法
f1 = () => {
this.setState({
num: this.state.num + 1 });
};
// 开始渲染内容
render() {
console.log("2-render渲染内容");
return (
<div
style={
{
backgroundColor: "red", userSelect: "none", margin: "20px" }}>
<h3>我是子组件哦</h3>
<p>计数器:{
this.state.num}</p>
<button onClick={
this.f1}>点击计数器</button>
</div>
);
}
}
函数式组件和钩子
React中定义的组件分为:function和class式组件
早期大家更偏向于使用class组件因为function组件缺乏状态,生命周期方法
从React 16.8开始官方为function式组件提供了新的功能——Hook,专门用于为function组件”钩住“更多的功能,例如:状态生命周期的方法…同时由于函数式组件本身的简单性,大家开始偏向于使用function组件了
React Hook:钩子
Hook是在React 16.8之后才有的低版本呢可以跳过
官方文档:Hook 简介
- React中的每一个钩子就是一个方法
- 官方总共提供了15个钩子函数,为了和普通函数进行区分,所有的钩子函数都必须以
useXxx
的格式书写 - 所有的钩子函数都只能在function式组件中调用——不能用于class组件
- 官方要求所有的钩子函数都只能在function组件内部的顶部进行调用
useState()
// 正确
if(useState()) {
}
// 错误
for (){
useState()
}
// 错误
function inner() {
useState()
}
// 错误
- 每个钩子函数都能为组件扩展功能
接下来我们来讲讲钩子怎么用吧
useState钩子函数
首先我们来写一个试试
let x = useState(0);
let y = useState(5);
console.log(x, y);
打印结果
我们可以看到它返回的是一个数组
如果还没懂的话我们使用解构来看一下
let [cont, f2] = useState(666);
console.log("cont", cont);
console.log("f2", f2);
既然我们知道了两个值的意义了接下来我们来写的demo加深理解一下
import React, {
useState } from "react";
export default function Child() {
// 点击计数器-状态变量
// let x = useState(0);
// let y = useState(5);
// console.log(x, y);
// 解构语法
let [cont, setCont] = useState(666);
console.log("cont", cont);
console.log("setCont", setCont);
return (
<div>
<h3>这里是计数器(子组件)</h3>
<p>计数器:{
cont}</p>
<button
onClick={
() => {
setCont(667);
}}>
点击添加
</button>
</div>
);
}
估计大家又要喷我了写什么setCont啊容易搞混,这里说一下,这个参数是随意的,只是一个方法名而已(解构时定义)
我也可以这样写
import React, {
useState } from "react";
export default function Child() {
// 点击计数器-状态变量
// let x = useState(0);
// let y = useState(5);
// console.log(x, y);
// 解构语法
let [cont, suibian] = useState(666);
console.log("cont", cont);
console.log("suibian", suibian);
return (
<div>
<h3>这里是计数器(子组件)</h3>
<p>计数器:{
cont}</p>
<button
onClick={
() => {
suibian(667);
}}>
点击添加
</button>
</div>
);
}
这样写还不会的话你就回家洗洗吧(滑稽)
上面说了数字了接下来我们来试试字符串试试
其实useState并不是只能用于数字也可以用于字符串例如我写一个点击之后变化的方式
import React, {
useState } from "react";
export default function Child() {
// 点击计数器-状态变量
// let x = useState(0);
// let y = useState(5);
// console.log(x, y);
// 解构语法
let [cont, suibian] = useState("你瞅啥");
console.log("cont", cont);
console.log("suibian", suibian);
return (
<div>
<h3>这里是子组件</h3>
<p>提示:{
cont}</p>
<button
onClick={
() => {
suibian("瞅你咋滴");
}}>
点击添加
</button>
</div>
);
}
其实useState定义字符串也是可以的,并不只能用于数字
这时候就有些杠精来问了,这个玩意有长度限制嘛?
答:是没有的
useEffect钩子函数
useEffect钩子函数(生命周期方法/副作用钩子)
使用方式
useEffect(()=>{
// 此处就是一个生命周期方式
})
注意:hook提供的生命周期方法是无名的(例如Vue就是有名的(Monuted…))
总结
class和函数组件是否能混用
子组件
父组件
结果能正常显示
答案是可以混用的
React函数式组件经典错误
Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
函数式组件发生无限循环,例如:
import React, {
useState } from "react";
import Child from "./Child";
export default function Parent() {
// const [Shower, setShower] = useState(false);
const [Shower, setShower] = useState(true);
return (
<div style={
{
backgroundColor: "#afa" }}>
<h2>这里是父组件</h2>
<button onClick={
setShower(!Shower)}>显示子组件</button>
{
Shower && <Child />}
</div>
);
}
解决方法为在外面写一个函数用于操作数据,点击之后进行操作数据
import React, {
useState } from "react";
import Child from "./Child";
export default function Parent() {
// const [Shower, setShower] = useState(false);
const [Shower, setShower] = useState(true);
function showChar() {
setShower(!Shower);
// 修改状态的方法都是异步的
}
return (
<div style={
{
backgroundColor: "#afa" }}>
<h2>这里是父组件</h2>
<button
onClick={
() => {
showChar();
}}>
显示子组件
</button>
{
Shower && <Child />}
</div>
);
}
关于React Hook提供的6种生命周期方法
其实一般的React的一些钩子函数都是由useEffect
演变而来的,中文翻译过来的意思就是使用效果
useEffect
的常见写法有两种,而携带的参数有两个,其中第二个参数可以不写(下面会说)格式如下
useEffect(() => {
// 执行代码
});
或者
useEffect(() => {
// 执行代码
},[依赖项目]);
其实这个依赖项目可以理解为监听,如果依赖列表为空的话就说明不见听和监听的区别
组件挂载时的一些钩子函数
这个分支主要是聊聊组件在挂载时的那些事
下面我们来写一个简单的demo来看看吧
import React, {
useEffect, useState } from "react";
export default function Child() {
/*hook 提供的生命周期方法一共有7个*/
useEffect(() => {
// console.log("生命周期方法:页面挂载时 - 1 = componentDidMount");
console.log("生命周期方法:组件挂载+任意属性/状态发生改变");
// componentDidMount+componentDidUpdate+componentWillUnmount
});
/*******************************/
// 点击计数器-状态变量
// let x = useState(0);
// let y = useState(5);
// console.log(x, y);
// 解构语法
let [cont, setCont] = useState(() => {
return "abc";
});
// console.log("cont", cont);
// console.log("setCont", setCont);
return (
<div>
<h3>这里是子组件</h3>
<p>提示:{
cont}</p>
<button
onClick={
() => {
setCont("瞅你咋滴");
}}>
点击添加
</button>
</div>
);
}
替换掉代码
运行如下图
但是既然是生命周期钩子函数但是为什么只有一个钩子呢?不应该象Vue那样拥有参数嘛?
别急,下面我们就来说说useEffect
的第二个变量了
- 第一个参数为生命周期方法
- 第二个参数为生命周期依赖的方法
还是之前的demo这次我们添加第二个参数
import React, {
useEffect, useState } from "react";
export default function Child() {
/*hook 提供的生命周期方法一共有7个*/
useEffect(() => {
// console.log("生命周期方法:页面挂载时 - 1 = componentDidMount");
console.log("生命周期方法:组件挂载+任意属性/状态发生改变1");
// componentDidMount+componentDidUpdate+componentWillUnmount
});
/*******************************/
// 点击计数器-状态变量
// let x = useState(0);
// let y = useState(5);
// console.log(x, y);
// 解构语法
let [cont, setCont] = useState(() => {
return "abc";
});
let [conts, setConts] = useState(() => {
return "abc";
});
useEffect(() => {
// console.log("生命周期方法:页面挂载时 - 1 = componentDidMount");
console.log("生命周期方法:组件挂载+任意属性/状态发生改变2");
// componentDidMount+componentDidUpdate+componentWillUnmount
}, [conts]);
// 第一个参数为生命周期方法
// 第二个参数为生命周期依赖的方法
// console.log("cont", cont);
// console.log("setCont", setCont);
return (
<div>
<h3>这里是子组件</h3>
<p>提示:{
cont}</p>
<button
onClick={
() => {
setCont("瞅你咋滴");
}}>
点击添加
</button>
</div>
);
}
但是我们操作几轮之后发现
我们发现第二个钩子函数树对应的consts变量只有发生变化是才会触发(这里是挂载渲染)
其实我们常见的就是
当第二个参数的依赖列表为空时,任何数据状态改变都不会重新调用
demo如下
import React, {
useEffect, useState } from "react";
export default function Child() {
/*hook 提供的生命周期方法一共有7个*/
useEffect(() => {
// console.log("生命周期方法:页面挂载时 - 1 = componentDidMount");
console.log("生命周期方法1:组件挂载+任意属性/状态发生改变");
// componentDidMount+componentDidUpdate+componentWillUnmount
});
/*******************************/
// 点击计数器-状态变量
// let x = useState(0);
// let y = useState(5);
// console.log(x, y);
// 解构语法
let [cont, setCont] = useState(() => {
return "abc";
});
let [conts, setConts] = useState(() => {
return "abc";
});
useEffect(() => {
// console.log("生命周期方法:页面挂载时 - 1 = componentDidMount");
console.log("生命周期方法2:组件挂载+任意属性/状态发生改变");
// componentDidMount+componentDidUpdate+componentWillUnmount
}, [conts]);
useEffect(() => {
// console.log("生命周期方法:页面挂载时 - 1 = componentDidMount");
console.log("生命周期方法3:组件挂载+任意属性/状态发生改变");
// componentDidMount+componentDidUpdate+componentWillUnmount
}, []);
// 第一个参数为生命周期方法
// 第二个参数为生命周期依赖的方法
// console.log("cont", cont);
// console.log("setCont", setCont);
return (
<div>
<h3>这里是子组件</h3>
<p>提示:{
cont}</p>
<button
onClick={
() => {
setCont("瞅你咋滴");
}}>
点击添加
</button>
</div>
);
}
我们发现除了组件挂载之后就没有了产生触发了
关于组件卸载相关的方法
其实和上面差不多下面的代码有注释将就看吧
// 关于组件卸载相关的方法
useEffect(() => {
return () => {
console.log("生命周期方法-4:组件卸载+任意属性/状态发生改变");
};
});
// 关于组件卸载相关的方法同时监听某个值的改变
useEffect(() => {
return () => {
console.log("生命周期方法-5:组件卸载+指定属性/状态发生改变");
};
}, [conts]);
// 只有组件卸载,不依赖于任何状态改变
useEffect(() => {
return () => {
console.log("生命周期方法-6:只有组件卸载,不依赖于任何状态改变");
};
}, [conts]);
在原有代码的基础上添加上上面的代码,运行我们可以看到如下结果
其实到这里React的生命周期我们就说完了
但是学到这里,可能已经学到很多东西,但是这距离我们的目标 简历上写上熟练使用React是远远不够的
所以接下来就到了我们的重点了
React路由导航
路由导航的功能
- 为每个页面分配访问地址----路由词典
- 路由导航,从一个页面跳转到另外一个页面
- 跳转时传递参数
React生态中有两个路由导航模块
- React-Router-DOM:用于浏览器项目中:H5,网站
- Router-Navigation:用于没有浏览器的项目中:RN的原生APP(这个组件暂时不讲,因为这个东西
React native
用得多,现在我们用不到,后面有时间就再出篇文章单独讲讲)
接下来就来看看这个包
资料下图
安装
npm i react-router-dom
** 安装完成后package.json
中就会有**
** 添加好之后我们就可以添加路由了**
添加路由
- 使用时首先需要在index.js里面按需导入
import { createBrowserRouter, RouterProvider } from "react-router-dom"; // createBrowserRouter:创建路由对象 // RouterProvider:挂载对象
- 然后创建一个路由对象
但是在这之前需要以// 创建路由器,包括词典 let router = createBrowserRouter([ { path: "路径以斜杆开头", element: 组件简单名, }, // 例如 { path: "*", element: <NoFound />, }, ]);
import
的方式导入页面如上面的import NoFound from "./pages/NoFound";
挂载路由
上面我们说了创建路由对象之后现在我们是不能直接使用路由的所以我们是需要去挂载一下路由的
挂载路由我们需要使用上面引入的第二个方法了( RouterProvider
)
使用方式为
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
// 注意看这里
<RouterProvider router={
router} />
</React.StrictMode>,
);
结尾
-看到这里相信你的React基本上就可以上简历了,欢迎点上关注期待往后的文章更新
- 技术交流可以添加我的微信
startrrshutsan
(长期有效)拉你进入交流群