React 基础

250 阅读6分钟

1 React 入门

1 React 简介

截屏2021-08-26 16.41.16.png

截屏2021-08-26 16.45.10.png

截屏2021-08-26 16.49.09.png

截屏2021-08-26 16.51.25.png

截屏2021-08-26 16.53.49.png

2 hello_React.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>hello_react</title>
</head>
<body>
<!--1 准备容器-->
<div id="test"></div>

<!--2-1 引入React 核心库-->
<script type="text/javascript" src="../js/react.development.js"></script>
<!--2-2 引入React-dom. 用于支持React操作DOM-->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!--2-3 引入 babel. 将jsx转为js-->
<script type="text/javascript" src="../js/babel.min.js"></script>

<script type="text/babel">/* 此处一定要写babel,因为我们写jsx */
  //  3-1 创建虚拟dom
  const VDOM = <h1>hello,react</h1>/*这里不用写引号,因为是jsx,不是字符串*/
  //  3-2 渲染虚拟dom 到页面/* ReactDOM.render(虚拟dom,容器) */
  ReactDOM.render(VDOM,document.getElementById('test'))

</script>

</body>
</html>

3 虚拟dom与真实dom

关于虚拟dom:

  • 1 本质是Object 类型对象(一般对象)
  • 2 虚拟dom 轻,真实dom重,因为虚拟dom是React内部在用,无需真实dom上那么多的属性
  • 3 虚拟dom最终会被React转换为真实dom,呈现在界面上
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>虚拟dom与真实dom</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" > /* 此处一定要写babel */
  //1.创建虚拟DOM
  const VDOM =(/* 此处一定不要写引号,因为不是字符串 */
    <h1 id="title">
     <span>Hello,React</span>
    </h1>)
  //2.渲染虚拟DOM到页面
  ReactDOM.render(VDOM,document.getElementById('test'))
  console.log('虚拟dom',VDOM)

  const TDOM = document.getElementById('demo')
  console.log('真实dom',TDOM)
  // debugger

 // console.log(typeof VDOM)
 // console.log(VDOM instanceof Object )


 </script>
</body>
</html>

4 jsx语法规则

json 存储方法好用

  • parse ::将json字符串解析为js的对象、数组
  • stringfy :将js的对象、数组解析为json字符串

jsx语法规则:


1 这里不用写引号,因为是jsx,不是字符串
2 标签中混入js表达式用{}
3 样式的类名指定为className
4 内联样式,用style={{key:value}}
5 只有一个根标签
6 标签必须闭合
7 标签首字母
  - 如小写字母开头,这jsx转为html,若html标签无对应,则报错
  - 若大写字母开头,则React就去渲染对应的组件,若组件没有定义,就保存
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title> jsx语法规则</title>
 <style>
  .title{
   background-color: burlywood;
   width: 200px;
  }
 </style>
</head>
<body>
<!--1 准备容器-->
<div id="test" ></div>

<!--2-1 引入React 核心库-->
<script type="text/javascript" src="../../js/react.development.js"></script>
<!--2-2 引入React-dom. 用于支持React操作DOM-->
<script type="text/javascript" src="../../js/react-dom.development.js"></script>
<!--2-3 引入 babel. 将jsx转为js-->
<script type="text/javascript" src="../../js/babel.min.js"></script>

<script type="text/babel">/* 此处一定要写babel,因为我们写jsx */
const myId = 'JulY'
const myData = 'heLLO, reAct'

//  3-1 创建虚拟dom
const VDOM = (
 <div>
 <h2 className="title" id={myId.toLowerCase()}>
  <span style={{color:'pink',fontsize:'29px'}}> {myData.toUpperCase()}</span>
 </h2>
 <h2 className="title" id={myId.toLowerCase()}>
  <span style={{color:'pink',fontsize:'29px'}}> {myData.toUpperCase()}</span>
 </h2>
  <input type="text"/>
 </div>
)
//  3-2 渲染虚拟dom 到页面/* ReactDOM.render(虚拟dom,容器) */
ReactDOM.render(VDOM,document.getElementById('test'))

</script>

</body>
</html>

截屏2021-08-27 14.55.55.png

5 js表达式 vs js语句

注意区分 : js表达式 vs js语句

  • 下面都是表达式:
    • a
    • a+b
    • demo(1)
    • arr.map()
    • function test(){}
  • 下面是语句:
    • for(){}
    • if(){}
    • switch(){}
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>jsx小练习</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" >


  //3 模拟一些数据
  const data = ['Angular','react','vue']
 // 1 创建虚拟dom
  const  VDOM =(
   <div>
    <h1>前端js框架列表</h1>
    <ul>
     {
     //4  js表达式
      data.map((item,index)=>{
       return <li key={index}>{item}</li>
      })
     }
    </ul>
   </div>
  )
 // 2 渲染虚拟dom到页面
  ReactDOM.render(VDOM, document.getElementById('test'))
 </script>
</body>
</html>

截屏2021-08-27 15.17.52.png

2 React 面向组件化编程

1 函数式组件

