目录
- 简介
- 基本使用
- 虚拟dom的两种创建方法
- jsx语法规则
- 模块与组件、模块化和组件化的理解
- 模块
- 组件
- 模块化
- 组件化
- 函数式组件
- 类式组件
- 组件实例三大属性
- state
- props
- refs
- 事件处理
- 包含表单的组件分类
- 非受控组件
- 受控组件
- 高阶函数_函数的柯里化
- 生命周期
- 引出生命周期
- 理解
- 生命周期(旧)
- 总结
- 新的生命周期
- 对比旧的生命周期
- 新的生命周期
- 总结
- DOM的diffing算法
简介
- react是什么?
react是用于构建用户界面的js库(是一个将数据渲染为html视图的开源js库) - 谁开发的?
- 为什么要学?
- 原生js操作dom繁琐、效率低(DOM-API操作UI)
- 使用js直接操作dom,浏览器会进行大量的重绘重排
- 原生js没有组件化编码方案,代码复用率低
- react的特点
- 才用组件化模式、声明式编码,提高开发效率及组件复用率
- 在ReactNative中可以使用react语法进行移动端开发
- 使用虚拟dom+优秀的diffing算法,尽量减少与真实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>Document</title>
</head>
<body>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">/*注意:此处一定要写babel*/
//1.创建虚拟DOM
const VDOM = (<h1 id="title"><span>hello react</span></h1>)/*注意:此处一定不要写引号,因为不是字符串*/
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('app'))/*注意:如果下面还有一模一样的这句话则是覆盖不是追加*/
</script>
</html>
虚拟dom的两种创建方法
- jsx (基本使用的写法)
- js (一般不用)
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
</body>
<script type="text/javascript">
//1.创建虚拟DOM
const VDOM = React.createElement('h1',{id:'title'},'hello react')
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('app'))
</script>
</html>
关于虚拟DOM:
- 本质是Object类型的对象(一般对象)
- 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React内部再用,无需真实DOM上那么多的属性(轻重指的是身上所有的方法和属性多与少)
- 虚拟DOM最终会被React转化为真实DOM,呈现在页面上
(debugger-浏览器打断点)
jsx语法规则
介绍jsx
- 全称:JavaScript XML
- react定义的一种类似于XML的JS扩张语法:JS+XML
- 作用:
- 写法:
var ele = <h1>hello react</h1>
- 注意1. 他不是字符串,也不是HTML/XML标签
- 注意2. 他最终产生的就是一个JS对象
- 写法:
- 标签名任意:HTML标签或其他标签
jsx语法规则 - 定义虚拟DOM时,不要写引号。
- 标签中混入js表达式要用{}
- 样式的类名指定不要用class,要用className
- 内联样式,要用style={{key:value}}的形式去写(第一个{}:是jsx语法,第二个{}是键值对的写法)
- 只有一个跟标签
- 标签必须闭合
- 标签首字母
- 若小写字母开头,则将改标签转为html中同名元素,若html中无该标签对应的同名元素,则报错
- 若大写字母开头,react就去渲染对应的组件。若组件没有定义,则报错
<script type="text/babel">
let data = 'hello react'
let dataClsaa='list'
let arr = ['vue','react','juqery']
//1.创建虚拟DOM
const VDOM = (
<div className={dataClsaa}>
<h2 style={{color:'red'}}>{data}</h2>
<input type="text"/>
<ul>/*遍历的时候必须要有个key*/
{
data.map((item,index)=>{
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM,document.getElementById('app'))
</script>
注意区分:【js语句(代码)】与【js表达式】
- 表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:1. a 2. a+b 3. demo(1) 4. arr.map() 5. function test(){}- 语句(代码)
下面这些都是语句(代码)1. if(){} 2. for(){} 3. switch*(){case:xx}
模块与组件、模块化和组件化的理解
模块
- 理解:向外提供俱特定功能的js程序,一般就是一个js文件
- 为什么要拆成模块:随着业务逻辑增加,代码越来越多且复杂
- 作用:复用js,简化js的编写,提高js运行效率
组件
- 理解:用来实现局部功能效果的代码和资源的集合
- 为什么:一个界面的功能更复杂
- 作用:复用编码,简化项目编码,提高运行效率
模块化
当应用的js都以模块来编写的,这个应用就是一个模块化应用
组件化
当应用是以组件的方式实现,这个应用就是一个组件化的应用
函数式组件
(简单组件):无状态{state}
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建函数式组件
function Demo(){/*1.函数名首字母要大写2.函数必须有返回值*/
console.log(this)//this是undefined(因为babel是严格模式,导致自定义组件的this不指向window)
return <h2>hello react</h2>
}
//2.渲染组件到页面
ReactDOM.render(<Demo/>,document.getElementById('app'))/*第一个参要写闭合的组件*/
/*
执行了ReactDOM.render(<Demo/>,document.getElementById('app'))之后发生了什么?
1.React解析组件标签,找到了Demo组件(找不见就会报错)
2.发现组件是使用函数定义的,税后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中
*/
</script>
</html>
类式组件
类复习
<!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>
</body>
<script type="text/javascript">
//常见一个Person类
class Person{
//构造器方法
constructor(name,age){
//构造器中的this是谁?——类的实例对象
this.name = name
this.age = age
}
//类中可以直接写赋值语句
a=2
//一般方法:除了构造器的方法,程序员根据业务需求写的方法称之为一般方法
speak(){
//speak方法放在哪里?——类的原型对象上,供实例使用
//通过Person实例调用speak时,speak中的this就是Person实例
console.log(`我叫${this.name},我的年龄是${this.age}`)
}
}
// const p1 = new Person('tom',18)
// const p2 = new Person('jerry',19)
// console.log(p1,p2)
//创建一个student类,继承于person类
class Student extends Person{
constructor(name,age,grade){
super(name,age)//继承父级-必须在最开始的时候调用super()
this.grade = grade
}
//重写从父类继承过来的方法
speak(){
console.log(`我叫${this.name},我的年龄是${this.age},我上${this.grade}`)
}
study(){
//study方法放在哪里?——类的原型对象上,供实例使用
//通过Student实例调用study时,study中的this就是Student实例
console.log('我很努力的学习')
}
}
const s1 = new Student('小张',15,'高一')
console.log(s1)
/*
总结:
1. 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写
2. 如果a类继承了b类,且a类中写了构造器,那么a类构造器中的super是必须要调用的
3. 类中所定义的方法,都是放在了类的原型对象上,供实例去使用
*/
</script>
</html>
组件实例三大属性
state
类式组件
(复杂组件):有状态{state}
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
//render是放在哪里的?——类的原型对象上,供实例使用
//render中的this是谁?——MyComponent的实例对象<=>(MyComponent组件实例对象)
render(){
console.log(this)
return <h2>hello react——简单组件(无状态)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))/*第一个参要写闭合的组件*/
/*
执行了 ReactDOM.render(<MyComponent/>,document.getElementById('app'))之后,发生了什么?
1.react解析组件标签,找到了MyComponent组件
2.发现组件是使用类定义的,税后new出来该类的实例,并通过该实例调用原型上的render方法
3.将render返回的虚拟dom转为真实dom,税后呈现在页面中
*/
</script>
</html>
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
constructor(props){
super(props)
this.state = {isHot:true}
}
render(){
console.log(this)
const {isHot} = this.state
return <h2>今天天气很{isHot?'炎热':'凉爽'}——复杂组件(有状态)</h2>
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))/*第一个参要写闭合的组件*/
</script>
</html>
复杂组件指的是类组件
复杂组件的事件和更改状态
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
//构造器调用几次?——1次
constructor(props){
super(props)
this.state = {isHot:true}
//注:call会直接执行,而bind是返回一个新的函数,需要手动执行,所以这里只能用bind
this.demo = this.demo.bind(this)
}
//render调用几次?——1+n次 1是初始化的 n是状态更新的次数
render(){
const {isHot} = this.state
//注意:1.点击onClick不要写成onclick 2.调用不要加()如果加会初始化自己会调用
return <h2 onClick={this.demo}>今天天气很{isHot?'炎热':'凉爽'}</h2>
//为什么要改变this指向是因为οnclick=this.demo本质意义上并没有调用而是直接把这个方法复制过来,(浅拷贝)所以this为undefined
}
demo(){
//demo放在哪里?——MyComponent的原型对象上,供实例使用
//由于demo是作为onClick的回调,所以不是通过实例调用的,是直接调用
//类中的方法默认开启了局部的严格模式,所以demo中的this为undefined
const isHot = this.state.isHot
//注意:状态(state)不可直接更改,下面这行就是直接更改
//this.state.isHot = !isHot//这是错误的写法
//严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
this.setState({isHot:!isHot})
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))/*第一个参要写闭合的组件*/
/*
bind:
function demo(){
console.log(this)
}
//demo.bind({a:1})//直接这么写不会执行,需要调用才会执行
const x = demo.bind({a:1})
x()
*/
</script>
</html>
以上代码简写形式
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
constructor(props){
super(props)
// this.state = {isHot:true}
// this.demo = this.demo.bind(this)
}
state = {isHot:true} //类中可以直接写赋值语句
render(){
const {isHot} = this.state
return <h2 onClick={this.demo}>今天天气很{isHot?'炎热':'凉爽'}</h2>
}
//自定义方法——要用赋值语句的形式+箭头函数
demo=()=>{
const isHot = this.state.isHot
this.setState({isHot:!isHot})
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
理解:
- state是组件对象最重要的属性,值是对象(可以包含多个key-valuie的组合)
- 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
强烈注意:
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this通过函数对象的bind()
- 箭头函数
- 状态数据,不能直接修改或更新
props
<!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>
<div id="app">
</div>
<div id="test">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 引入prop-types 用于对组件标签属性进行限制 -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
render(){
//props是只读的
const {name,age} = this.props
console.log(this)
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age+1}</li>
</ul>
)
}
}
MyComponent.propTypes={
//name:ProtoTypes.string//这种写法在16.xxx写法可以
//name:React.PropTypes这种写法在15.5xx版本之前可以,在16.xxx就被弃用了
name:PropTypes.string.isRequired,//string必须是string+isRequired必填项
age:PropTypes.number
//function=>.func——限制函数
}
MyComponent.defaultProps={
age:50
}
const p = {name:'lisa',age:18}
//2.渲染组件到页面
ReactDOM.render(<MyComponent name="tom" age="18"/>,document.getElementById('app'))
ReactDOM.render(<MyComponent {...p}/>,document.getElementById('test'))//{...p}=>由于react+babel导致允许你在标签中这样写,不然拓展运算符是不可以展开对象的
/*
展开运算符:
let arr1 = [1,2,3,4]
let arr2 = [7,8,9,6]
console.log(...arr1)//1.展开一个数组
let arr3 = [...arr1,...arr2]
console.log(arr3)//2.连接数组
function sum(...numbers){//求和
return numbers.reduce((preValue,currentValue)=>{
return preValue+currentValue
})
}
sum(1,5,6,7)//3.函数传参
let person = {name:'tom',age:18}
let person2 = {...person}//深拷贝
console.log(...person)//报错,展开运算符不能展开对象
*/
</script>
</html>
props简写
<!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>
<div id="app">
</div>
<div id="test">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 引入prop-types 用于对组件标签属性进行限制 -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
//对标签属性进行类型、必要性的限制
static propTypes={
name:React.PropTypes.string.isRequired,
age:React.PropTypes.number
}
//指定默认标签属性值
static defaultProps={
age:50
}
render(){
//props是只读的
const {name,age} = this.props
console.log(this)
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age+1}</li>
</ul>
)
}
}
const p = {name:'lisa',age:18}
//2.渲染组件到页面
ReactDOM.render(<MyComponent name="tom" age="18"/>,document.getElementById('app'))
ReactDOM.render(<MyComponent {...p}/>,document.getElementById('test'))//{...p}=>由于react+babel导致允许你在标签中这样写,不然拓展运算符是不可以展开对象的
</script>
</html>
构造器
<!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>
<div id="app">
</div>
<div id="test">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 引入prop-types 用于对组件标签属性进行限制 -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
//构造器_类中的构造器基本不会使用
constructor(props){
//构造器是否接受props,是否传递给super,取决于:是否希望在构造器中通过this访问props
super(props)
console.log(props)
}
//对标签属性进行类型、必要性的限制
static propTypes={
name:React.PropTypes.string.isRequired,
age:React.PropTypes.number
}
//指定默认标签属性值
static defaultProps={
age:50
}
render(){
//props是只读的
const {name,age} = this.props
console.log(this)
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age+1}</li>
</ul>
)
}
}
const p = {name:'lisa',age:18}
//2.渲染组件到页面
ReactDOM.render(<MyComponent name="tom" age="18"/>,document.getElementById('app'))
ReactDOM.render(<MyComponent {...p}/>,document.getElementById('test'))//{...p}=>由于react+babel导致允许你在标签中这样写,不然拓展运算符是不可以展开对象的
</script>
</html>
函数式组件获取props
<!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>
<div id="app">
</div>
<div id="test">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 引入prop-types 用于对组件标签属性进行限制 -->
<script src="https://unpkg.com/prop-types@15.6/prop-types.min.js"></script>
</body>
<script type="text/babel">
function MyComponent(props) {
console.log(props)
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
</ul>
)
}
//对标签属性进行类型、必要性的限制
MyComponent.propTypes={
name:React.PropTypes.string.isRequired,
age:React.PropTypes.number
}
//指定默认标签属性值
MyComponent.defaultProps={
age:50
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent name="tom" age="18"/>,document.getElementById('app'))
</script>
</html>
refs
勿过渡使用ref
什么时候不使用ref而使用event?
发生事件的元素正好是你要操作的元素的时候
字符串形式的ref(过时的api——)
不建议使用(string的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>Document</title>
</head>
<body>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
render(){
return (
<ul>
<li ref="name" onClick={this.demo}>姓名:lily</li>
<li>性别:女</li>
</ul>
)
}
demo = ()=>{
console.log(this.refs.name)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
回调形式的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>Document</title>
</head>
<body>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
render(){
return (
<ul>
{/*内联函数写法(一般工作中这种写法比较多):<li ref={currentNode=>this.input1 = currentNode} onClick={this.demo}>姓名:lily</li>*/}
<li ref={this.getrefs}>性别:女</li>
</ul>
)
/*
回调ref中调用执行次数的问题:如图
内联写法和定义成class绑定函数的区别:(无关紧要——不会有多大的影响)内联函数更新的时候会触发两次 一次为null一次为当前节点
*/
/*ref={currentNode=>this.input1 = currentNode} <=> ref={(a)=>{this.input1 = a}} 意义:把ref当前所处的节点挂载了实例自身上并且取了一个名字教input1*/
}
getrefs=(c)=>{
this.input1 = c
}
demo = ()=>{
const {input1} = this
console.log(input1)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
createRef
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
//React.createRef调用后可以返回一个容器,改容器可以存储被ref所标识的节点
//该容器是“专人专用”的
//每用一个就要写一个下面的
myRef = React.createRef()
myRef2 = React.createRef()
render(){
return (
<ul>
<li ref={this.myRef} onClick={this.demo}>姓名:lily</li>
<li ref={this.myRef2} onClick={this.demo2}>性别:女</li>
</ul>
)
}
demo = ()=>{
console.log(this.myRef.current)
}
demo2 = ()=>{
console.log(this.myRef2.current)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
事件处理
- 通过onXxx属性指定事件处理函数(注意大小写)
1. React使用的是自定义(合成)事件,而不是使用的原生DOM事件——为了更好的兼容性
2. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了高效 - 通过
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>Document</title>
</head>
<body>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
dot=(event)=>{
console.log(event.target)
}
render(){
return (
<ul>
<li onClick={this.dot}>点我一下</li>
</ul>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
包含表单的组件分类
非受控组件
所有输入类的DOM(checkbox、radio、input...)现用现取
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
handleSubmit=(event)=>{
event.preventDefault();//阻止默认事件
const {username,password} = this
alert(`用户名${username.value}密码${password.value}`)
}
render(){
return (
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c=>this.username=c} type="text" name="username"/>
密码:<input ref={c=>this.password=c} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
受控组件
页面中所有输入类的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>Document</title>
</head>
<body>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
// 初始化状态
state={
username:'',
password:''
}
// 表单提交问题
handleSubmit=(event)=>{
event.preventDefault();//阻止默认事件
const {username,password} = this.state
console.log(username,password)
}
// 保存用户名到状态中
getUsername=(event)=>{
this.setState({username:event.target.value})
}
// 保存密码到状态中
getPassword=(event)=>{
this.setState({password:event.target.value})
}
render(){
return (
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.getUsername} type="text" name="username"/>
密码:<input onChange={this.getPassword} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
建议使用受控组件,因为受控组件中未使用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>Document</title>
</head>
<body>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
// 初始化状态
state={
username:'',
password:''
}
// 获取表单数据到状态中
handleSubmit=(event)=>{
event.preventDefault();//阻止默认事件
const {username,password} = this.state
console.log(username,password)
}
getFormData=(dataType)=>{
console.log(dataType)
return (event)=>{//这种写法是有返回值的(返回的函数)————onChange调用的函数获取的就是event
this.setState({[dataType]:event.target.value})
}
}
render(){
return (
<form onSubmit={this.handleSubmit}>
{/*
onChange={this.getFormData('username')}————这种写法的意思是把这个函数getFormData的返回值作为onchange回调——(因为他的返回值是undefined所以就没有效果)
getFormData=(event)=>{//这种写法的返回值为undefined
this.setState({username:event.target.value})
}
*/}
{/*<input onChange={this.getFormData('username')} type="text" name="username"/>*/}
用户名:<input onChange={this.getFormData('username')} type="text" name="username"/>
密码:<input onChange={this.getFormData('password')} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
/*
对象相关的知识:
let a ='name'
let obj = {}
obj[a]='tom'
console.log(obj)
*/
</script>
</html>
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
1. 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数
2. 若A函数,调用的参数是一个函数,那么A就可以称之为高阶函数
常见的高阶函数有:promise setTimeout arr.map()
函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
(让注释代码折叠:#region/**/#endregion)
不用柯里化的写法
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class MyComponent extends React.Component{
// 初始化状态
state={
username:'',
password:''
}
// 获取表单数据到状态中
handleSubmit=(event)=>{
event.preventDefault();//阻止默认事件
const {username,password} = this.state
}
getFormData=(dataType,value)=>{
this.setState({[dataType]:event.target.value})
}
render(){
return (
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={(event)=>{this.getFormData('username',event)}} type="text" name="username"/>
密码:<input onChange={(event)=>{this.getFormData('password',event)}} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('app'))
</script>
</html>
生命周期
引出生命周期
componentDidMount——组件挂载完毕
componentWillUnmount——组件将要卸载
render——初始化渲染‘状态更新之后’
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
//1.创建类式组件
class Life extends React.Component{
/*
状态
*/
state = {
opacity:1
}
/*
方法
*/
death = ()=>{
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
/*
生命周期
*/
//组件挂载完毕
componentDidMount(){//只执行一次
this.timer=setInterval(() => {
//获取原状态
let {opacity} = this.state
//减小0.1
opacity-=0.1
if(opacity<=0) opacity=1
//设置新的透明度
this.setState({opacity})
}, 200);
}
//组件将要卸载
componentWillUnmount(){
//清除定时器
clearInterval(this.timer)
}
/*
初始化渲染
*/
render(){
return (
<div>
<h2 style={{opacity:this.state.opacity}} >学不会react怎么办?</h2>
<button onClick={this.death}>不活了</button>
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Life/>,document.getElementById('app'))
</script>
</html>
理解
- 组件从创建到死亡会经历一些特定的阶段
- react组件中包含一系列钩子函数(生命周期回调函数),会在特定的时刻调用
- 我们在定义组件时,会在特定的生命周期回调函数,中做特定的工作
生命周期(旧)
shouldComponentUpdate默认是true 即使不调用
挂载时
<!DOCTYPE html>
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
class Count extends React.Component{
// 构造器
constructor(props){
console.log('count-constructor')
super(props)
this.state={count:0}
}
add=()=>{
const {count} = this.state
this.setState({count: count+1})
}
death=()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 组件将要挂载的钩子
componentWillMount(){
console.log('count-componentWillMount')
}
// 组件挂载完毕的钩子
componentDidMount(){
console.log('count-componentDidMount')
}
// 组件将要卸载的钩子
componentWillUnmount(){
console.log('count-componentWillUnmount')
}
render(){
console.log('count-render')
const {count} = this.state
return(
<div>
<div>{count}</div>
<div onClick={this.add}>点击+1</div>
<div onClick={this.death}>点击卸载组件</div>
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Count/>,document.getElementById('app'))
</script>
</html>
父组件render
路线一
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
class Count extends React.Component{
// 构造器
constructor(props){
console.log('count-constructor')
super(props)
this.state={count:0}
}
add=()=>{
const {count} = this.state
this.setState({count: count+1})
}
death=()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 组件将要卸载的钩子
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>
<div>{count}</div>
<div onClick={this.add}>点击+1</div>
<div onClick={this.death}>点击卸载组件</div>
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Count/>,document.getElementById('app'))
</script>
</html>
路线二
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
class Count extends React.Component{
// 构造器
constructor(props){
console.log('count-constructor')
super(props)
this.state={count:0}
}
add=()=>{
const {count} = this.state
this.setState({count: count+1})
}
death=()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
//强制更新
force=()=>{
this.forceUpdate()
}
// 组件将要卸载的钩子
componentWillUnmount(){
console.log('count-componentWillUnmount')
}
//控制组件更新的阀门
shouldComponentUpdate(){
console.log('count-shouldComponentUpdate')
return false
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('count-componentWillUpdate')
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log('count-componentDidUpdate')
}
render(){
console.log('count-render')
const {count} = this.state
return(
<div>
<div>{count}</div>
<div onClick={this.add}>点击+1</div>
<div onClick={this.death}>点击卸载组件</div>
<div onClick={this.force}>不更改状态,强制更新</div>
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Count/>,document.getElementById('app'))
</script>
</html>
路线三
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
class A extends React.Component{
state = {carName:'奔驰'}
changeCar = ()=>{
this.setState({carName:'奥迪'})
}
render(){
return (
<div>
<div>a</div>
<button onClick={this.changeCar}>换车</button>
<B carName={this.state.carName}/>
</div>
)
}
}
class B extends React.Component{
//将要props(需要新的,意思就是刚接收的不算,只有新的在算)
componentWillReceiveProps(){
console.log('b')
}
//控制组件更新的阀门(必须要return布尔值)
shouldComponentUpdate(){
console.log('b-shouldComponentUpdate')
return true
}
//组件将要更新的钩子
componentWillUpdate(){
console.log('b-componentWillUpdate')
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log('b-componentDidUpdate')
}
render(){
console.log('b---render')
return (
<div>b {this.props.carName}</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<A/>,document.getElementById('app'))
</script>
</html>
总结
/*
1.初始化阶段:由ReactDOM。render()触发————初次渲染
1.constructor()
2.componentWillMount()
3.render()
4.componentDidMount()
2.更新阶段:由组件内部this.setState()或父组件重新render触发
1.shouldComponentUpdate()
2.componentWillUpdate()
3.render()
4.componentDidUpdate()
3.卸载组件:由ReactDOM.unmountComponentAtNode()触发
1.componentWillUnmount()
*/
新的生命周期
对比旧的生命周期
/*在新版本里,旧的生命周期前面需要加 UNSEFE_ 来解决警告 只有一下三个需要加
1.componentWillMount
2.componentWillReceiveProps
3.componentWillUpdate
*/
新的生命周期
/*挂载时*/
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
class Count extends React.Component{
// 构造器
constructor(props){
console.log('count-constructor')
super(props)
this.state={count:0}
}
add=()=>{
const {count} = this.state
this.setState({count: count+1})
}
death=()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
force=()=>{
this.forceUpdate()
}
// 组件将要卸载的钩子
componentWillUnmount(){
console.log('count-componentWillUnmount')
}
//控制组件更新的阀门
shouldComponentUpdate(){
console.log('count-shouldComponentUpdate')
return false
}
// 组件更新完毕的钩子
componentDidUpdate(){
console.log('count-componentDidUpdate')
}
//派生组件会导致代码沉余,并使用组件难以维护(了解即可)
static getDerivedStateFromProps(props,state){//基本上以后用不到
//props是传的值
console.log('getDerivedStateFromProps',props,state)//必须有返回值:第一种返回值是个状态对象,第二种返回值是null
return props//一旦返回状态对象,那么状态的更新就不执行了__若state值在任何时候都取决于props(这是其中一种解决办法,还有一种解决办法就是在构造器中给状态对象赋值props即可),那么可以使用
//return null 不影响功能
}
render(){
console.log('count-render')
const {count} = this.state
return(
<div>
<div>{count}</div>
<div onClick={this.add}>点击+1</div>
<div onClick={this.death}>点击卸载组件</div>
<div onClick={this.force}>不更改状态,强制更新</div>
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Count count="199"/>,document.getElementById('app'))
</script>
</html>
/*更新时*/
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
class Count extends React.Component{
// 构造器
constructor(props){
console.log('count-constructor')
super(props)
this.state={count:0}
}
add=()=>{
const {count} = this.state
this.setState({count: count+1})
}
death=()=>{
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
force=()=>{
this.forceUpdate()
}
// 组件将要卸载的钩子
componentWillUnmount(){
console.log('count-componentWillUnmount')
}
//控制组件更新的阀门
shouldComponentUpdate(){
console.log('count-shouldComponentUpdate')
return false
}
// 组件更新完毕的钩子
componentDidUpdate(preProps,preState,snapshotValue){
console.log('count-componentDidUpdate',preProps,preState,snapshotValue)
}
//派生组件会导致代码沉余,并使用组件难以维护(了解即可)
static getDerivedStateFromProps(props,state){//基本上以后用不到
//props是传的值
console.log('getDerivedStateFromProps',props,state)//必须有返回值:第一种返回值是个状态对象,第二种返回值是null
return null//一旦返回状态对象,那么状态的更新就不执行了__若state值在任何时候都取决于props(这是其中一种解决办法,还有一种解决办法就是在构造器中给状态对象赋值props即可),那么可以使用
//return null 不影响功能
}
//在更新之前获取快照
getSnapshotBeforeUpdate(){
console.log('getSnapshotBeforeUpdate')//有返回值:字符串或者null
return 'null'
}
render(){
console.log('count-render')
const {count} = this.state
return(
<div>
<div>{count}</div>
<div onClick={this.add}>点击+1</div>
<div onClick={this.death}>点击卸载组件</div>
<div onClick={this.force}>不更改状态,强制更新</div>
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Count count={199}/>,document.getElementById('app'))
</script>
</html>
/*getSnapshotBeforeUpdate_-___案例*/
<!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>
<style>
.list{
width: 200px;
height: 210px;
max-height: 210px;
overflow: auto;
background: bisque;
}
.news{
height: 30px;
}
</style>
</head>
<body>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
class Count extends React.Component{
state = {newsArr:[]}
componentDidMount(){
setInterval(() => {
const {newsArr} = this.state
//模拟一条新闻
const news = '新闻'+(newsArr.length+1)
//更新状态
this.setState({newsArr:[news,...newsArr]})
}, 1000);
}
getSnapshotBeforeUpdate(){
return this.refs.list.scrollHeight
}
componentDidUpdate(preProps,preState,height){
this.refs.list.scrollTop += this.refs.list.scrollHeight - height
}
render(){
return(
<div className="list" ref="list">
{
this.state.newsArr.map((n,index)=>{
return <div key={index} className="news">{n}</div>
})
}
</div>
)
}
}
//2.渲染组件到页面
ReactDOM.render(<Count count={199}/>,document.getElementById('app'))
</script>
</html>
总结
/*
1.初始化阶段:由ReactDOM.render()触发——————初次渲染
1.constructor()
2.getDeriverStateFromProps
3.render()
4.componentDidMount
2.更新阶段:由组件内部this.setState()或父组件重新render触发
1.getDerivedStateFromProps
2.shouldComponentUpdate()
3.render()
4.getSnapshotBeforeUpdate
5.componentDidUpdate
3.卸载组件:由ReactDom.unmountComponentAtNode()触发
1.componentWillUnmount()
*/
DOM的diffing算法
<!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>
<div id="app">
</div>
<!-- 注意:三者引入顺序 -->
<!-- react核心库 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 引入babel 用于将jsx转换为js -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</body>
<script type="text/babel">
/*
问题:
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也是可以的-
*/
// class Time extends React.Component{
// state = {date:new Date()}
// componentDidMount(){
// setInterval(()=>{
// this.setState({
// date:new Date()
// })
// },1000)
// }
// render(){
// return (
// <div>
// <input type="text"/>
// <span>{this.state.date.toTimeString()}</span>
// </div>
// )
// }
// }
//2.渲染组件到页面
ReactDOM.render(<Time />,document.getElementById('app'))
</script>
</html>