-
无论是使用函数或是类来声明一个组件,它决不能修改它自己的 props。
-
所有 React 组件都必须是纯函数,并禁止修改其自身 props。
-
React是单项数据流,父组件改变了属性,那么子组件视图会更新。
-
属性 props是外界传递过来的,状态 state是组件本身的,状态可以在组件中任意修改
-
组件的属性和状态改变都会更新视图。
函数组件:
-
函数组件接收一个单一的 props 对象并返回了一个React元素
-
函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。
===========================================================================================
Virtual DOM 是一个轻量级的 JavaScript 对象,它最初只是 real DOM 的副本。它是一个节点树,它将元素、它们的属性和内容作为对象及其属性。 React 的渲染函数从 React 组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。
Virtual DOM 工作过程有三个简单的步骤:
-
每当底层数据发生改变时,整个 UI 都将在 Virtual DOM 描述中重新渲染。
-
然后计算之前 DOM 表示与新表示的之间的差异。
-
完成计算后,将只用实际更改的内容更新 real DOM。
==========================================================================================
State
-
一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state,一般在 constructor 中初始化
-
当需要修改里面的值的状态需要通过调用setState来改变,从而达到更新组件内部数据的作用,并且重新调用组件render方法
-
setState还可以接受第二个参数,它是一个函数,会在setState调用完成并且组件开始重新渲染时被调用,可以用来监听渲染是否完成
Props
-
React的核心思想就是组件化思想,页面会被切分成一些独立的、可复用的组件,组件从概念上看就是一个函数,可以接受一个参数作为输入值,这个参数就是props,所以可以把props理解为从外部传入组件内部的数据
-
react具有单向数据流的特性,所以他的主要作用是从父组件向子组件中传递数据
-
props除了可以传字符串,数字,还可以传递对象,数组甚至是回调函数
-
在子组件中,props在内部不可变的,如果想要改变它看,只能通过外部组件传入新的props来重新渲染子组件,否则子组件的props和展示形式不会改变
相同点
-
两者都是 JavaScript 对象
-
两者都是用于保存信息
-
props 和 state 都能触发渲染更新
区别
-
props 是外部传递给组件的,而 state 是在组件内被组件自己管理的,一般在 constructor 中初始化
-
props 在组件内部是不可修改的,但 state 在组件内部可以进行修改
-
state 是多变的、可以修改
======================================================================================
是什么
React 中的 Refs提供了一种方式,允许我们访问 DOM节点或在 render方法中创建的 React元素。
本质为ReactDOM.render()返回的组件实例,如果是渲染组件则返回的是组件实例,如果渲染dom则返回的是具体的dom节点。
如何使用
-
传入字符串,使用时通过 this.refs.传入的字符串的格式获取对应的元素
-
传入对象,对象是通过 React.createRef() 方式创建出来,使用时获取到创建的对象中存在 current 属性就是对应的元素
-
传入函数,该函数会在 DOM 被挂载时进行回调,这个函数会传入一个 元素对象,可以自己保存,使用时,直接拿到之前保存的元素对象即可
-
传入hook,hook是通过 useRef() 方式创建,使用时通过生成hook对象的 current 属性就是对应的元素
应用场景
在某些情况下,我们会通过使用refs来更新组件,但这种方式并不推荐,过多使用refs,会使组件的实例或者是DOM结构暴露,违反组件封装的原则;
但下面的场景使用refs非常有用:
-
对Dom元素的焦点控制、内容选择、控制
-
对Dom元素的内容设置及媒体播放
-
对Dom元素的操作和对组件实例的操作
-
集成第三方 DOM 库
==============================================================================
setState本身并不是异步,之所以会有一种异步方法的表现形式,归根结底还是因为react框架本身的性能机制所导致的。因为每次调用setState都会触发更新,异步操作是为了提高性能,将多个状态合并一起更新,减少re-render调用。
实现同步:
- setState提供了一个回调函数供开发者使用,在回调函数中,我们可以实时的获取到更新之后的数据。
state = {
number:1
};
componentDidMount(){
this.setState({number:3},()=>{
console.log(this.state.number) // 3
})
}
- 利用setTimeout
state = {
number:1
};
componentDidMount(){
setTimeout(()=>{
this.setState({number:3})
console.log(this.state.number) //3
},0)
}
- 还有在原生事件环境下
state = {
number:1
};
componentDidMount() {
document.body.addEventListener(‘click’, this.changeVal, false);
}
changeVal = () => {
this.setState({
number: 3
})
console.log(this.state.number) //3
}
=========================================================================================
在ES6中,通过extends关键字实现类的继承,super关键字实现调用父类,super代替的是父类的构建函数,使用super(xx)相当于调用sup.prototype.constructor.call(this.xx),如果在子类中不使用super关键字,则会引发报错
super()就是将父类中的this对象继承给子类的,没有super()子类就得不到this对象
在React中,类组件是基于es6的规范实现的,继承React.Component,因此如果用到constructor就必须写super()才初始化this,在调用super()的时候,我们一般都需要传入props作为参数,如果不传进去,React内部也会将其定义在组件实例中,所以无论有没有constructor,在render中this.props都是可以使用的,这是React自动附带的,但是也不建议使用super()代替super(props),因为在React会在类组件构造函数生成实例后再给this.props赋值,所以在不传递props在super的情况下,调用this.props为undefined,而传入props的则都能正常访问,确保了 this.props 在构造函数执行完毕之前已被赋值,更符合逻辑
总结
-
在React中,类组件基于ES6,所以在constructor中必须使用super
-
在调用super过程,无论是否传入props,React内部都会将porps赋值给组件实例porps属性中
-
如果只调用了super(),那么this.props在super()和构造函数结束之间仍是undefined
===============================================================================
是什么
React基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等,在React中这套事件机制被称之为合成事件;
合成事件是 React模拟原生 DOM事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器
执行顺序
-
React 所有事件都挂载在 document 对象上
-
当真实 DOM 元素触发事件,会冒泡到 document 对象后,再处理 React 事件
-
所以会先执行原生事件,然后处理 React 事件
-
最后真正执行 document 上挂载的事件
总结
-
React 上注册的事件最终会绑定在document这个 DOM 上,而不是 React 组件对应的 DOM(减少内存开销就是因为所有的事件都绑定在 document 上,其他节点没有绑定事件)
-
React 自身实现了一套事件冒泡机制,所以这也就是为什么我们 event.stopPropagation()无效的原因。
-
React 通过队列的形式,从触发的组件向父组件回溯,然后调用他们 JSX 中定义的 callback
-
React 有一套自己的合成事件 SyntheticEvent
了解更多详情请点击React事件机制的理解
==================================================================================
-
render方法中使用bind
-
render方法中使用箭头函数
-
constructor中bind
-
定义阶段使用箭头函数绑定
class App extends React.Component {
handleClick() {
console.log('this > ', this);
}
render() {
return (
{/1.render方法中使用bind/}
{/*2.render方法中使用箭头函数 */}
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
//3.constructor中bind
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('this > ', this);
}
render() {
return (
)
}
}
class App extends React.Component {
//4.定义阶段使用箭头函数绑定
handleClick = () => {
console.log('this > ', this);
}
render() {
return (
)
}
}
区别
-
编写方面:方式一、方式二写法简单,方式三的编写过于冗杂
-
性能方面:方式一和方式二在每次组件render的时候都会生成新的方法实例,性能问题欠缺。若该函数作为属性值传给子组件的时候,都会导致额外的渲染。而方式三、方式四只会生成一个方法实例
-
综合上述,方式四(箭头函数绑定)是最优的事件绑定方式
===============================================================================
- 初始渲染阶段:这是组件即将开始其生命之旅并进入 DOM 的阶段。
getDefaultProps:获取实例的默认属性
getInitialState:获取每个实例的初始化状态
componentWillMount:组件即将被装载、渲染到页面上
render:组件在这里生成虚拟的 DOM 节点
componentDidMount:组件真正在被装载之后
- 更新阶段:一旦组件被添加到 DOM,它只有在 prop 或状态发生变化时才可能更新和重新渲染。这些只发生在这个阶段。
componentWillReceiveProps:组件将要接收到属性的时候调用
shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止 render 调用,后面的函数不会被继续执行了)
componentWillUpdate:组件即将更新不能修改属性和状态
render:组件重新描绘
componentDidUpdate:组件已经更新
- 卸载阶段:这是组件生命周期的最后阶段,组件被销毁并从 DOM 中删除。
componentWillUnmount:组件即将销毁
===================================================================================
-
componentWillMount() – 在渲染之前执行,在客户端和服务器端都会执行。
-
componentDidMount() – 仅在第一次渲染后在客户端执行。
-
componentWillReceiveProps() – 当从父类接收到 props 并且在调用另一个渲染器之前调用。
-
shouldComponentUpdate() – 根据特定条件返回 true 或 false。如果你希望更新组件,请返回true ,不想更新组件则返回 false就会阻止render渲染。默认情况下,它返回 true。
-
componentWillUpdate() – 在 DOM 中进行渲染之前调用。
-
componentDidUpdate() – 在渲染发生后立即调用。
-
componentWillUnmount() – 从 DOM 卸载组件后调用。用于清理内存空间。
==============================================================================
shouldComponentUpdate,这个方法用来判断是否需要调用 render 方法重绘 dom。
因为 dom 的描绘非常消耗性能,如果我们能在这个方法中能够写出更优化的 dom diff 算法,可以极大的提高性能。
点击React学习笔记四——受控组件和非受控组件查看详解
============================================================================
受控组件是React控制的组件,input等表单输入框值不存在于 DOM 中,而是以我们的组件状态存在。每当我们想要更新值时,我们就像以前一样调用setState。
不受控制组件是您的表单数据由 DOM 处理,而不是React 组件,Refs 用于获取其当前值;
=============================================================================
和原生HTML定义事件的唯一区别就是JSX采用驼峰写法来描述事件名称,大括号中仍然是标准的JavaScript表达式,返回一个事件处理函数。在JSX中你不需要关心什么时机去移除事件绑定,因为React会在对应的真实DOM节点移除时就自动解除了事件绑定。
React并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件的target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。
==============================================================================
虚拟dom(virtual dom) 是 JS对象,是一个真实dom的JS对象;虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。
===============================================================================
跟Vue一样,React 也存在diff算法,而元素key属性的作用是用于判断元素是新创建的还是被移动的元素,从而减少不必要的Diff,因此key的值需要为每一个元素赋予一个确定的标识。
-
如果列表数据渲染中,在数据后面插入一条数据,key作用并不大;前面的元素在diff算法中,前面的元素由于是完全相同的,并不会产生删除创建操作,在最后一个比较的时候,则需要插入到新的DOM树中。因此,在这种情况下,元素有无key属性意义并不大。
-
如果列表数据渲染中,在前面插入数据时,当拥有key的时候,react根据key属性匹配原有树上的子元素以及最新树上的子元素,只需要将元素插入到最前面位置,当没有key的时候,所有的li标签都需要进行修改
-
并不是拥有key值代表性能越高,如果说只是文本内容改变了,不写key反而性能和效率更高,主要是因为不写key是将所有的文本内容替换一下,节点不会发生变化,而写key则涉及到了节点的增和删,发现旧key不存在了,则将其删除,新key在之前没有,则插入,这就增加性能的开销
总结
良好使用key属性是性能优化的非常关键的一步,注意事项为:
-
key 应该是唯一的
-
key不要使用随机值(随机数在下一次 render 时,会重新生成一个数字)
-
避免使用 index 作为 key
=================================================================================
-
把树形结构按照层级分解,只比较同级元素
-
通过给列表结构的每个单元添加的唯一 key值进行区分同层次的子节点的比较。
-
React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
-
合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty. 到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制。
-
选择性渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。
============================================================================
-
父子:父传子:props; 子传父:子调用父组件中的函数并传参;
-
兄弟:利用redux实现和利用父组件
-
所有关系都通用的方法:利用PubSub.js订阅
点击React学习笔记十二——组件之间的通信方式查看详细内容
=======================================================================
高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。基本上,这是从React的组成性质派生的一种模式,我们称它们为“纯”组件, 因为它们可以接受任何动态提供的子组件,但它们不会修改或复制其输入组件的任何行为。
-
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧
-
高阶组件的参数为一个组件返回一个新的组件
-
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件
点击对高阶组件的理解查看详解
=========================================================================================
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性,因此,现在的函数组件也可以是有状态的组件,内部也可以维护自身的状态以及做一些逻辑方面的处理;
最常见的hooks有:useState、useEffect
hooks的出现,使函数组件的功能得到了扩充,拥有了类组件相似的功能;
点击React学习笔记十一——扩展知识点(setState / lazyLoad / Hook / Fragment / Context)查看其中的Hook知识点详解
=======================================================================================
组件式开发选择合适的css解决方案尤为重要,而在react中,引入CSS就不如Vue方便简洁,其引入css的方式有很多种,各有利弊;
-
在组件内直接使用
-
组件中引入 .css 文件
-
组件中引入 .module.css 文件
-
CSS in JS
点击 react中引入css的方式查看以上几种引入方式的代码案例
区别
-
在组件内直接使用css该方式编写方便,容易能够根据状态修改样式属性,但是大量的演示编写容易导致代码混乱
-
组件中引入 .css 文件符合我们日常的编写习惯,但是作用域是全局的,样式之间会层叠
-
引入.module.css 文件能够解决局部作用域问题,但是不方便动态修改样式,需要使用内联的方式进行样式的编写
-
通过css in js 这种方法,可以满足大部分场景的应用,可以类似于预处理器一样样式嵌套、定义、修改状态等
-
至于使用react用哪种方案引入css,并没有一个绝对的答案,可以根据各自情况选择合适的方案
==================================================================================
在react中,react-transition-group是一种很好的解决方案,其为元素添加enter,enter-active,exit,exit-active这一系列勾子,可以帮助我们方便的实现组件的入场和离场动画
其主要提供了三个主要的组件:
-
CSSTransition:在前端开发中,结合 CSS 来完成过渡动画效果
-
SwitchTransition:两个组件显示和隐藏切换时,使用该组件
-
TransitionGroup:将多个动画组件包裹在其中,一般用于列表中元素的动画
在React中组件间过渡动画查看以上过度组件案例详解
================================================================================
简单说就是,当你不想在组件树中通过逐层传递props或者state的方式来传递数据时,可以使用Context来实现跨层级的组件数据传递。
使用props或者state传递数据,数据自顶下流。
使用Context,可以跨越组件进行数据传递。
==================================================================================