执行了 ReactDOM.render(....后,发生了什么?

  • 1 React 解析组件标签,找到MyComponent组件
  • 2 发现组件是函数式定义的,随后调用该函数,将返回的虚拟dom转为真实dom,随后呈现在页面中
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>1_函数式组件</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">
  // 1 函数函数式组件
  function MyComponet() {
   console.log(this)//此处this 是undefined,因为babel编译后开启了严格模式
   return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
  }
  //2 渲染组件到页面
  ReactDOM.render(<MyComponet/>, document.getElementById('test'))

  /*

  * */
 </script>
</body>
</html>

截屏2021-08-27 15.45.45.png

1.5 class 复习

  • 类中的构造器不是必须写,要对实例进行一些初始化操作,如添加特定属性
  • 如果A类 extend B类,且A类中写了构造器,那么A类构造器中的super()必须调用
  • 类中所定义的方法,都是放在类的原型对象上,供实例使用

//1 创建一个Person的类
class Person {
//3 构造器方法
  constructor(name,age) {
    // 构造器中this是 ——类的实例对象
    this.name = name
    this.age = age
  }
  //一般方法
  say(){
    //say方法放在--类的原型对象上,给实例用
    //通过Person实例调用say(),say中this就是Person实例
    console.log(`我叫${this.name},年龄是${this.age}`)
  }
}
//2 创建一个Person的实例对象
const p1 = new Person('tom',12)
const p2 = new Person('lily',14)
console.log(p1)//Person { name: 'tom', age: 12 }
console.log(p2)//Person { name: 'lily', age: 14 }

p1.say()//我叫tom,年龄是12
p2.say()//我叫lily,年龄是14

p1.say.call({a:1,b:2})//call apply 等方法可以更改this指向,我叫undefined,年龄是undefined

// 4 创建一个Student类,继承 与Person类
class  Student extends Person{
  constructor(name ,age,grade) {
    super(name,age);
    this.grade = grade
  }
  say() {
    //重写继承的say方法
    console.log(`我叫${this.name},年龄是${this.age},年级是${this.grade}`)
  }
  study(){
    //study方法放在--类的原型对象上,给实例用
    //通过Student实例调用study(),study中this就是Student实例
    console.log('study hard')
  }
}

const s1 = new Student('sam',15,'高一')
console.log(s1)//Student { name: 'sam', age: 15, grade: '高一' }
s1.say()//我叫sam,年龄是15,年级是高一

2 类式组件

  • 执行了 ReactDOM.render(....后,发生了什么?
    • 1 React 解析组件标签,找到MyComponent组件

    • 2 发现组件是类定义的,随后new出来该类实例,并通过该实例调用到原型上的render方法

    • 3 将返回的虚拟dom转为真实dom,随后呈现在页面中

  • render方法放在了哪里?——MyCompnont的原型对象上,供实例使用

  • render 中this是?——MyCompnont的实例对象==MyCompnont组件实例对象

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>2_类式组件</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">
 // 1 创建类式组件
 class MyCompnont extends React.Component{
  render() {
   //render方法放在了哪里?——MyCompnont的原型对象上,供实例使用
   //render 中this是?——MyCompnont的实例对象==MyCompnont组件实例对象
   console.log('render中this是:',this)//render中this是: MyCompnont

   return (
    <h2>我是用函数定义的组件(适用于【复杂组件】的定义)</h2>
   );
  }
 }
  //2 渲染组件到页面
  ReactDOM.render(<MyCompnont/>, document.getElementById('test'))
 /*
  * 执行了   ReactDOM.render(<MyComponet/>....后,发生了什么?
  * 1 React 解析组件标签,找到MyComponent组件
  * 2 发现组件是类定义的,随后new出来该类实例,并通过该实例调用到原型上的render方法
  * 3 将返回的虚拟dom转为真实dom,随后呈现在页面中
  * */

 </script>
</body>
</html>

3 state --组件实例的三大核心属性之一

state是组件对象最重要的属性,值是对象

组件被称为状态机,通过更新组件的state,来重新渲染组件

    • 组件中render方法中this 为组件实例对象
    • 组件自定义方法中this 为 undefined,用bind()强制绑定this;or 箭头函数 + 赋值语句
    • 状态数据不能直接更新,用 setState
  • 原生事件绑定的三种方式

<!doctype html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <button id="btn1">1</button>
  <button id="btn2">2</button>
  <button onclick="demo()">3</button><!--   way 3,推荐使用-->

  <script type="text/javascript">

  // way 1
  const btn1 = document.getElementById('btn1')
  btn1.addEventListener('click',()=>{
    alert('按钮1被点击')
  })

  // way 2
  const btn2 = document.getElementById('btn2')
  btn1.onclick=()=>{
    alert('按钮2被点击')
  }

  // way 3,推荐使用
  function  demo(){
    alert('按钮3被点击')
  }

</script>


</body>
</html>

React事件绑定的方法

  • step 1 *这里注意 onClick 和{changeWeather}
    • 点击事件 在js中是onclick;在React中是onClick
    • {changeWeather}里面不能写{changeWeather()}这里changeWeather加了()就是回调了changeWeather,这里只需要有此函数即可,回调在点击后才显示
<!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">
  let that
  //1 创建组件
  class Weather extends React.Component{
   //1-1 构造器
   constructor(props) {
    super(props);
    //1-2初始化状态
    this.state = {isHot:false}
    that = this//1-3缓存this
   }
   render() {
    //1-3 读取状态
    const {isHot} = this.state
    console.log(this)//Render 这里的this就是weather的实例对象
    return (
     // <h1 id="title">今天天气很{isHot?'热':'凉'}</h1>
    <h1 onClick={changeWeather}>今天天气很{isHot?'热':'凉'}</h1>
   

   );
   }
  }
  //2 渲染组件
  ReactDOM.render(<Weather/>, document.getElementById('test'))
  // 2-1 添加点击事件
  //way 2-1-3
  function changeWeather() {
   console.log('此处修改isHot')
   // const {isHot} = this.state.isHot//这里的this是undefined,因为babel是「use strict」严格模式,识别这里是独立的函数
   console.log(that.state.isHot)

  }

  }

 </script>
</body>
</html>

类方法中的this: 类中开了局部的严格模式,this是undefined

 class Person{
   constructor(name,age) {
    this.name = name
  this.age = age
 }
 speak(){
  //speak 放在?  Person 的原型对象上,供实例使用
  //通过 Person 实例调用speak时,speak中的this就是 Person 的实例
  console.log(this)
 }
}
const p1 = new Person('tom',12)
p1.speak()//通过实例调用speak
const x = p1.speak
x()// ,此处是函数直接调用speak
// undefined 类中开了局部的严格模式,this就是undefined
  • step 2
<!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">
  //1 创建组件
  class Weather extends React.Component {
   //1-1 构造器
   constructor(props) {
    super(props);
    //1-2初始化状态
    this.state = {isHot: false}
   }
   render() {
    //1-3 读取状态
    const {isHot} = this.state//Render 这里的this就是weather的实例对象
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '热' : '凉'}</h1>
   }
   // 2-1 添加点击事件
   changeWeather() {
    //changeWeather 放在?  weather的原型对象上,供实例使用
    //通过Weather实例调用changeWeather时,changeWeather中的this就是Weather的实例
    console.log(this.state.isHot)
   }
  }
  //2 渲染组件
  ReactDOM.render(<Weather/>, document.getElementById('test'))




 </script>
