目录
1.1. React的基本认识
1.2. React的基本使用
1.3. JSX的理解和使用
1.4. 几个重要概念理解
1). 模块与组件
2). 模块化与组件化
2. react组件化开发
2.1. 基本理解和使用
2.2. 组件的3大属性: state
2.2. 组件的3大属性: props
2.2. 组件的3大属性: refs
2.3. 组件中的事件处理
2.4. 组件的组合使用
2.5. 组件收集表单数据(受控组件和非受控组件)
2.6. 组件的生命周期
2.7. 虚拟DOM与DOM diff算法
1). 虚拟DOM是什么?
2). Virtual DOM 算法的基本步骤
3). 进一步理解
4). key的作用
2.8. 命令式编程与声明式编程
2.9. 高阶函数__函数柯里化
1). Facebook开源的一个js库
2). 一个用来动态构建用户界面的js库
3). React的特点
Declarative(声明式编码)
Component-Based(组件化编码)
Learn Once, Write Anywhere(支持客户端与服务器渲染)
高效
单向数据流
4). React高效的原因
虚拟(virtual)DOM, 不总是直接操作DOM(批量更新, 减少更新的次数)
高效的DOM Diff算法, 最小化页面重绘(减小页面更新的区域)
1). 导入相关js库文件(react.js, react-dom.js, babel.min.js)
2). 编码:
1). 理解
* 全称: JavaScript XML
* react定义的一种类似于XML的JS扩展语法: XML+JS
* 作用: 用来创建react虚拟DOM(元素)对象
2). 编码相关
* js中直接可以套标签, 但标签要套js需要放在{}中
* 在解析显示js数组时, 会自动遍历显示
* 把数据的数组转换为标签的数组:
var liArr = dataArr.map(function(item, index){
return{item}
})
3). 注意:
* 标签必须有结束
* 标签要套js需要放在{}中
* 标签的class属性必须改为className属性
* 标签的style属性值必须为: {{color:'red', width:12}}
小驼峰:fontSize
* 标签首字母
(1)小写开头,标签改为HTML同名元素,没有则报错
(2)大写字母,react渲染相应的组件
4). React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site- scripting, 跨站脚本)攻击。5). Babel 会把JSX转译成一个名为React.createElement()函数调用
1. 模块:
理解: 向外提供特定功能的js程序, 一般就是一个js文件
为什么: js代码更多更复杂
作用: 复用js, 简化js的编写, 提高js运行效率
2. 组件:
理解: 用来实现特定功能效果的代码集合(html/css/js)
为什么: 一个界面的功能太复杂了
作用: 复用编码, 简化项目界面编码, 提高运行效率
1. 模块化:
当应用的js都以模块来编写的, 这个应用就是一个模块化的应用
2. 组件化:
当应用是以多组件的方式实现功能, 这上应用就是一个组件化的应用
1). 自定义的标签: 组件类(函数)/标签
2). 创建组件类
//方式1: 无状态函数(简单组件, 推荐使用) function MyComponent1(props) { return
自定义组件标题11111
} //方式2: ES6类语法(复杂组件, 推荐使用) class MyComponent3 extends React.Component { render () { return自定义组件标题33333
} }
3). 渲染组件标签
ReactDOM.render(
, cotainerEle) 4). ReactDOM.render()渲染组件标签的基本流程
React内部会创建组件实例对象/调用组件函数, 得到虚拟DOM对象
将虚拟DOM并解析为真实DOM
插入到指定的页面元素内部
5) constructor 构造器执行了一次
render 调用了n+1次 1是初始化的次数 n是状态更新的次数
方法() 点几次调用几次
1. 组件被称为"状态机", 页面的显示是根据组件的state属性的数据来显示2. 初始化指定:constructor(props) { // 通过此类方式将props传递到父类的构造函数中super(props)this.state = {stateName1 : stateValue1,stateName2 : stateValue2}}3. 读取显示:this.state.stateName14. 更新状态-->更新界面 : (状态数据不允许直接更改)this.setState({stateName1 : newValue})5. 组件中的render方法中的this为组件实例对象组件自定义的方法默认开启了局部的严格模式,this指向undefined方法放在类的原型对象上,修改this指向由于change是作为onClick的回调,所以不是通过实例调用的,是直接调用(1) this.change = this.change.bind(this)(2) 箭头函数 change=()->{}箭头函数没有this,找到外层函数的this作为自己的this使用,即为类的实例对象6. 类中直接写赋值语句,给实例对象添加属性
props的只读性:所有React组件必须像纯函数一样保护他们的props不被更改
所有组件标签的属性的集合对象
给标签指定属性, 保存外部数据(可能是一个function)
在组件内部读取属性: this.props.propertyName
作用: 从目标组件外部向组件内部传递数据props是只读的
this.props.name = 'jack' 错误,因为props只读*** 对props中的属性值进行类型限制和必要性限制 static 给自身加了属性
static propTypes = {
name:PropTypes.string.isRequired, //限制name必传,且为字符串
sex:PropTypes.string,//限制sex为字符串
speak:PropTypes.func,//限制speak为函数 函数限制为func
}
*** 指定默认标签属性值
static defaultProps = {
sex:'男',//sex默认值为男
age:18 //age默认值为18
}*** 扩展属性: 将对象的所有属性通过props传递
*** 构造器与props
构造函数只有两种使用情况:
(1)通过给this.state赋值对象来初始化内部state
(2)为事件处理函数绑定实例constructor(props){ //构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问props // console.log(props); super(props) console.log('constructor',this.props); }
组件内包含ref属性的标签元素的集合对象
给操作目标标签指定ref属性, 打一个标识
在组件内部获得标签对象: this.refs.refName(只是得到了标签元素对象)
作用: 找到组件内部的真实dom元素对象, 进而操作它*** 1 字符串形式的ref
showData = ()=>{ const {input1} = this.refs alert(input1.value) } showData = ()=>{ const {input1} = this.refs alert(input1.value) }
*** 2 回调函数形式的ref
以内联函数的方式定义,在更新过程中执行两次,第一次null(清空) 第二次传入DOM元素
每次渲染时会创建一个新的函数实例,react清空旧的ref设置新的
将ref的回调函数定义成class的绑定函数的方式
this.input1 = c } type="text" placeholder="点击"/> showData = ()=>{ const {input1} = this alert(input1.value) } saveInput = (c)=>{ this.input1 = c; console.log('@',c); }
*** 3 createRef
myRef = React.createRef() //React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的 showData = ()=>{ alert(this.myRef.current.value); } myRef = React.createRef() showData=()=>{ alert(this.myRef.current.value) }
自定义合成事件,而不是原生的DOM事件-----为了更好的兼容性
事件委托方式(委托给最外层的元素)----为了更加的高效
1. 给标签添加属性: onXxx={this.eventHandler}
2. 在组件中添加事件处理方法
eventHandler(event) {
}
3. 使自定义方法中的this为组件对象
在constructor()中bind(this)
使用箭头函数定义方法(ES6模块化编码时才能使用)
4. 事件监听
绑定事件监听
事件名
回调函数
触发事件
用户对对应的界面做对应的操作
编码
5. 阻止默认行为
通过 preventDefault ,不能通过 return false
ActionLink = ()=>{ function handleClick(e){ // e 是合成事件 e.preventDefault(); console.log('hello') } }
(1).通过onXxx属性指定事件处理函数(注意大小写)
a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) —————为了的高效
(2).通过event.target得到发生事件的DOM元素对象 —————不要过度使用ref
showData = (event)=>{ alert(event.target.value) alert(this.myRef.current.value); }
1)拆分组件: 拆分界面,抽取组件
2)实现静态组件: 使用组件实现静态页面效果
3)实现动态组件
① 动态显示初始化数据
② 交互功能(从绑定事件监听开始)
受控组件
页面中所有输入类的DOM随着输入维护到状态中,用到的时候从状态中取出来
渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。
阻止表单的默认提交行为:event.preventDefault()
value:event.target.value
受控组件更新state的流程:
可以通过初始state中设置表单的默认值
每当表单的值发生变化时,调用onChange事件处理器
事件处理器通过事件对象e拿到改变后的状态,并更新组件的state
一旦通过setState方法更新state,就会触发视图的重新渲染,完成表单组件的更新非受控组件
页面中所有输入类的DOM,现用现取 用到ref,不要过度使用ref
不是为每个状态更新都编写数据处理函数,可以使用 ref 来从 DOM 节点中获取表单数据。
react旧版生命周期包含三个过程:
1、挂载过程
constructor()
componentWillMount()
componentDidMount()2、更新过程
componentWillReceiveProps(nextProps)
shouldComponentUpdate(nextProps,nextState)
componentWillUpdate (nextProps,nextState)
render()
componentDidUpdate(prevProps,prevState)3、卸载过程
componentWillUnmount()其具体作用分别为:
1、constructor()
完成了React数据的初始化。它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。
注意:只要使用了constructor()就必须写super(),否则会导致this指向错误。2、componentWillMount()
组件已经完成初始化数据,但是还未渲染DOM时执行的逻辑,主要用于服务端渲染。3、componentDidMount()
组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染4、componentWillReceiveProps(nextProps)
1).在接受父组件改变后的props需要重新渲染组件时用到的比较多
2).接受一个参数nextProps
3).通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件5、shouldComponentUpdate(nextProps, nextState)
1).主要用于性能优化(部分更新)
2).唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
3).因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断6、componentWillUpdate(nextProps, nextState)
shouldComponentUpdate返回true以后,组件进入重新渲染的流程时执行的逻辑。7、render()
页面渲染执行的逻辑,render函数把jsx编译为函数并生成虚拟dom,然后通过其diff算法比较更新前后的新旧DOM树,并渲染更改后的节点。8、componentDidUpdate(prevProps, prevState)
组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。9、componentWillUnmount()
在此处完成组件的卸载和数据的销毁。
clear你在组建中所有的setTimeout,setInterval
移除所有组建中的监听 removeEventListener————————————————
getDerivedStateFromProps : 传入的props映射到State上面 每次re-rendering之前被调用
getSnapshotBeforeUpdate : 在更新之前获得快照在更改之前从DOM中捕获信息,任何返回值将作为参数传递给 componentDidUpdate()使用getDerivedStateFromProps代替了旧的componentWillReceiveProps及componentWillMount。使用getSnapshotBeforeUpdate代替了旧的componentWillUpdate。
***使用getDerivedStateFromProps(nextProps, prevState)的原因:
旧的React中componentWillReceiveProps方法是用来判断前后两个 props 是否相同,如果不同,则将新的 props 更新到相应的 state 上去。在这个过程中我们实际上是可以访问到当前props的,这样我们可能会对this.props做一些奇奇怪怪的操作,很可能会破坏 state 数据的单一数据源,导致组件状态变得不可预测。而在 getDerivedStateFromProps 中禁止了组件去访问 this.props,强制让开发者去比较 nextProps 与 prevState 中的值,以确保当开发者用到 getDerivedStateFromProps 这个生命周期函数时,就是在根据当前的 props 来更新组件的 state,而不是去访问this.props并做其他一些让组件自身状态变得更加不可预测的事情。
***使用getSnapshotBeforeUpdate(prevProps, prevState)的原因:
在 React 开启异步渲染模式后,在执行函数时读到的 DOM 元素状态并不总是渲染时相同,这就导致在 componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。而getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与componentDidUpdate 中一致的。
1. 组件的三个生命周期状态:
Mount:插入真实 DOM
Update:被重新渲染
Unmount:被移出真实 DOM
2. 生命周期流程:
* 第一次初始化显示: ReactDOM.render(, containDom)触发---初次渲染
constructor()
componentWillMount() : 将要插入回调
render() : 用于插入虚拟DOM回调
*componentDidMount() : 已经插入回调
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
* 每次更新state: this.setState({})或父组件render触发
componentWillReceiveProps(): 组件将要接收父组件新的属性,父组件render
shouldComponentUpdate: 调用setState()之后调用,默认返回值为true,控制组件更新的阀门
componentWillUpdate() : 将要更新回调 (forceUpdate()强制更新)
*render() : 更新(重新渲染)
componentDidUpdate() : 已经更新回调
* 删除组件: ReactDOM.unmountComponentAtNode(div): 移除组件 ---触发
*componentWillUnmount() : 组件将要被移除回调
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
3. 常用的方法
render(): 必须重写, 返回一个自定义的虚拟DOM
constructor(): 初始化状态, 绑定this(可以箭头函数代替)
componentDidMount() : 只执行一次, 已经在dom树中, 适合启动/设置一些监听
render():初始化渲染,状态更新之后
componentDidMount():组件挂载完毕
componentWillUnmount():组件将要卸载
ReactDOM.unmountComponentAtNode(div):卸载组件
***新的生命周期 钩子函数
<--
避免使用UNSAFE_ 可能出现bug
UNSAFE_componentWillMount()
UNSAFE_componentWillReceiveProps()
UNSAFE_componentWillUpdate()
弃用:componentWillMount()、componentWillReceiveProps()、componentWillUpdate()
-->1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
1. constructor()
2. getDerivedStateFromProps(nextProps,prevState) :state的值在任何时候都取决于 props,前面加static
3. render()
4. componentDidMount() :组件挂载完毕的钩子
一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
1. getDerivedStateFromProps
2. shouldComponentUpdate() :控制组件更新的“阀门”
3. render()
4. getSnapshotBeforeUpdate(prevProps,prevState,s) :在更新之前获得快照
在更改之前从DOM中捕获信息,任何返回值将作为参数传递给componentDidUpdate()
5. componentDidUpdate() :组件更新完毕的钩子3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
1. componentWillUnmount() =====> 常用
一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
*** 重要的钩子
1.render:初始化渲染或更新渲染调用
2.componentDidMount:开启监听, 发送ajax请求
3.componentWillUnmount:做一些收尾工作, 如: 清理定时器
一个虚拟DOM(元素)是一个一般的js对象, 准确的说是一个对象树(倒立的)
虚拟DOM保存了真实DOM的层次关系和一些基本属性,与真实DOM一一对应
如果只是更新虚拟DOM, 页面是不会重绘的
用JS对象树表示DOM树的结构;然后用这个树构建一个真正的DOM树插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把差异应用到真实DOM树上,视图就更新了
Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。
可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。
1. 虚拟DOM中key的作用:
1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。2). 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,
随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
(1).若虚拟DOM中内容没变, 直接使用之前的真实DOM
(2).若虚拟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也是可以的。
声明式编程
只关注做什么, 而不关注怎么做(流程), 类似于填空题
命令式编程
要关注做什么和怎么做(流程), 类似于问答题var arr = [1, 3, 5, 7] // 需求: 得到一个新的数组, 数组中每个元素都比arr中对应的元素大10: [11, 13, 15, 17] // 命令式编程 var arr2 = [] for(var i =0;i
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
1.若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
2.若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise、setTimeout、arr.map()等等函数的柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum(a){ return(b)=>{ return (c)=>{ return a+b+c } } }