React
React是什么:
react 是一个用于构建用户界面的JavaScript库
React特点:
1、声明式编码
2、组件化编码 将一个较大复杂的页面拆分成几个可复用的部分封装成多个组件,在组合使用 组件可以被反复使用
3、引入了JSX语法,编写高效,可以封装多个组件,实现页面复用
4、内置diff算法,运行高效
5、一次学习,随处编写 不仅可以开发web应用(reaact-dom),还可以开发原生安卓或ios应用 (react-native)
React 初体验
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
<script>
let html = React.createElement('div',null,React.createElement('h1',{title:"react二次体验"},'react二次体验'),React.createElement('div',{title:'内容'},'就只是下要嵌套一下'))
ReactDOM.render(html,document.getElementById('root'))
</script>
</body>
</html>
createElement 可以接受无数个参数 ,从第三个开始都是当作标题体存在(关系是嵌套关系)
createElement 标题的属性可以写对象 ,也可以写null、undefined
React.createElement 创建一个虚拟dom对象
虚拟dom特点
1、属性比较少
2、更新高效 (因为真实dom属性比较多 ,虚拟dom属性比较少 ,所以当挂载的时候对比的就比较少,更新也就更高效)
3、虚拟dom最后也会变成真实dom
使用jsx创建虚拟dom对象
使用jsx 需要在script标签中 配置 type = “text/babel”
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<!-- 引入babel 将jsx代码转换成js代码 -->
<script src="./js/babel.min.js"></script>
</head>
<body>
<div id="rood"></div>
<script type="text/babel">
let jsx = <div>
<h1 title='标题'>标题</h1>
<div>hell world!</div>
</div>
ReactDOM.render(jsx,document.getElementById('rood'))
</script>
</body>
</html>
jsx
jsx是什么
全称 : javascript xml
作用:在js中更加高效的编写html代码
jsx特点
1、不能用引号包裹 , 但是可以用(小括号) 包裹
2、JSX 与 JS 拼接的时候需要用 {花括号} 表达式包裹
3、JSX 设置class 时 ,需要使用className (因为 class 类是关键字)
4、JSX设置style时 ,需要使用 {{ }} (外面 { } 是表达式的花括号 , 里面 { } 是style的花括号 ,因为style是对象
5、JSX 必须有跟标签包裹
6、JSX中的单标签需要有结束符号
7、JSX中的标签名需要注意 (1)小写字母的标签,会自动找到HTML中对应的标签。 (2)大写字母的标签,会自动找到React中对应的组件,找不到会报错。(3)如果编写的是html中的标签 ,字母需要小写 , 如果编写的是React中的组件, 字母需要大写。
JSX表达式特点
1、书写格式 : { }
2、存放的内容 :
- (1)字面量数据、 变量、 虚拟dom、
- (2)string number array 可以直接渲染到页面中(是有意义的)array会自动循环遍历
- (3)unmber null boolean Symol() 不可以直接渲染到页面 ,但是会有空标签显式 (没有意义)
- (4)object 不可以放入字面量中 会报错
- (5)function 需要看返回值 ,返回值如果是 (2) 那就有意义; 返回值如果是 (3) 那么就没有意义;
代码执行顺序
1、顺序结构 2、选择结构 3、循环结构
React条件渲染
条件渲染有三种:
- 1、if…else
- 2、三元运算符
- 3、逻辑与&&
<div id="root"></div>
<script type="text/babel">
let vip = true
let vdom
// if...else
if (!vip){ //判断如果是vip不显式广告
vdom = <div className='box'>广告</div>
}
// 三元运算符
vdom = !vip ? null : <div className='box'>广告</div>
// 逻辑 && 判断
vdom = !vip && <div className='box'>广告</div>
ReactDOM.render(vdom,document.getElementById('root'))
</script>
React列表渲染
在JSX中使用循环只能使用 map , 因为它有返回值
<div id="root"></div>
<script type="text/babel">
let arr = ['a','b','c',1,2,3];
let vdom = <div>
{
arr.map(item =>{
return <div>{item}</div>
})
}
</div>
ReactDOM.render(vdom, document.getElementById("root"));
</script>
JS中的循环
- 1、for 已知条件、已知范围
- 2、while 未知条件、未知范围
- 3、do whlie 至少执行一次
- 4、for in 遍历对象
- 5、for of 遍历数组
- 6、forEach 循环遍历 (没有返回值)
- 7、map 循环遍历 (有返回值 且 返回一个新数组 ,对原数组没有影响)
diff算法 (比较算法)
diffing 算法 也叫 比较算法 。 是比较新老dom的结构 对比新dom和老dom之间有哪些变动 ,有变动的才改变,没有变动的就不做改变 ,key 表示数据的唯一性 可以使用id当做key值(index 不可以当做key , 因为index会变化)
React 的 事件绑定
- 1、react 中的事件执行机制和原生js一致
- 2、react 中的事件是一个合成事件 , 对原始js的事件进行重新封装,解决了不同浏览器的兼容性问题
- 3、react 中的事件名 必须要使用小驼峰
- 4、react 中事件必须调用函数
- 5、react 事件绑定只能使用行内绑定方式
- 6、react 中的JSX语法使用了严格模式 ,事件回调函数中的this值向了undefind
- 7、react 中获取事件对象,只能使用 e 或 event
- 8、react 中阻止默认事件行为 不能使用 return false ; 只能使用 e.preventDefault()
<div id="root"></div>
<script type="text/babel">
function fn(){
alert("外部函数调用")
}
function fn1 (){
return () => {
alert('外部函数括号调用')
}
}
let vdom = (
<div>
{/* 函数调用 */}
<button onClick = {function(){
alert('普通函数调用')
}} >普通函数调用</button>
<button onClick = {()=>{
alert("箭头函数调用")
}}>箭头函数调用</button>
{/* 外部函数不写 括号 是点击后调用的 , 写 括号 打开浏览器会自动调用 */}
<button onClick = {fn} >外部函数不写()调用</button>
{/* 写括号调用 ,调用的是 函数内部返回的 函数方法 的调用 */}
<button onClick = {fn1()} >外部函数写()调用</button>
{/* 阻止默认事件 */}
{/* 点击获取标签文字 , 在jsx中 this 指向 undefined*/}
<div onClick={(e)=>{
alert( e.target.innerHTML)
}} >这是一句话 ,点击获取它</div>
{/* 点击 阻止默认行为 两种方法return false在jsx中无法使用e.preventDefault 可以在jsx中使用*/}
<a onClick={(e)=>{
e.preventDefault()
}} href="http://baidu.com">百度</a>
{/* onChange 事件 在js中 失去焦点才触发 , 在jsx中改变就触发*/}
<input type="text" onChange = {()=>{
console.log ('输入框内容发生变化了');
}} />
{/* onInput 事件 在js改变就触发 , 在jsx中改变就触发*/}
<input type="text" onInput = {()=>{
console.log ('输入框内容发生变化了');
}} />
</div>)
ReactDOM.render(vdom,document.getElementById('root'))
</script>
默认行为
- 1、表单会自动提交
- 2、点击a链接会自动跳转
- 3、鼠标右键展示 操作列表
组件
组件
本质:高度封装 js css html
特点: 解决代码耦合 , 实现代码复用
组件在 React 中分为函数组件和类组件
使用: 组件最终是用标签呈现出来的(标签是以大写字母开头)
组件其实就是在自定义标签
执行本质
- 1、创建一个函数式组件
- 2、函数组件内部必须返回一个虚拟DOM对象
- 3、当使用组件标签时,react内部就会找到对应的同名函数,自动返回一个虚拟DOM
组件的区别
- 1、存放位置
- 一般组件 存放在 componets 文件夹中
- 路由组件 存放在pages 文件夹中
- 2、使用形式
- 一般组件 <Header></Header>
- 路由组件 <Route path = “/” element = {<Header></Header>}> </Route>
React中分为函数组件和类组件
1、函数式组件
函数式组件的执行过程 : 当使用组件标签的时候 , react内部会找到对应的同名函数 , 会自动执行这个函数 , 返回一个虚拟dom对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>函数式组件</title>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
// 函数式组件
//1、定义一个函数组件
function Zujian (){
return <div>这是一个函数是组件</div>
}
function Zujian2 (){
return <div>这是第二个函数是组件</div>
}
// App 是组件的根
function App (){
return <div>
<Zujian/>
<Zujian2></Zujian2>
</div>
}
ReactDOM.render(<App></App>, document.getElementById("root"));
</script>
</body>
</html>
2、类式组件
类式组件的执行过程: 当使用类式组件标签时, react内部会自动找同名的这个类,自动帮你实例化对象【new Fn ()】/ 【new Mengyao ()】 , 得到一个实例化对象后, 在去调用各自内部的 render 方法 , 返回一个虚拟dom对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>类式组件</title>
</head>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<!-- 引入 babel 将jsx语法 转换为 js语法 -->
<script src="./js/babel.min.js"></script>
<body>
<div id="root"></div>
<script type="text/babel">
class Fn extends React.Component{
render(){
return <div>这是类方法实现的组件</div>
}
}
class Mengyao extends React.Component{
render(){
return <div>这是创建了一个自己的组件</div>
}
}
class App extends React.Component{
render(){
return <div>
<Fn></Fn>
<Mengyao/>
</div>
}
}
ReactDOM.render(<App/>,document.getElementById("root"))
</script>
</body>
</html>
class 类
1、构造方法 : 初始化实例化对象
- constructor (构造器)
2、向实力对象添加 : 方法
- run = ( ) => { }
- run = function ( ) { }
- 上面两个都是给实例化对象添加方法 , 箭头函数和普通函数都可以添加
3、向原型对象身上添加方法
- run ( ) { }
4、向类身上添加静态方法
- static
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
class Father{
// 属性
uname = "张三";
age;
//类的静态成员 类
static like;
//构造方法 : 初始化实例对象
constructor(uname,age,sex){
this.uname = uname;
this.age = age;
this.sex = sex;
}
// 向实例对象添加 方法
speak = ()=>{
console.log("我是箭头函数");
}
run = function(){
console.log("我是普通函数");
}
//向原型对象添加方法
Cook(){
console.log("我会做饭");
}
}
class Son extends Father{
// 属性
height;
//构造方法
constructor(uname,age,sex,height){
super(uname,age,sex); // 调用父类的构造函数
this.height = height;
}
//方法
// 重写父类方法
speak = ()=>{
console.log("我是儿子,我是个普通函数");
}
}
//实例化对象
/* let f = new Father("张三",18,"男");
console.log(f);
// f.speak();
f.run();
console.dir(Father); */
//实例化对象
let s = new Son("李四",15,"男",150);
console.log(s);
let s1 = new Son("王五",17,"男",180);
console.log(s1);
// s.speak();
</script>
</body>
</html>
继承
- 1、原型继承
- 2、类继承
- js默认继承是原型继承
- 而 class 只是 Es6 中的语法糖而已 (本质还是原型继承)
如何改变this指向
- 1、call
- 2、apply
- 3、bind
- 4、new 关键字: 在堆中开辟一个空间 , this 指向该空间 , 执行函数代码 , 返回实例化对象的地址
图片的类型和特点
图片有两种 位图和矢量图
- 1、位图 : png 、 jpg 、 jpeg 、 gif 、特点:缩放会失真 , 可以做色彩丰富的图片
- 2、矢量图 : svg、 特点: 缩放不会失真 , 可以做色彩简单的图片 如(logo 、小图标)
React脚手架
- 1、下载安装环境
npm i create-react-app -g
- 2、创建项目 , 在项目所在的目录下 (项目名称不可以有大写字母和中文)
create-react-app reactcli
- 3、启动服务
npm start
项目目录
- 1、node-modules 项目依赖包文件
- 2、public 网站根目录 (静态资源目录)
- 3、public 文件里面的 index.html文件 (是webpack打包的模板文件)
- 4、package.json 项目声明文件
- 5、package-lock.json 依赖包的锁文件
- 6、src 源代码目录
- 7、src文件中的 index.js文件 (是项目的入口文件)
react版本
- 1、最新版本 18.2.0
- 2、版本分水岭 16.8 之前,官方推荐使用类式组件
- 3、16.8之后 , 官方推荐函数式组件 + hook
- 4、讲课用的 17 版本 用的 类式组件
npm i react@17 react-dom@17
前端的数据来源
- 1、后台服务器响应返回到的数据
- 2、前端列表收集到的数据
React 类式组件特点
react类式组件实例化对象三大属性之一 state
state 作用 :在类式组件中存储状态数据
state 值 : state 值是一个对象
state特点 : 数据不可以直接更改 react 不承认
setState( ) : setState( )用来修改/更新state数据 , 这些数据才是响应式数据
import React from "react";
import "./Demo.css"
export default class Demo extends React.Component{
state = {
str:"明天自习我不上课",
num:4
}
render(){
return <div className="demo">
<h1 >{ this.state.str }</h1>
<h1>{ this.state.num }</h1>
<button onClick={()=>{
//更新state数据
this.setState({
str:"这才是更新的响应式数据",
num:5,
});
}}>点击更新</button>
</div>
}
}
react的类式组件实例化对象三大属性之一 props
props 的定义 : 是用来接受外部组件传递的数据 , 只可读 ,不可改
props 的特点 : props接受的数据是只可读 ,不可修改。 数据源在那就在哪里更改。
标签体形式传递 — Childeren
props 的三种传参方式
import {Component} from "react"
import Header from '../../component/Header/Header'
import Foot from '../../component/Foot/Foot'
import Middle from '../../component/Middle/Middle'
import Mv from '../../component/Mv/Mv'
import News from '../../component/News/News'
export default class Home extends Component{
state = {
middleArr: [
{ title: "html", position: "熟练掌握" , bol:false},
{ title: "css", position: "了解", bol:false },
{ title: "js", position: "精通" , bol:false},
{ title: "ES6", position: "了解" , bol:false},
{ title: "git", position: "熟练掌握" , bol:false},
{ title: "node", position: "精通", bol:false },
{ title: "webpack", position: "熟练掌握", bol:false },
{ title: "express", position: "精通" , bol:false},
{ title: "npm", position: "熟练掌握", bol:false },
{ title: "mongod", position: "精通", bol:false },
{ title: "axios", position: "熟练掌握", bol:false },
{ title: "bootstrap", position: "了解" , bol:false},
{ title: "Promise", position: "精通", bol:false },
{ title: "react", position: "熟练掌握" , bol:false},
{ title: "http", position: "精通", bol:false },
{ title: "vue", position: "了解", bol:false },
{ title: "wechat", position: "熟练掌握", bol:false },
{ title: "json", position: "熟练掌握", bol:false }
],
uname:"yaoyao",
agr: 18,
bol:true
};
// 点击调用,页面更新
changeMiddle = (i)=>{
return ()=>{
// 改变数据,返回一个新数组
let newmiddleArr = this.state.middleArr.map ((item,index) =>{
if(index === i){
item.bol = !item.bol
}
return item
})
// 将原数组替换成新数组
this.setState({middleArr : newmiddleArr})
}
}
render (){
// 第二种传递方法 的结构
let {middleArr , uname , agr ,bol} = this.state
return <div>
<Header></Header>
{/* 第一种传递方法 ,不推荐 */}
{/* <Middle>{this.state.middleArr}</Middle> */}
{/* 第二种传递方法 ,需要哪些数据传递哪些数据 */}
<Middle middleArr = {middleArr} agr = {agr} changeMiddle={this.changeMiddle}></Middle>
{/* 第三种传递方法 ,使用扩展运算符传递数据 , {}是表达式 , 在react中结构对象可以省略它自己的花括号*/}
{/* <Middle {...this.state} changeMiddle={this.changeMiddle}></Middle> */}
<Mv></Mv>
<News></News>
<Foot></Foot>
</div>
}
}
props 三种接收方式
props – types 约束 props传递的数据 (isRequired 必传项)
defaultProps 设置默认数据
import {Component} from 'react'
import './Middle.css'
// 约束props数据
import PropTypes from 'prop-types'
export default class Middfle extends Component{
// 限制传递的类型
static propType = {
middleArr:PropTypes.array.isRequired,
uname:PropTypes.string.isRequired,
changeMiddle:PropTypes.func.isRequired,
}
// 设置默认数据
static defaultProps = {
uname:'这是默认数据'
}
render (){
// 第一种传递 需要一个一个传递,顺序都不可以发送改变 (不推荐)
console.log(this.props);
// let middleArr = this.props.children
// 第二种传递方法,需要哪些传递哪些
// let {middleArr , uname , agr } = this.props
// 第三种 , 使用 ... 扩展运算符
let {middleArr ,uname, changeMiddle} = this.props
console.log(uname);
return (<div className='middle'>
<h1>{uname}</h1>
<ul>
{
middleArr.map((item,index) =>{
return (
<li onClick = {changeMiddle(index)} key={index}>
<div> {item.title}</div>
{/* 点击显式熟练程度 */}
{
item.bol ? <div> {item.position}</div> : null
}
</li>)
})
}
</ul>
</div>)
}
}
类式组件实例化对象三大属性之一 ref
ref 的作用 : 获取真实DOM对象
1、字符串方式的ref
import React, { Component } from 'react';
class StrRef extends Component {
//获取数据方法
getValue = () =>{
// console.dir(this.refs.username.value);
//获取输入框数据
let {value} = this.refs.username;
// 获取div对象并展示
this.refs.con.innerHTML = value;
//清空数据
this.refs.username.value = "";
}
render() {
return (
<div>
<h1>字符串形式的ref</h1>
<input ref="username" type="text" />
<button onClick={this.getValue}>获取数据</button>
<div ref="con" style={{width:300,height:300,border:"1px solid red"}}></div>
</div>
);
}
}
export default StrRef;
2、回调函数形式的ref
当react 在 执行虚拟DOM这行代码时,会将真实DOM对象作为实参 去执行后面的回调函数 , 将真实DOM对象赋值给了 实例化对象的属性
import React, { Component } from 'react';
class FnRef extends Component {
//获取数据方法
getValue = () =>{
// console.dir(this.con);
//获取数据
let value = this.input.value;
// 展示到div
this.con.innerHTML = value;
//清空
this.input.value = "";
}
render() {
return (
<div>
<h1>回调函数形式的ref</h1>
{/* 会将真实dom对象 作为参数去执行后边的回调函数 */}
<input type="text" ref={ (el) => { this.input = el } } />
<button onClick={this.getValue}>获取数据</button>
<div ref={ el => this.con = el } style={{width:300,height:300,border:"1px solid red"}}></div>
</div>
);
}
}
export default FnRef;
3、使用crearteRef( ) 创建 ref 容器
import React, { Component,createRef } from 'react';
class CreateRef extends Component {
// 1. 使用 createRef() 创建 ref 容器
input = React.createRef();
con = createRef();
//获取数据方法
getValue = () =>{
console.dir(this);
//获取数据
let {value} = this.input.current;
// 展示数据
this.con.current.innerHTML = value;
//清空数据
this.input.current.value = "";
}
render() {
return (
<div>
<h1>createRef()</h1>
<input ref={this.input} type="text" />
<button onClick={this.getValue}>获取数据</button>
<div ref={this.con} style={{width:300,height:300,border:"1px solid red" }}></div>
</div>
);
}
}
export default CreateRef;
非受控组件
概念 : 前端表单收集的数据 不被 state 所管理 。 是通过ref 获取真实Dom对象获取的。
import React, { Component,createRef } from 'react';
class CreateRef extends Component {
// 1. 使用 createRef() 创建 ref 容器
input = React.createRef();
con = createRef();
//获取数据方法
getValue = () =>{
console.dir(this);
//获取数据
let {value} = this.input.current;
// 展示数据
this.con.current.innerHTML = value;
//清空数据
this.input.current.value = "";
}
render() {
return (
<div>
<h1>createRef()</h1>
<input ref={this.input} type="text" />
<button onClick={this.getValue}>获取数据</button>
<div ref={this.con} style={{width:300,height:300,border:"1px solid red" }}></div>
</div>
);
}
}
export default CreateRef;
受控组件
概念 : 前端表单收集的数据被 state 所管理
import React, { Component } from 'react';
class Control extends Component {
//声明状态
state = {
username:"",
pwd:""
}
//获取用户名
saveUsername = (e) =>{
// console.log(e.target.value);
//更新数据
this.setState({
username:e.target.value
})
}
// 获取密码
savePwd = (e) => {
//更新数据
this.setState({
pwd:e.target.value
})
}
//登录方法
login = () =>{
alert(`账号是:${this.state.username},密码是:${this.state.pwd}`);
}
//清空
reset = () =>{
this.setState({
username:"",
pwd:""
})
}
render() {
return (
<div style={{width:300,margin:"200px auto"}} >
<h1>受控组件</h1>
<input type="text" placeholder='账号' onChange={this.saveUsername} value={this.state.username} /><br /><br />
<input type="text" placeholder='密码' onChange={this.savePwd} value={this.state.pwd} /><br /><br />
<button onClick={this.login}>登录</button>
<button onClick={this.reset}>清空</button>
</div>
);
}
}
export default Control;
React.Fragment
作用 :包裹内部组件标签的 。 (因为所有div包裹, 发现有很多的无用层级,可以使用 <React.Fragment></React.Fragment>包裹 ,可以减少无用层级。)
特点 : 在浏览器中不加载这个内置组件
简写形式 : <></>
生命周期
概念 : 组件从创建到销毁的一系列的过程就叫组件的生命周期。 在这个过程中,会自动调用一些函数 , 这个函数就叫生命周期钩子函数
component 组件 ; mount 挂载
- 1、componentDidMount 、组件完成挂载后执行
- 在这个阶段做一些初始化的工作
- (1) 创建定时器
- (2) 发送ajax请求
- (3) 订阅消息
- (4) 操作Dom对象
- 2、componentDidUpdate、组件完成更新后执行
- (1) 操作新的Dom对象
- 3、componentwillUnmount、组件将要销毁(卸载)时执行
- 做一些收尾工作
- (1) 清除定时器
- (2) 取消订阅
- 4、render 渲染虚拟Dom对象
- 注意点:render方法中不能更新数据 , 不能调用setState()
- render 执行的次数 ( 1 + n 次)
React 函数式组件特点
hooks
本质 : 本质就是react内置的一些函数
作用 : 只可以在函数时组件中使用
hook 注意点
- hook 不能在条件判断或者循环中
- hook 只能在函数式组件内部使用
useState( )
作用:在函数式组件中声明状态 是一个数组
存放 :在useState中可以存放数据 , 但是基于状态的最小原则,已有数据能够计算出新的结果 ,就没有必要在存放在sueState中
import React ,{useState}from 'react'
export default function UserState() {
let [bol,setBol] = useState(true);
let click = () => {
setBol(!bol)
}
return (
<>
<h1>UserState ,hooks 中的函数 </h1>
<hr />
<div>明天学习吗?{bol ? "学习" : '不学习'}</div>
<button onClick={click}>点击</button>
</>
)
}
useRef( )
作用:在函数式组件中操作真实Dom对象
import React, { useRef } from 'react'
import './UseRef.css'
export default function UseRef() {
let input = useRef()
let box = useRef()
let click = () =>{
console.log(input.current.value);
box.current.innerHTML = input.current.value
input.current.value = ''
}
return (
<div className='useRef'>
<input ref={input} type="text" />
<button onClick={click}>提交</button>
<div ref={box}></div>
</div>
)
}
useEffect( )
作用 :在函数式组件中模拟生命周期钩子函数
1、模拟componentDidMount 和 componentdidUpdate
useEffect(()=>{
console.log("模拟 componentDidMount 和 componentDidUpdate");
})
2、useEffect可以多次调用
3、单独模拟componentDidMount
useEffect(()=>{
console.log("单独模拟 componentDidMount");
},[])
4、模拟componentDidMount 和 监听某个声明的状态变化后的更新
useEffect(()=>{
console.log("模拟 componentDidMount 和监听某个状态变化后的更新");
},[bol])
5、模拟componentWillunmount
useEffect(()=>{
return ()=>{
console.log("单独模拟 componentWillUnmount");
}
},[])
useContext( )
隔代组件数据传递
前后台数据通讯
- 1、xmlHttpRequest (原生js)
- 2、ajax (jqyery)
- 3、axios (react)
- 4、fetch (ES6)
axios
- 1、纯粹 , 只做数据交互
- 2、基于 Promise
- promise的状态 : pending (等待), resolve(成功) , reject(失败)
- 3、方法
- axios.get(url , config)获取数据
- axios.post(url , data , config)添加数据
- axios.put(url , data , config)全局更新数据
- axios.patch(url , data , config)局部更新数据
- axios.delete(url , config)删除数据
组件数据传递
1、props (父子件传递)
作用:父子组件中传递参数
如果在父组件中自定义属性传递数据 , 子组件中props接受数据 , 则是父亲向儿子传递数据
如果在父组件中自定义属性传递方法,子组件中props接收方法,子组件调用方法并传递数据,则是儿子向父亲传递参数
// 在父组件中传递参数
<TodoMain todolist={todolist} />
// 在子组件中使用props接受参数
export default function TodoMain(props) {
//解构
let {todolist} = props;
2、useContext (隔代组件数据传递)
// 1. 在 祖先 组件中
// 1. 创建 context 对象
export let countContext = React.createContext();
export default function A() {
//声明状态
let [count,setCount] = useState(100);
return (
<>
{/* 2. 用 countContext.Provider 包裹所有的后代组件, 使用 value 属性传递数据*/}
<countContext.Provider value={count}>
<h1>AAAAAAAAAAAAAAA</h1>
<B/>
</countContext.Provider>
</>
)
}
// 在 任意的 后代组件中
import React, { useContext } from 'react'
// 3. 导入 context 上下文对象
import { countContext } from "../A/A"
export default function C() {
// 4. 使用 useContex hook 接受传递的数据
let count = useContext(countContext);
// console.log(count);
return (
<>
<h3>CCCCCCCCCCCCCCC</h3>
<h3>展示A组件中的数据:{count}</h3>
</>
)
}
3、pubsub-js (发布订阅) ,任意组件传递
1、下载 第三方库 ,实现发布订阅机制
// --force 强制下载 -发简写
npm i pubsub-js --force
2、导入 在要使用是页面导入
import PubSub from "pubsub-js"
3、使用 (发布消息) (订阅消息)
// 在获取数据页面发布数据
PubSub.publish("频道名称" , 要发布的数据)
// 在使用数据页面订阅数据
PubSub.subscribe("频道名称" , (message , data) => {
// message 频道名称 ,
// data 订阅的数据
})
// 在销毁阶段 取消订阅
PubSub.unsubscribe("频道名称")
4、redux (集中式状态管理工具)
5、本地存储
6、路由传参
路由传参
SPA
概念 :单页面应用 , 整个项目只有一个页面
优点 : 加载速度块 , 不会出现页面空白
路由
概念 : key ===》 value 映射
- key 地址
- value 函数 或 组件
// 后台 路由
app.get ("/home" ,(req , res)=>{
end("首页")
})
// 前端 路由
<Route path="/home" element={<Home/>}></Route>
react – router – dom
1、安装
npm i react-router-dom --force
2、导入
// 在App 根组件中导入
import {BrowserRouter , Routes , Route} from "react-router-dom"
react 路由初体验
//在 App根组件中
export default class App extends React.Component{
render(){
return <>
<BrowserRouter>
<Routes>
<Route path="/home" element={ <Home/>}/>
<Route path="/ref" element={ <Ref/>}/>
<Route path="/hooks" element={ <Hooks/>}/>
<Route path="/heros" element={ <Heros/>}/>
<Route path="/duanzi" element={ <Duanzi/>}/>
</Routes>
</BrowserRouter>
</>
}
}
react-router-dom中的组件
- 1、BrowserRouter 最外层的一个路由组件,用来包裹所有的路由
- 2、Routes 是指所有的路由组件, 用来包裹当路由组件
- 3、Route 单个路由组件
- path 路由地址
- element 路由组件
- index 入口
- 4、Link 链接组件
- 5、NavLink 导航链接
- 6、Outlet 子路由组件的占位
- 7、Navigate 路由重定向组件
- 8、HashRouter 最外层的一个路由组件 , 用来包裹所有的路由组件
react – router – dom Hook
- useRoutes() 创建路由对象
- useParams() 获取地址栏params参数
- useSearchParams() 获取地址栏query参数
- uselocation() 获取 state 参数
- useNavigate () 编程式路由导航
params 传递参数
1、当地址栏 是 ( /heros )时,展示英雄列表页面
2、当地址栏 是 (herosDetail/1)时 展示英雄详情页面
// 1. 配置路由
{
path:"/herosdetail/:id/:name",
element:<HerosDetail/>,
meta:{ // 路由元信息
title:"英雄详情",
isHidden:true
}
},
// 2. 在 英雄列表页面 使用 Link 跳转
<li key={item.id}>
<Link to={`/herosdetail/${item.id}/${item.name}`}>
<img src={`http://cdn.xiaohigh.com${item.image}`} alt="" />
<h3>{item.name}</h3>
</Link>
</li>
// 3. 在 英雄详情组件
// 获取地址栏 params 参数
let {id,name} = useParams();
query 传递参数
1、当地址栏 是 ( /heros ) 时,展示英雄列表也面
2、当地址栏 是 ( /herosDetail?id=1&name= ) 时,展示英雄详情也面
// 1. 配置路由
{
path:"/herosdetail",
element:<HerosDetail/>,
meta:{ // 路由元信息
title:"英雄详情",
isHidden:true
}
},
// 2. 在 英雄列表页面 使用 Link 跳转
<li key={item.id}>
<Link to={`/herosdetail?id=${item.id}&name=${item.name}`}>
<img src={`http://cdn.xiaohigh.com${item.image}`} alt="" />
<h3>{item.name}</h3>
</Link>
</li>
// 3. 在 英雄详情组件
// 获取地址栏 query 参数
let [search,setSearch] = useSearchParams();
// console.log(arr)
// 获取id
let id = search.get("id");
let name = search.get("name");
state 传递参数
1、当地址栏 是 ( /heros )时, 展示英雄列表页面
2、当地址栏 是 ( /hoersDetail )时 , 展示英雄列表页面
// 1. 配置路由
{
path:"/herosdetail",
element:<HerosDetail/>,
meta:{ // 路由元信息
title:"英雄详情",
isHidden:true
}
},
// 2. 在 英雄列表页面 使用 Link 跳转
<li key={item.id}>
<Link to="/herosdetail" state={{id:item.id,name:item.name}} >
<img src={`http://cdn.xiaohigh.com${item.image}`} alt="" />
<h3>{item.name}</h3>
</Link>
</li>
// 3. 在 英雄详情组件
// 获取 state 参数
let {state:{id,name}} = useLocation();
路由传参综合 (params , query , state)
- 1、params
- 优点
- 地址栏格式化比较清晰优雅
- 在其他浏览器打开数据不会丢失
- 缺点
- 不安全
- 传递的参数的个数和顺序必须固定
- 优点
- 2、query
- 优点
- 在其他浏览器打开数据不会丢失
- 传递的参数的个数和顺序可以不固定
- 缺点
- 不安全
- 地址栏格式不友好
- 优点
- 3、state
- 优点
- 传递的参数个数和顺序可以不固定
- 比较安全,地址栏看不到传递数据
- 缺点
- 在其他浏览器打开数据(会) 丢失
- state数据保存在浏览器的历史纪录中 (history)
- 优点
useNavigate 编程式路由导航
路由懒加载
解决的问题 : 首屏加载速度慢的问题
// 1. 导入 lazy 函数 Suspense【组件】
import { lazy, Suspense } from "react"
// 一般情况: 首页 404 登录 注册 不需要懒加载
import NotFound from "../pages/NotFound";
// 2. 使用 lazy 函数导入 组件
let Home = lazy(() => import("../pages/Home/Home"));
let Ref = lazy(() => import("../pages/Ref/Ref"));
let Hooks = lazy(() => import("../pages/Hooks/Hooks"));
let TodoList = lazy(() => import("../pages/TodoList/TodoList"));
let Heros = lazy(() => import("../pages/Heros/Heros"));
let Duanzi = lazy(() => import("../pages/Duanzi/Duanzi"));
let News = lazy(() => import("../pages/News"));
let Guonei = lazy(() => import("../pages/News/Guonei"));
let Junshi = lazy(() => import("../pages/News/Junshi"));
let HerosDetail = lazy(() => import("../pages/HerosDetail/HerosDetail"));
//3. 封装一懒加载组件的函数
let load = (Comp) =>{
return (
<Suspense fallback={<div>正在加载中...</div>}>
<Comp />
</Suspense>
)
}
// 4. 使用懒加载组件
{
path: "/", // 路由地址
element: load(Home), //路由组件
meta: { // 路由元信息
title: "首页"
}
},
哈希路由和BrowserRouter(history)
- 形式上 BrowserRouter 格式比较清晰,优雅 localhost:3000/home HashRouter 格式比较怪异 localhost:3000/#/home
- 兼容性 BrowserRouter 兼容 IE9 以上 HashRouter 兼容 IE8 以上
- 项目部署 BrowserRouter 项目打包编译后,必须有后台人员更改服务器配置,才可运行 HashRouter 项目打包编译后,可直接放在服务器上运行。
HashRouter原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HashRouter</title>
</head>
<style>
#content{
width: 300px;
height: 300px;
border: 1px solid red;
}
</style>
<body>
<a href="#home">首页</a>
<a href="#ref">ref</a>
<div id="content"></div>
<script>
// HashRouter 的原理 借助 hashchange 事件来完成的。
// hashchange 当浏览器地址栏的hash【锚点】值发生变化时 ,自动执行
window.addEventListener("hashchange",function(){
// location 浏览器地址栏对象
// console.log(location);
let {hash} = location;
switch(hash){
case "#home":
content.innerHTML = "首页 首页 首页 首页 首页";
break;
case "#ref":
content.innerHTML = "ref ref ref ref ref";
break;
default :
content.innerHTML = "404";
}
})
</script>
</body>
</html>
BrowserRouter原理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BrowserRouter</title>
</head>
<style>
#content{
width: 300px;
height: 300px;
border: 1px solid red;
}
</style>
<body>
<a href="/home">首页</a>
<a href="/ref">ref</a>
<div id="content"></div>
<script>
// 获取所有的a链接
let links = document.querySelectorAll('a');
links.forEach(link => {
//为 每个 a 链接绑定点击事件
link.addEventListener("click",function(e){
// 阻止默认行为
e.preventDefault();
// history 浏览器历史记录对象
// pushState() 向浏览器的记录的末尾 新增一条新的纪录
// pushState(要新增的数据,新纪录的标题,url地址)
history.pushState({id:123,name:"张三"},null,e.target.href);
// console.log(location);
console.log(history);
//获取地址栏路径
let {pathname} = location;
//判断
switch(pathname){
case "/home":
content.innerHTML = "首页 首页 首页 首页 首页";
break;
case "/ref":
content.innerHTML = "ref ref ref ref ref";
break;
default :
content.innerHTML = "404";
}
})
})
</script>
</body>
</html>
Redux
作用 :集中式状态管理工具
1、安装
npm i @reduxjs/toolkit --force
2、使用
// 1. 导入
// createSlice创建 切片对象
// configureStore 创建 store 对象
import { createSlice,configureStore } from "@reduxjs/toolkit"
// 2. 创建切片对象
let countSlice = createSlice({
//数据别名
name:"count",
//为数据赋值,推荐值为对象
initialState:{
value:100
},
// action 描述一个行为/动作。值为一个对象 {type:"类型",payload:"要操作的新的数据" }
// { type:"打",payload:"张三" } {type:"打",payload:"酱油"} {type:"学习",payload:"react"}
// {type:"incre",payload:1} {type:"decre",payload:2}
reducers:{
//加
incre:function(state,action){
// 必须对数据进行直接更改
state.value += action.payload;
},
//减
decre(state,{payload}){
// 必须对数据进行直接更改
state.value -= payload;
}
}
});
// 3. 提取action 方法
let { incre , decre} = countSlice.actions;
// 4. 创建仓库
let store = configureStore({
reducer:countSlice.reducer
});
// 5. 读取仓库数据
console.log(store.getState());
// 6. 调用action 方法
store.dispatch(incre(1));
store中的方法
- store.getState() 读取store 数据
- store.dispatch() 分发行为/动作
- store.subscribe() 订阅 store中的数据,当store 中的数据发生变化时,自动调用。
redux三大核心概念
- store 存储一些公共的状态,项目中只能有一个store 仓库,是和组件通信的桥梁。
- action 描述一个行为/动作,[对数据的具体的操作],值应为一个对象 {type:”类型”,payload:”要更新的数据”}
- reducer 是一个函数,接收 state 和 action, 计算返回新的 state
store.dispatch()
dispatch() 参数为对象 同步更新数据
// 分发行为
store.dispatch({type:"incre",payload:1});
store.dispatch({type:"decre",payload:2});
// 在老版
//封装 action 函数 返回 action 对象
//封装加方法
function incre(payload){
return {type:"incre",payload};
}
//封装减方法
function decre(payload){
return {type:"decre",payload};
}
// 分发行为
store.dispatch(incre(1));
store.dispatch(decre(2));
dispatch() 参数为函数 异步更新数据
//调用store方法
store.dispatch(() =>{
setTimeout(() => {
// 调用同步方法更新数据
store.dispatch(incre(payload));
}, 3000);
});
//在store 中封装方法
//创建异步加方法
export let asyncIncre = (payload) =>{
return () =>{
setTimeout(() => {
// 调用同步方法更新数据
store.dispatch(incre(payload));
}, 3000);
}
}
// 在组件中调用异步方法
store.dispatch(asyncIncre(3));
封装reducer
在 store 下创建 slices 目录 及 countSlice.js 文件
// 导入
import {createSlice} from "@reduxjs/toolkit"
// 创建切片对象
let countSlice = createSlice({
//数据别名
name:"count",
//为数据赋值,推荐值为对象
initialState:{
value:100
},
// action 描述一个行为/动作。值为一个对象 {type:"类型",payload:"要操作的新的数据" }
// { type:"打",payload:"张三" } {type:"打",payload:"酱油"} {type:"学习",payload:"react"}
// {type:"incre",payload:1} {type:"decre",payload:2}
// 配置 reducer 函数,也在创建 action 函数
reducers:{
//加
incre:function(state,action){
// 必须对数据进行直接更改
state.value += action.payload;
},
//减
decre(state,{payload}){
// 必须对数据进行直接更改
state.value -= payload;
}
}
});
// 提取action 方法
export let { incre , decre} = countSlice.actions;
//创建异步加方法
export let asyncIncre = (payload) =>{
return dispatch =>{
setTimeout(() => {
// 调用同步方法更新数据
dispatch(incre(payload));
}, 1000);
}
}
//创建异步减方法
export let asyncDecre = (payload) =>{
return dispatch =>{
setTimeout( () => {
// 调用同步方法更新数据
dispatch(decre(payload));
}, 1000);
}
}
//暴露
export default countSlice.reducer
在 store index.js 中导入并使用
// configureStore 创建 store 对象
import { configureStore } from "@reduxjs/toolkit"
import countReducer from "./slices/countSlice";
// 4. 创建仓库
let store = configureStore({
reducer:countReducer
});
//暴露store
export default store;
react-redux
安装
npm i react-redux --force
导入
// 1. 在 项目的入口文件
import {Provider} from "react-redux"
// 2. 将所有的组件 包裹在 Provider 组件中并传递store 数据
//渲染
ReactDOM.render(<Provider store={store}><BrowserRouter><App/></BrowserRouter></Provider>,document.getElementById('root'));
// 3. 在对应的组件中导入 两个hook
import {useSelector,useDispatch} from "react-redux"
//useSelector hook 获取到 store 仓库中的所有的状态
// useDispatch 能获取 到 dispatch 函数
// 4. 在函数式组件的内部 使用 useSelecter 来获取 store 所有 的数据
// 1. 形式
// let state = useSelector(state => state);
// 2. 形式
let {count,zan} = useSelector(state => state);
// 3. 形式
// let count = useSelector(state => state.count);
// 5. 直接使用数据
<h1> store中的数据是:{count.value}</h1>
<h1>当前点赞数:{zan.value} </h1>
// 6. 使用useDispatch 能获取 到 dispatch 函数
let dispatch = useDispatch();
// 7. 调用 store 中的action 方法来更新数据
//加
let add = () => {
dispatch(incre(1));
}
//减操作
let sub = () => {
// 调用store 中的 decre 方法
dispatch(decre(2));
}
//异步加
let asyncAdd = () => {
//调用store方法
dispatch(asyncIncre(3));
}
//异步jian
let asyncSub = () => {
//调用store方法
dispatch(asyncDecre(4));
}
w(゚Д゚)w
\( ̄︶ ̄*\))