</body>
</html>
  • step 3
<!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">
  //1 创建组件
  class Weather extends React.Component {
   //1-1 构造器
   constructor(props) {
    super(props);
    //1-2初始化状态
    this.state = {isHot: false}
   }
   render() {
    //1-3 读取状态
    const {isHot} = this.state//Render 这里的this就是weather的实例对象
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '热' : '凉'}</h1>
   }
   // 2-1 添加点击事件
   changeWeather() {
    //changeWeather 放在?  weather的原型对象上,供实例使用
    //由于 changeWeather 是作为onClick的回调,所以不是通过实例调用的,是直接调用,
    // 类中方法默认开启局部的 严格模式,所以 changeWeather 中的this是undefined
    console.log(this)
   }
  }
  //2 渲染组件
  ReactDOM.render(<Weather/>, document.getElementById('test'))




 </script>
</body>
</html>

解决类中this指向问题

<!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">
  //1 创建组件
  class Weather extends React.Component {
   //1-1 构造器
   constructor(props) {
    super(props);
    //1-2初始化状态
    this.state = {isHot: false}
    //解决 changeWeather this 指向问题
    this.changeWeather = this.changeWeather.bind(this)//bind可以 1 生成新函数; 2 改变this指向
   }
   render() {
    //1-3 读取状态
    const {isHot} = this.state//Render 这里的this就是weather的实例对象
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '热' : '凉'}</h1>
   }
   // 2-1 添加点击事件
   changeWeather() {
    //changeWeather 放在?  weather的原型对象上,供实例使用
    //由于 changeWeather 是作为onClick的回调,所以不是通过实例调用的,是直接调用,
    // 类中方法默认开启局部的 严格模式,所以 changeWeather 中的this是undefined
    console.log(this)
   }
  }
  //2 渲染组件
  ReactDOM.render(<Weather/>, document.getElementById('test'))




 </script>
</body>
</html>

截屏2021-08-31 09.47.28.png

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">
  //1 创建组件
  class Weather extends React.Component {
   //1-1 构造器 ,调用几次? ———— 1次
   constructor(props) {
    super(props);
    //1-2初始化状态
    this.state = {isHot: false,wind:'微风'}
    //解决 changeWeather this 指向问题
    this.changeWeather = this.changeWeather.bind(this)//bind可以 1 生成新函数; 2 改变this指向
   }
   //rander 调用几次? 1+n 次,1 是 初始化那次, n 是更新次数
   render() {
    //1-3 读取状态
    const {isHot,wind} = this.state//Render 这里的this就是weather的实例对象
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '热' : '凉'},{wind}</h1>
   }

   // 1-4 添加点击事件
   //changeWeather 调用几次? 点击几次调用几次
   changeWeather() {
    //changeWeather 放在?  weather的原型对象上,供实例使用
    //由于 changeWeather 是作为onClick的回调,所以不是通过实例调用的,是直接调用,
    // 类中方法默认开启局部的 严格模式,所以 changeWeather 中的this是undefined

   //获取原来的isHot值
    const isHot = this.state.isHot
    this.setState({isHot:!isHot})// !!! state 通过 setState 更改,且更新是同名更改,合并,不是替换

    // this.state.isHot = !isHot    // !!! : state 不可以直接更改,这句是错误的,是直接更改
   }
  }

  //2 渲染组件ReactDOM.render(<Weather/>, document.getElementById('test'))
  


 </script>
</body>
</html>

state的简写方式(开发)

<!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">
 //1 创建组件
 class Weather extends React.Component {
  //初始化状态
  state = {isHot: false,wind:'微风'}

  render() {
   // 读取状态
   const {isHot,wind} = this.state
   return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '热' : '凉'},{wind}</h1>
  }
  
  //自定义方法—— 用 赋值语句的形式 + 箭头函数
  changeWeather = () =>{//箭头函数没有自己的this,是外侧的this,即 Weather 的实例对象
   const isHot = this.state.isHot
   this.setState({isHot:!isHot})
  }
 }

 //2 渲染组件到页面
 ReactDOM.render(<Weather/>, document.getElementById('test'))


</script>
</body>
</html>

4 props --组件实例的三大核心属性之一

props 是通过标签属性从组件外部相组件内部传递 变化的数据

