前言笔记总结
1. setState
setState更新状态的2种写法
(1). setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
(2). setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,
要在第二个callback函数中读取
2. lazyLoad
路由组件的lazyLoad
//1.通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Login = lazy(()=>import('@/pages/Login'))
//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
<Suspense fallback={<h1>loading.....</h1>}>
<Switch>
<Route path="/xxx" component={Xxxx}/>
<Redirect to="/login"/>
</Switch>
</Suspense>
3. Hooks
1. React Hook/Hooks是什么?
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性
2. 三个常用的Hook
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()
3. State Hook
(1). State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
(2). 语法: const [xxx, setXxx] = React.useState(initValue)
(3). useState()说明:
参数: 第一次初始化指定的值在内部作缓存
返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
(4). setXxx()2种写法:
setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
4. Effect Hook
(1). Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
(2). React中的副作用操作:
发ajax请求数据获取
设置订阅 / 启动定时器
手动更改真实DOM
(3). 语法和说明:
useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
(4). 可以把 useEffect Hook 看做如下三个函数的组合
componentDidMount()
componentDidUpdate()
componentWillUnmount()
5. Ref Hook
(1). Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
(2). 语法: const refContainer = useRef()
(3). 作用:保存标签对象,功能与React.createRef()一样
4. Fragment
使用
<Fragment><Fragment>
<></>
作用
可以不用必须有一个真实的DOM根标签了
5. Context
理解
一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信
使用
1) 创建Context容器对象:
const XxxContext = React.createContext()
2) 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={数据}>
子组件
</xxxContext.Provider>
3) 后代组件读取数据:
//第一种方式:仅适用于类组件
static contextType = xxxContext // 声明接收context
this.context // 读取context中的value数据
//第二种方式: 函数组件与类组件都可以
<xxxContext.Consumer>
{
value => ( // value就是context中的value数据
要显示的内容
)
}
</xxxContext.Consumer>
注意
在应用开发中一般不用context, 一般都用它的封装react插件
6. 组件优化
Component的2个问题
只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低
效率高的做法
只有当组件的state或props数据发生改变时才重新render()
原因
Component中的shouldComponentUpdate()总是返回true
解决
办法1:
重写shouldComponentUpdate()方法
比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false
办法2:
使用PureComponent
PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
注意:
只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false
不要直接修改state数据, 而是要产生新数据
项目中一般使用PureComponent来优化
7. render props
如何向组件内部动态传入带内容的结构(标签)?
Vue中:
使用slot技术, 也就是通过组件标签体传入结构 <A><B/></A>
React中:
使用children props: 通过组件标签体传入结构
使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
children props
<A>
<B>xxxx</B>
</A>
{this.props.children}
问题: 如果B组件需要A组件内的数据, ==> 做不到
render props
<A render={(data) => <C data={data}></C>}></A>
A组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
8. 错误边界
理解:
错误边界(Error boundary):用来捕获后代组件错误,渲染出备用页面
特点:
只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误
使用方式:
getDerivedStateFromError配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}
9. 组件通信方式总结
组件间的关系:
- 父子组件
- 兄弟组件(非嵌套组件)
- 祖孙组件(跨级组件)
几种通信方式:
1.props:
(1).children props
(2).render props
2.消息订阅-发布:
pubs-sub、event等等
3.集中式管理:
redux、dva等等
4.conText:
生产者-消费者模式
比较好的搭配方式:
父子组件:props
兄弟组件:消息订阅-发布、集中式管理
祖孙组件(跨级组件):消息订阅-发布、集中式管理、conText(开发用的少,封装插件用的多)
- XML早期用于存储和传输数据
- js里表达jsx
//html
<div id="test></div>
//JS
.创建虚拟DOM
const VDOM=(
<h2>
<span> helloword </span>
</h2>
)
.渲染虚拟DOM到页面
ReactDom.react(VDOM,document,getElementById('text'))
- JSX语法规则
- 定义虚拟dom时,不需要写引号
-标签中使用 JS表达式 要用花括号 { }
-样式类名用 className
-内联样式格式 style={{key:value}}
写内联样式 例如:<div style={{ color:'white',fontSize:'16px' }}"></div>
-虚拟dom必须只有一个根标签
-标签必须闭合
-标签首字母要小写,大写的 是组件标签
-JSX 小练习
动态的表达
.hello
.react
.记笔记
//html
<div id="test></div>
//js
const VDOM =(
<h2>
<ul>
<li>hello <li>
<li>react <li>
<li>记笔记 <li>
</ul>
</h2>
)
ReactDOM.render(VDOM,document.getElementById('test'))
//模拟一些数据
const data=['hello','react','记笔记']
//
const VDOM =(
<h2>
<ul>
{data.map((item,index)=>{
return <li key={index}> {item}</li>
})
}
</ul>
</h2>
)
ReactDOM.render(VDOM,document.getElementById('test'))
React面向组件编程
类 要会
<!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>Document</title>
</head>
<body>
<script>
/***
* constructor不是必须写的。对实例有操作的时候就可以写,例如添加属性可以写
* super().如果A类继承了B类,且B类已经写了构造器,那么A类中的super()必须调用
* 类中定义的方法都放在了原形对象上,供实例去使用
* **/
// 1,创建一个类
class person{
// 3,构造器
constructor(name,hoby){
// 构造器的this是类的实例对象
this.name =name;
this.hoby =hoby;
}
// 一般方法
speak(){
// speak的方法放在类的原型对象上,供实例使用
//通过person调用speak,speak的this就是类的实例//this的指向问题
console.log(`我的名字${this.name},我最喜欢${this.hoby}了`);
}
}
// 4,创建于一个Student类。继承person
class Student extends person{
// constructor在什么时候写?-
constructor(name,hoby,age){
super(name,hoby)//调用
this.age="100"
}
speak(){
console.log(`我的名字${this.name},我最喜欢${this.hoby}了,我们能活到${this.age}岁`);
//原型链查找规则,查找到第一个就停止了
}
}
//5,
var s1= new Student('小乌龟','拉屎')
console.log(s1);
s1.speak()
// 2,创建 person的实例对象
const girl = new person('杜秀秀','学习');
const dog = new person('周锋','吃粑粑')
console.log(girl);
console.log(dog);
girl.speak()
dog.speak()
// girl.speak().call({a:1}) //undefined call会改变this的指向,apply
</script>
</body>
</html>
- react的类解析
class myComponent extends React.component{
// render 放在myComponent的原型对象上。供实例使用
// render 的this是 myComponent的实例对象
render(){
console.log('render()中的实例对象:',this);
return <div></div>
}
}
// 渲染到页面
ReactDOM.render(<mycomponent/>,document.getElementById('test'))
// 做了哪些事
// 1,react去找到组件
// 2,发现组件是类定义的,随后new出来该类的实例,并通过该实例调用到原形render()方法。
// 3,将rende()返回的虚拟dom转化成真实的dom,呈现在页面上
10,组件三大状态 (状态里存储着数据)
1. state
- 组件中的render() 中的this是组件的实例对象
- 组件中的this为undefined,如何解决?
1)强制绑定this.通过 bind()
2) 箭头函数
- 状态数据不能更改或直接更新?
this.setState()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>state</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
// 练习react 点击切换
class Foods extends React.Component{
state ={banana:true}
render(){
const { banana } = this.state;
return <h1 onClick={this.getFoods}>今晚吃{banana?'香蕉':'葡萄'}</h1>
}
//自定义方法 用箭头函数。箭头函数没有自身的this.
getFoods=()=>{
const banana = this.state.banana//this.是类 Foods的
this.setState({ banana :!banana})
}
}
ReactDOM.render(<Foods/>,document.getElementById('test'))
</script>
</body>
</html>
2,props
对标签进行限制
Person.propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
speak:PropTypes.func,//限制speak为函数
}
//指定默认标签属性值
Person.defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>对props进行限制</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test1"></div>
<div id="test2"></div>
<div id="test3"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
//创建组件
class Person extends React.Component{
constructor(props){
//构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props
// console.log(props);
super(props)
console.log('constructor',this.props);
}
//对标签属性进行类型、必要性的限制
static propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
age:PropTypes.number,//限制age为数值
}
//指定默认标签属性值
static defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}
render(){
// console.log(this);
const {name,age,sex} = this.props
//props是只读的
//this.props.name = 'jack' //此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
</script>
</body>
</html>
关键词
propTypes
…
defaultProps
3,refs
1,字符串形式的ref 不推荐使用
2,createRef() 创建的 ref 属性
<!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> createRef() 创建的 ref 属性</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点我左侧提示" />
<button ref='btn1' onClick={this.showbtn}>按钮</button>
<input ref={this.myOnblur} type="text" placeholder="点我失去焦点提示" onBlur={this.blurBtn}/>
<button ref='btn2'>按钮</button>
</div>
)
}
// /****/ 把ref理解成打标识
// React.createRef调用后可以返回一个容器,该容器存储被ref所标识的节点,该容器是“专人专用"
myRef = React.createRef()
myOnblur = React.createRef()
showbtn = ()=>{
console.log('showbtn:',this);
alert(this.myRef.current.value)
}
blurBtn=()=>{
console.log(this)
alert(this.myOnblur.current.value)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
<style>
.mr{
margin-right: 10px;
}
</style>
</body>
</html>
官方文档:
ref 回调以内联函数的方式定义,在更新期间会被调用两次,第一次参数是 null ,之后参数是 DOM 元素。这是因为在每次渲染中都会创建一个新的函数实例。因此,React 需要清理旧的 ref 并且设置新的。
-ref不推荐频繁使用
通过event.target得到发生dom的元素对象-不用使用ref
在这里插入代码片
<!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> createRef() 创建的 ref 属性</title>
</head>
<body>
<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
class Demo extends React.Component{
render() {
return (
<div>
<input ref={this.myRef} type="text" placeholder="点我左侧提示" />
<button ref='btn1' onClick={this.showbtn}>按钮</button>
<input type="text" placeholder="点我失去焦点提示" onBlur={this.blurBtn}/>
<button ref='btn2'>按钮</button>
</div>
)
}
// /****/ 把ref理解成打标识
// React.createRef调用后可以返回一个容器,该容器存储被ref所标识的节点,该容器是“专人专用"
myRef = React.createRef()
myOnblur = React.createRef()
showbtn = ()=>{
console.log('showbtn:',this);
alert(this.myRef.current.value)
}
blurBtn=(event)=>{
console.log(event.target)
alert(event.target.value)
}
}
ReactDOM.render(<Demo/>,document.getElementById('test'))
</script>
<style>
.mr{
margin-right: 10px;
}
</style>
</body>
</html>
11,生命周期
1,生命周期旧
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount() =====> 常用
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() =====> 必须使用的一个
4. componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息