目录
- 简介
- 基本使用
- 虚拟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.创建虚拟DOMconst 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.创建虚拟DOMconst 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.创建虚拟DOMconst 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 = namethis.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.statereturn <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是返回一个新的函数,需要手动执行,所以这里只能用bindthis.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为undefinedconst 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.statereturn <h2 onClick={this.demo}>今天天气很{isHot?'炎热':'凉爽'}</h2>}//自定义方法——要用赋值语句的形式+箭头函数demo=()=>{const isHot = this.state.isHotthis.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.propsconsole.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.propsconsole.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访问propssuper(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.propsconsole.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} = thisconsole.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} = thisalert(`用户名${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.stateconsole.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.stateconsole.log(username,password)}getFormData=(dataType)=>{console.log(dataType)return (event)=>{//这种写法是有返回值的(返回的函数)————onChange调用的函数获取的就是eventthis.setState({[dataType]:event.target.value})}}render(){return (<form onSubmit={this.handleSubmit}>{/*onChange={this.getFormData('username')}————这种写法的意思是把这个函数getFormData的返回值作为onchange回调——(因为他的返回值是undefined所以就没有效果)getFormData=(event)=>{//这种写法的返回值为undefinedthis.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.1opacity-=0.1if(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.statethis.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.statereturn(<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.statethis.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.statereturn(<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.statethis.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.statereturn(<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.componentWillMount2.componentWillReceiveProps3.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.statethis.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)//必须有返回值:第一种返回值是个状态对象,第二种返回值是nullreturn props//一旦返回状态对象,那么状态的更新就不执行了__若state值在任何时候都取决于props(这是其中一种解决办法,还有一种解决办法就是在构造器中给状态对象赋值props即可),那么可以使用//return null 不影响功能}render(){console.log('count-render')const {count} = this.statereturn(<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.statethis.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)//必须有返回值:第一种返回值是个状态对象,第二种返回值是nullreturn null//一旦返回状态对象,那么状态的更新就不执行了__若state值在任何时候都取决于props(这是其中一种解决办法,还有一种解决办法就是在构造器中给状态对象赋值props即可),那么可以使用//return null 不影响功能}//在更新之前获取快照getSnapshotBeforeUpdate(){console.log('getSnapshotBeforeUpdate')//有返回值:字符串或者nullreturn 'null'}render(){console.log('count-render')const {count} = this.statereturn(<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.getDeriverStateFromProps3.render()4.componentDidMount2.更新阶段:由组件内部this.setState()或父组件重新render触发1.getDerivedStateFromProps2.shouldComponentUpdate()3.render()4.getSnapshotBeforeUpdate5.componentDidUpdate3.卸载组件:由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相同的key1)若虚拟DOM中内容没变,直接使用之前的真实DOM2)若虚拟DOM中内容变了,则生成新的真实DOM,随后替换页面中之前的真实DOMb.旧虚拟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>