注意 组件内部不要修改props数据

props 基本使用

<!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>

 <script type="text/babel">
  //创建组件

  class Person extends React.Component {
   render() {
    const {name,age,gender} = this.props//解构赋值
    return (
     <ul>
      <li>姓名:{name}</li>
      <li>性别:{gender}</li>
      <li>年龄:{age}</li>
     </ul>
    )
   }
  }

  //渲染组件到页面
  ReactDOM.render(<Person name='tom' age='17' gender='man'/>, document.getElementById('test1'))

 </script>

</body>
</html>

...展开运算符(复习)

用法:

  • 展开一个数组
  • 连接两个数组
  • 函数传参
  • 复制对象(构造字面量时使用展开语法)
  • 负责对象同时修改属性,合并 注:let clonePerson = {...Person}//可以克隆一个对象,字面量对象,注意不能与React中{...p}相提并论,react里即...p
 let arr1 = [1,3,4,5,6]
let arr2 = [2,3,57,8,9]
//展开一个数组
console.log(...arr1)//1 3 4 5 6

//连接数组
let arr3 = [...arr1,...arr2]
console.log(arr3)//(10) [1, 3, 4, 5, 6, 2, 3, 57, 8, 9]

//函数传参
function sum(...args) {
 return args.reduce((pre,cur)=>{
  return pre + cur
 })
}
console.log(sum(1,23,4))

//构造字面量时使用展开语法
let Person = {name:'sam',age:'24'}
// console.log(...Person)//报错,展开运算符不能展开对象
let clonePerson  = {...Person}//可以克隆一个对象,字面量对象,注意不能与React中{...p}相提并论,react里即...p
console.log(clonePerson)

//负责对象同时修改属性,合并
let person3 = {...Person,name:'jack',address:'house'}
console.log(person3)

批量传递props

<!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>

 <script type="text/babel">
  //创建组件

  class Person extends React.Component {
   render() {
    const {name,age,gender} = this.props//解构赋值
    return (
     <ul>
      <li>姓名:{name}</li>
      <li>性别:{gender}</li>
      <li>年龄:{age}</li>
     </ul>
    )
   }
  }

  //渲染组件到页面
  ReactDOM.render(<Person name='tom' age='17' gender='man'/>, document.getElementById('test1'))
  ReactDOM.render(<Person name='sam' age='18' gender='man'/>, document.getElementById('test2'))

  const p = {name:'lily',age:'19',gender:'female'}
  console.log('@',...p)
  ReactDOM.render(<Person{...p}/>, document.getElementById('test3'))//展开运算符... 不能展开对象

 </script>

</body>
</html>

截屏2021-08-31 12.31.05.png

对props 进行限制

this.props.name = 'jack' //会报错 props 是只读的

<!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 {
   render() {
    const {name,age,gender} = this.props//解构赋值
    return (
     <ul>
      <li>姓名:{name}</li>
      <li>性别:{gender}</li>
      <li>年龄:{age+1}</li>
     </ul>
    )
   }
  }
  //对标签属性进行类型和必要性的限制  Person.属性规则
  Person.propTypes = {
   name:PropTypes.string.isRequired,//注意这里string,是小写的
   gender:PropTypes.string,//限制性别是字符串
   age:PropTypes.number,
   speak:PropTypes.func //限制speak是函数,

  }
  //指定默认标签属性值
  Person.defaultProps = {
   gender:'不告知',
   age:18
  }


  //渲染组件到页面
  ReactDOM.render(<Person name='tom' age={17} gender='man'/>, document.getElementById('test1'))
  ReactDOM.render(<Person name='sam'  speak={speak} gender='man'/>, document.getElementById('test2'))

  const p = {name:'lily',age:19,gender:'female'}
  ReactDOM.render(<Person{...p}/>, document.getElementById('test3'))//展开运算符... 不能展开对象

  function speak() {
   console.log('ok')
  }
 </script>
</body>
</html>

props简写

<!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 {
   static propTypes = {
    name:PropTypes.string.isRequired,//注意这里string,是小写的
    gender:PropTypes.string,//限制性别是字符串
    age:PropTypes.number,
    speak:PropTypes.func //限制speak是函数,

   }
   //指定默认标签属性值
   static defaultProps = {
    gender:'male',
    age:18
   }

   render() {
    const {name,age,gender} = this.props//解构赋值

      return (
     <ul>
      <li>姓名:{name}</li>
      <li>性别:{gender}</li>
      <li>年龄:{age+1}</li>
     </ul>
    )
   }
  }
  //对标签属性进行类型和必要性的限制  Person.属性规则



  //渲染组件到页面
  ReactDOM.render(<Person name='tom' age={17} gender='man'/>, document.getElementById('test1'))
  ReactDOM.render(<Person name='sam'  speak={speak} gender='man'/>, document.getElementById('test2'))

  const p = {name:'lily',age:19,gender:'female'}
  ReactDOM.render(<Person{...p}/>, document.getElementById('test3'))//展开运算符... 不能展开对象

  function speak() {
   console.log('ok')
  }
 </script>
</body>
</html>

类式组件中的构造器与props

  • construct(props) 通常在React中 construct函数有两种情况:
    • 1 通过给this。state赋值对象来初始化内部state
    • 2 为事件处理函数绑定实例

函数式组件使用props

由于函数本身能够穿参数,因此可以传props

但函数式组件只能使用props ,不能使用state 和 refs

<!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">
 //创建组件
 function Person(props) {

  const {name,age,gender} = props
  return (
    <ul>
     <li>姓名:{name}</li>
     <li>性别:{gender}</li>
     <li>年龄:{age}</li>
    </ul>
   )
 }
 Person.propTypes = {
  name:PropTypes.string.isRequired, //限制name必传,且为字符串
  gender:PropTypes.string,//限制sex为字符串
  age:PropTypes.number,//限制age为数值
 }

 //指定默认标签属性值
 Person.defaultProps = {
  gender:'男',//sex默认值为男
  age:18 //age默认值为18
 }
 //渲染组件到页面
 ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))
</script>
</body>
</html>

5 refs与事件处理 --组件实例的三大核心属性之一

相当于原始js 里的 id 就是打标签

字符串形式的ref(不推荐)

效率不高哦,已过时

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>1_字符串形式的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/babel">
  //创建组件
  class Demo extends React.Component {
   //展示左侧输入框数据
   showData = ()=>{
    const {input1} = this.refs
    alert(input1.value)
   }
   //展示右侧输入框数据
   showData2 = ()=>{
    //函数体
    const {input2} = this.refs
    alert(input2.value)
   }
   render() {
    return (
     <div>
      <input ref='input1' type="text" placeholder="点击按钮提示数据"/> &nbsp;
      <button onClick={this.showData}>点我提示左侧数据</button> &nbsp;
      <input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
     </div>
    )
   }
  }
  //渲染组件到页面
  ReactDOM.render(<Demo a="1" b="2" />, document.getElementById('test'))
 </script>
</body>
</html>

回调形式的ref

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>1_字符串形式的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/babel">
  //创建组件
  class Demo extends React.Component {
   //展示左侧输入框数据
   showData = ()=>{
    const {input1} = this
    alert(input1.value)
   }
   //展示右侧输入框数据
   showData2 = ()=>{
    //函数体
    const {input2} = this
    alert(input2.value)
   }
   render() {
    return (
     <div>
      <input ref={currentNode=>this.input1 = currentNode} type="text" placeholder="点击按钮提示数据"/> &nbsp;
      <button onClick={this.showData}>点我提示左侧数据</button> &nbsp;
      <input onBlur={this.showData2} ref={currentNode=>this.input2 = currentNode} type="text" placeholder="失去焦点提示数据"/> &nbsp;

     </div>
    )
   }
  }
  //渲染组件到页面
  ReactDOM.render(<Demo a="1" b="2" />, document.getElementById('test'))
 </script>
</body>
</html>

回调ref中回调执行次数的问题

回调ref以 内联函数的方式展示,在更新的过程中会被执行 2 次,第一传入的是 Null ,第二次传入的参数是 DOM 元素(这个可以有)

用 class 方法

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>3_回调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/babel">
  //创建组件
  class Demo extends React.Component {
   state = {isHot:false}
   changeWeather = ()=>{
    //获取原来的状态
    const {}  = this.state
    //更新状态
    this.state({isHot :!isHot })
   }


   showInfo = ()=>{
    const {input1} =this
    alert(input1.value)
   }

   saveInput = (c)=>{
    this.input1 = c
    console.log('@',c)
   }

   render() {
    const {isHot} = this.state
    return (
     <div>
      <h2>今天天气很{isHot ? 'hot':'cold'}</h2>
      {/*<input ref={c => {this.input1=c;console.log('@',c)}} type="text"/><br/><br/>*/}
      <input ref={this.saveInput} type="text"/>
      <button onClick={this.showInfo}>点我显示输入的数据</button> <br/><br/>
      <button onClick={this.changeWeather}>点我切换天气</button>
     </div>
    )
   }
  }

  //渲染组件到页面
  ReactDOM.render(<Demo/>,document.getElementById('test'))
 </script>
</body>
</html>

createRef

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>4_createRef</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">
  //创建组件
  class Demo extends React.Component {
   /*React。createRef()调用后可以返回一个容器,该容器可以存储被ref所标识的节点*/
   myRef1 = React.createRef()
   //展示左侧输入框数据
   showData = ()=>{
    alert(this.myRef1.current.value)
   }

   render() {
    return (
     <div>
      <input ref={this.myRef1} type="text" placeholder="点击按钮提示数据"/> &nbsp;
      <button onClick={this.showData}>点我提示左侧数据</button> &nbsp;
     </div>
    )
   }
  }
  //渲染组件到页面
  ReactDOM.render(<Demo a="1" b="2" />, document.getElementById('test'))
 </script>
</body>
</html>

6 React事件处理

  • (1).通过onXxx属性指定事件处理函数(注意大小写)

    • a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
    • b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效(冒泡)
  • (2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref

  • 不要过度使用ref:当 发送事件是 自己 要操作的事件,可以不用ref ,用event.target.value

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>事件处理</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">
  //创建组件
  class Demo extends React.Component{
   /* 
    (1).通过onXxx属性指定事件处理函数(注意大小写)
      a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
      b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
    (2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
    */
   //创建ref容器
   myRef = React.createRef()
   myRef2 = React.createRef()

   //展示左侧输入框的数据
   showData = (event)=>{
    alert(this.myRef.current.value);
   }

   //展示右侧输入框的数据
   showData2 = (event)=>{
    alert(event.target.value);
   }

   render(){
    return(
     <div>
      <input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
      <button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
      <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
     </div>
    )
   }
  }
  //渲染组件到页面
  ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
 </script>
</body>
</html>

7 React 收集表单数据

非受控组件

页面中所有输入型的组件,现用现取的,就是非受控组件

截屏2021-09-03 17.45.30.png

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>1_非受控组件</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">
  //创建组件
  class Login extends React.Component {
   handleSubmit = (event)=>{
    event.preventDefault()//阻止表单提交
    const {username,password} = this
    alert(`用户名是${username.value},密码是${password.value}`)
   }
   render() {
    return (
     <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
      用户名:<input ref={c => this.username =c} type="text" name="username"/><br/>
      密码: <input ref={c => this.password =c}  type="text" name="password"/><br/>
      <button>登录</button>
     </form>
    )
   }
  }
  //渲染组件
  ReactDOM.render(<Login/>, document.getElementById('test'))
 </script>
</body>
</html>

受控组件--推荐使用

页面中所有输入型的组件,维护状态到setState(即vue中的双向绑定), 去掉了ref

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>2_受控组件</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">
  //创建组件
  class Login extends React.Component {
   //初始化状态
   state = {
    username: '',
    password:''
   }
   //保存用户名到状态中
   saveUsername = (event)=>{
    this.setState({username:event.target.value})
   }
   //保存密码到状态中
   savePassword = (event)=>{
    this.setState({password:event.target.value})
   }

   handleSubmit = (event)=>{
    event.preventDefault()//阻止表单提交
    const {username,password} = this.state
    alert(`用户名是${username},密码是${password}`)
   }
   
   render() {
    return (
     <form action="http://www.atguigu.com" onSubmit={this.handleSubmit}>
      用户名:<input onChange={this.saveUsername} type="text" name="username"/><br/>
      密码: <input onChange={this.savePassword} type="text" name="password"/><br/>
      <button>登录</button>
     </form>
    )
   }
  }
  //渲染组件
  ReactDOM.render(<Login/>, document.getElementById('test'))
 </script>
</body>
</html>

8 组件的生命周期

生命周期回调函数 、生命周期钩子函数、生命周期函数、生命周期钩子

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>1_引出生命周期</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">
  //创建组件
  //生命周期回调函数 、生命周期钩子函数、生命周期函数、生命周期钩子
  class Life extends React.Component {
   state = {opacity:1}

   blank = ()=>{
    //卸载组件
    ReactDOM.unmountComponentAtNode(document.getElementById('test'))
   }

   // componentDidMount 调用时机:组件挂载完毕(只调用一次)
   componentDidMount(){
    this.timer = setInterval(()=>{
     //初始化 透明度
     let {opacity} = this.state
     // 初始化减少0.1
     opacity -= 0.1
     if (opacity <= 0) {
      opacity = 1
     }
     //设置新的透明度
     this.setState ({opacity})
    },200)

   }

   // 组件将要卸载 componentWillUnmount
   componentWillUnmount(){
    //清除定时器
    clearInterval(this.timer)
   }

   // render 调用时机:初始化渲染,状态更新之后
   render() {
    return (
     <div>
      <h2 style={{opacity: this.state.opacity}}>天很透明</h2>
      <button onClick={this.blank}>是的</button>

     </div>
    )
   }
  }
  //渲染组件
  ReactDOM.render(<Life/>, document.getElementById('test'))

 </script>
</body>
</html>

截屏2021-09-06 10.27.40.png

组件挂载流程(旧)(完成上图左侧)

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>2_react生命周期(旧)</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">

  //创建组件
  class Count extends React.Component {

   //构造器
   constructor(props) {
    console.log('Count-constructor')
    super(props);
    //初始化状态
    this.state = {count:0}
   }
   
   //加1 按钮回调
   add=()=>{
    //获取原状态
    const {count} = this.state
    //更新状态
    this.setState ({count:count+1})
   }

   //组件将要挂载钩子
   componentWillMount(){
    console.log('Count-componentWillMount')
   }
   //组件挂载完成钩子
   componentDidMount(){
    console.log('Count - componentDidMount')
   }


   render() {
    console.log('Count-render')
    const {count} = this.state
    return (
     <div>
      <h2>当前求和为</h2>
      <button onClick={this.add}>点我+1</button>
     </div>
    )
   }
  }
  //渲染组件
  ReactDOM.render(<Count/>, document.getElementById('test'))
 </script>
</body>
</html>

setState流程

正常更新流程(打开 控制组件更新的阀门) 截屏2021-09-06 10.27.40.png

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>2_react生命周期(旧)</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">

  //创建组件
  class Count extends React.Component {

   //构造器
   constructor(props) {
    console.log('Count-constructor')
    super(props);
    //初始化状态
    this.state = {count:0}
   }

   //加1 按钮回调
   add=()=>{
    //获取原状态
    const {count} = this.state
    //更新状态
    this.setState({count:count+1})
   }
   //卸载组件按钮的回调
   finish=()=>{
    ReactDOM.unmountComponentAtNode(document.getElementById('test'))

   }

   //组件将要挂载钩子
   componentWillMount(){
    console.log('Count-componentWillMount')
   }
   //组件挂载完成钩子
   componentDidMount(){
    console.log('Count-componentDidMount')
   }

   //组件将卸载的钩子
   componentWillUnmount(){
    console.log('Count-componentWillUnmount')
   }

   //控制组件更新的阀门
   shouldComponentUpdate(){
    console.log('Count-shouldComponentUpdate')
    return true
   }

   //组件将更新的钩子
   componentWillUpdate(){
    console.log('Count-componentWillUpdate')
   }

   //组件更新完毕的钩子
   componentDidUpdate(){
    console.log('Count-componentDidUpdate')
   }
   render() {
    console.log('Count-render')
    const {count} = this.state
    return (
     <div>
      <h2>当前求和为:{count}</h2>
      <button onClick={this.add}>点我+1</button>
      <button onClick={this.finish}>完成</button>

     </div>
    )
   }
  }
  //渲染组件
  ReactDOM.render(<Count/>, document.getElementById('test'))
 </script>
</body>
</html>

forceUpdate流程

强制更新路径 截屏2021-09-06 10.27.40.png 不更改任何状态中的数据,强制更新,

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>2_react生命周期(旧)</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">
 
  //创建组件
  class Count extends React.Component {

   //构造器
   constructor(props) {
    console.log('Count-constructor')
    super(props);
    //初始化状态
    this.state = {count:0}
   }

   //加1 按钮回调
   add=()=>{
    //获取原状态
    const {count} = this.state
    //更新状态
    this.setState({count:count+1})
   }
   //卸载组件按钮的回调
   finish=()=>{
    ReactDOM.unmountComponentAtNode(document.getElementById('test'))

   }
   //不更改任何状态中的数据,强制更新
   force=()=>{
    this.forceUpdate()
  }


   //组件将要挂载钩子
   componentWillMount(){
    console.log('Count-componentWillMount')
   }

   //组件挂载完成钩子
   componentDidMount(){
    console.log('Count-componentDidMount')
   }

   //组件将卸载的钩子
   componentWillUnmount(){
    console.log('Count-componentWillUnmount')
   }

   //控制组件更新的阀门
   shouldComponentUpdate(){
    console.log('Count-shouldComponentUpdate')
    return true
   }

   //组件将更新的钩子
   componentWillUpdate(){
    console.log('Count-componentWillUpdate')
   }

   //组件更新完毕的钩子
   componentDidUpdate(){
    console.log('Count-componentDidUpdate')
   }

   render() {
    console.log('Count-render')
    const {count} = this.state
    return (
     <div>
      <h2>当前求和为:{count}</h2>
      <button onClick={this.add}>点我+1</button>
      <button onClick={this.finish}>完成</button>
      <button onClick={this.force}>不更改任何状态中的数据,强制更新</button>

     </div>
    )
   }
  }
  //渲染组件
  ReactDOM.render(<Count/>, document.getElementById('test'))
 </script>
</body>
</html>

父组件render流程

截屏2021-09-06 10.27.40.png 注意这里的componentWillReceiveProps实际上应该是componentWillReceiveNewProps

截屏2021-09-06 11.26.45.png

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>2_react生命周期(旧)</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">

  class A extends React.Component {
   //初始化状态
   state = {carName:'benz'}

   changeCar = ()=>{
    this.setState({carName:'BMW'})
   }
   render() {
    return (
     <div>
      <div>我是A父组件</div>
      <button onClick={this.changeCar}> 换车</button>
      <B carName={this.state.carName}/>
     </div>

    )
   }
  }

  class B extends React.Component {
   componentDidMount(){
    console.log('b-componentDidMount')
   }

   //组件将要介绍新的props钩子
   componentWillReceiveProps(props){
    console.log('b-componentWillReceiveProps',props)
   }

   //控制组件更新的阀门
   shouldComponentUpdate(){
    console.log('b-shouldComponentUpdate')
    return true
   }

   //组件将更新的钩子
   componentWillUpdate(){
    console.log('b-componentWillUpdate')
   }

   //组件更新完毕的钩子
   componentDidUpdate(){
    console.log('b-componentDidUpdate')
   }
   render() {
    console.log('b-rander')
    return (
     <div>我是B子组件,接收的车是:{this.props.carName}</div>
    )
   }
  }
  //渲染组件
  ReactDOM.render(<A/>, document.getElementById('test'))
 </script>
</body>
</html>

总结生命周期(旧)

截屏2021-09-06 10.27.40.png

    1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
      1. constructor()
      2. componentWillMount()
      3. render()
      4. componentDidMount() =====> 常用 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
    1. 更新阶段: 由组件内部this.setSate()或父组件render触发
      1. shouldComponentUpdate()
      2. componentWillUpdate()
      3. render() =====> 必须使用的一个
      4. componentDidUpdate()
    1. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
      1. componentWillUnmount() =====> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

getDerivedStateFromProps

state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps,静态方法

getSnapshotBeforeUpdate

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>3_react生命周期(新)</title>
</head>
<body>
 <!-- 准备好一个“容器” -->
 <div id="test"></div>
 
 <!-- 引入react核心库 -->
 <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
 <!-- 引入react-dom,用于支持react操作DOM -->
 <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
 <!-- 引入babel,用于将jsx转为js -->
 <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

 <script type="text/babel">
  //创建组件
  class Count extends React.Component{
  
   //构造器
   constructor(props){
    console.log('Count---constructor');
    super(props)
    //初始化状态
    this.state = {count:0}
   }

   //加1按钮的回调
   add = ()=>{
    //获取原状态
    const {count} = this.state
    //更新状态
    this.setState({count:count+1})
   }

   //卸载组件按钮的回调
   death = ()=>{
    ReactDOM.unmountComponentAtNode(document.getElementById('test'))
   }

   //强制更新按钮的回调
   force = ()=>{
    this.forceUpdate()
   }
   
   //若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
   static getDerivedStateFromProps(props,state){
    console.log('getDerivedStateFromProps',props,state);
    return null
   }

   //在更新之前获取快照
   getSnapshotBeforeUpdate(){
    console.log('getSnapshotBeforeUpdate');
    return 'atguigu'
   }

   //组件挂载完毕的钩子
   componentDidMount(){
    console.log('Count---componentDidMount');
   }

   //组件将要卸载的钩子
   componentWillUnmount(){
    console.log('Count---componentWillUnmount');
   }

   //控制组件更新的“阀门”
   shouldComponentUpdate(){
    console.log('Count---shouldComponentUpdate');
    return true
   }

   //组件更新完毕的钩子
   componentDidUpdate(preProps,preState,snapshotValue){
    console.log('Count---componentDidUpdate',preProps,preState,snapshotValue);
   }
   
   render(){
    console.log('Count---render');
    const {count} = this.state
    return(
     <div>
      <h2>当前求和为:{count}</h2>
      <button onClick={this.add}>点我+1</button>
      <button onClick={this.death}>卸载组件</button>
      <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
     </div>
    )
   }
  }
  
  //渲染组件
  ReactDOM.render(<Count count={199}/>,document.getElementById('test'))
 </script>
</body>
</html>

总结 新 生命周期

截屏2021-09-06 11.31.43.png

    1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
      1. constructor()
      1. getDerivedStateFromProps
      1. render()
      1. componentDidMount() =====> 常用 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
    1. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
      1. getDerivedStateFromProps
      1. shouldComponentUpdate()
      1. render()
      1. getSnapshotBeforeUpdate
      1. componentDidUpdate()
    1. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
      1. componentWillUnmount() =====> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

9 虚拟 DOM 和 DOM diff 算法

验证diff算法

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>验证diff算法</title>
</head>
<body>
 <!-- 准备好一个“容器” -->
 <div id="test"></div>
 
 <!-- 引入react核心库 -->
 <script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
 <!-- 引入react-dom,用于支持react操作DOM -->
 <script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
 <!-- 引入babel,用于将jsx转为js -->
 <script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

 <script type="text/babel">
  class Time extends React.Component {
   state = {date: new Date()}

   componentDidMount () {
    setInterval(() => {
     this.setState({
      date: new Date()
     })
    }, 1000)
   }

   render () {
    return (
     <div>
      <h1>hello</h1>
      <input type="text"/>
      <span>
       现在是:{this.state.date.toTimeString()}
       <input type="text"/>
      </span>
     </div>
    )
   }
  }

  ReactDOM.render(<Time/>,document.getElementById('test'))
</script>
</body>
</html>

经典面试题:

  • 1). react/vue中的key有什么作用?(key的内部原理是什么?)
  • 2). 为什么遍历列表时,key最好不要用index?

1. 虚拟DOM中key的作用:

1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。

2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】, 随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:

  a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
     (1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
     (2).若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM

  b. 旧虚拟DOM中未找到与新虚拟DOM相同的key
     根据数据创建新的真实DOM,随后渲染到到页面
  

2. 用index作为key可能会引发的问题:

 1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:
     会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。

 2. 如果结构中还包含输入类的DOM:
     会产生错误DOM更新 ==> 界面有问题。
     
 3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,
  仅用于渲染列表用于展示,使用index作为key是没有问题的。

3. 开发中如何选择key?:

 1.最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
 2.如果确定只是简单的展示数据,用index也是可以的。
 

截屏2021-09-06 17.35.48.png

慢动作回放----使用index索引值作为key

  初始数据:
    {id:1,name:'小张',age:18},
    {id:2,name:'小李',age:19},
  初始的虚拟DOM:
    <li key=0>小张---18<input type="text"/></li>
    <li key=1>小李---19<input type="text"/></li>

  更新后的数据:
    {id:3,name:'小王',age:20},
    {id:1,name:'小张',age:18},
    {id:2,name:'小李',age:19},
  更新数据后的虚拟DOM:
    <li key=0>小王---20<input type="text"/></li>
    <li key=1>小张---18<input type="text"/></li>
    <li key=2>小李---19<input type="text"/></li>

-----------------------------------------------------------------

慢动作回放----使用id唯一标识作为key

  初始数据:
    {id:1,name:'小张',age:18},
    {id:2,name:'小李',age:19},
  初始的虚拟DOM:
    <li key=1>小张---18<input type="text"/></li>
    <li key=2>小李---19<input type="text"/></li>

  更新后的数据:
    {id:3,name:'小王',age:20},
    {id:1,name:'小张',age:18},
    {id:2,name:'小李',age:19},
  更新数据后的虚拟DOM:
    <li key=3>小王---20<input type="text"/></li> 
    <li key=1>小张---18<input type="text"/></li> (复用)
    <li key=2>小李---19<input type="text"/></li> (复用)
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>key的作用</title>
</head>
<body>
<div id="test"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel -->
<script type="text/javascript" src="../js/babel.min.js"></script>

<script type="text/babel">


  class Person extends React.Component {
   state = {
     persons:[
        {id:1,name:'leo',age:18},
        {id:2,name:'lily',age:15},
        {id:3,name:'tom',age:17},
      ]
    }
    add=()=>{
     const {persons} = this.state
      const p = {id:persons.length+1,name:'jerry',age:20 }
      this.setState ({persons:[p,...persons]})
    }
    render() {
      return (
        <div>
          <h2>展示人员信息</h2>
          <button onClick={this.add}>添加</button>
          <h3>使用index作为key</h3>
          <ul>
            {
              this.state.persons.map((personObj,index)=>{
                return <li key={index}>{personObj.name}---{personObj.age} <input type="text"/></li>
              })
            }
          </ul>

          <hr/><hr/>
          <h3>使用id作为key</h3>

          <ul>
            {
              this.state.persons.map((personObj,)=>{
                return <li key={personObj.id}>{personObj.name}---{personObj.age}<input type="text"/></li>
              })
            }
          </ul>
        </div>
      )
    }
  }

  ReactDOM.render(<Person/>,document.getElementById('test'))
</script>
</body>
</html>