const element = <h1>Hello, world!</h1>;
JSX,既不是字符串也不是HTML,本质上是一个 JavaScript 的语法扩展,且更接近于JavaScript,是通过React.createElement()
创建的一个对象,称为React 元素
。
React 不强制使用JSX,但将标记与逻辑放在一起形成组件
,实现关注点分离。同时,JSX 能够防止XSS注入攻击。
ReactDOM.render()
。function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。
这样的函数被称为“纯函数”,因为该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。
setState(updater,[callback])
在React中,如果是由React引发的事件处理
(比如通过onClick引发的事件处理),调用setState不会同步更新this.state
,
为什么要异步?如果setState是同步更新state,而state的更新又会触发组件的重新渲染,那么每次setState都会渲染组件,这对性能是很大的消耗。
使用 compoentDidUpdate
或 setState
的回调函数,来保证在更新应用后触发。
批量更新,是基于一个队列和一个变量锁isBatchingUpdates
实现。
正确地使用 State的姿势:
在react的生命周期和合成事件中,react仍然处于他的更新机制中,这时isBranchUpdate为true
。
按照上述过程,这时无论调用多少次setState,都会不会执行更新,而是将要更新的state存入_pendingStateQueue
,将要更新的组件存入dirtyComponent
。
当上一次更新机制执行完毕,以生命周期为例,所有组件,即最顶层组件didmount
后会将isBranchUpdate
设置为false
。这时将执行之前累积的setState
。
由执行机制看,setState本身并不是异步的
,而是如果在调用setState时,如果react正处于更新过程,当前更新会被暂存,等上一次更新执行后在执行,这个过程给人一种异步的假象
。
在生命周期,根据JS的异步机制,会将异步函数先暂存,等所有同步代码执行完毕后在执行,这时上一次更新过程已经执行完毕,
isBranchUpdate
被设置为false
,根据上面的流程,这时再调用setState即可立即执行更新,拿到更新结果。
它将会触发一次额外的渲染,但是它将在浏览器刷新屏幕之前发生。这保证了在此情况下即使render()将会调用两次,用户也不会看到中间状态。
componentDidMount本身处于一次更新中
,我们又调用了一次setState,就会在未来再进行一次render,造成不必要的性能浪费,大多数情况可以设置初始值来搞定。
componentWillUpdate
、componentDidUpdate
不能调用setState, 会造成死循环,导致程序崩溃。生命周期:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
注意:
下述生命周期方法即将过时,在新代码中应该避免使用它们: UNSAFE_componentWillMount()
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()
注意:
下述方法即将过时,在新代码中应该避免使用它们:
UNSAFE_componentWillUpdate() UNSAFE_componentWillReceiveProps()
componentWillUnmount()
preventDefault
。React自己实现了一套事件机制,自己模拟了事件冒泡和捕获的过程,采用了事件代理,批量更新等方法,并且抹平了各个浏览器的兼容性问题。
挂载在document中
所以原生的事件会先执行
react事件和原生事件最好不要混用。
原生事件中如果执行了stopPropagation方法,则会导致其他react事件失效。因为所有元素的事件将无法冒泡到document上
。
this绑定:你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。
方法有三:
this.handleClick = this.handleClick.bind(this);
handleClick = () => {console.log('this is:', this);}
<button onClick={() => this.handleClick()}>Click me</button>
注意:
性能优化点每次渲染 Button 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染
。
我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用。 Props 和 组合
为你提供了清晰而安全地定制组件外观和行为的灵活方式。
注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。
{props.children}
来将他们的子组件传递到渲染结果中slot
的概念。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递
props。
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。
代码优化点
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。
如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。
一种无需 context 的解决方案是将子组件自身传递下去,因而中间组件无需知道该子组件用到的props
。
部分 UI 的 JavaScript 错误不应该导致整个应用崩溃,为了解决这个问题,React 16 引入了一个新的概念 —— 错误边界。
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
错误边界是一种 React 组件,这种组件可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它会渲染出备用 UI,而不是渲染那些崩溃了的子组件树。
错误边界在渲染期间、生命周期方法和整个组件树的构造函数中捕获错误。
代码优化点
错误边界无法捕获以下场景中产生的错误:
Ref 转发是一项将 ref 自动地通过组件传递到其一子组件的技巧。这个技巧对高阶组件(也被称为 HOC)特别有用
。
Ref 转发是一个可选特性,其允许某些组件接收 ref,并将其向下传递(换句话说,“转发”它)给子组件。
React 中的一个常见模式是一个组件返回多个元素。Fragments 允许你将子列表分组,而无需向 DOM 添加额外节点。
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
或者使用短语法:<> </>
定义:高阶组件是参数为组件,返回值为新组件的函数。
HOC 不会修改传入的组件,也不会使用继承来复制其行为。相反,HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用
。
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式
。
const EnhancedComponent = higherOrderComponent(WrappedComponent);
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。例如:Redux 的 connect
注意:
我们会添加一个 ref 到这个根 DOM 元素
。 在 componentDidMount 中,我们能够获取它的引用这样我们就可以把它传递给 jQuery 插件了。
为了防止 React 在挂载之后去触碰这个 DOM,我们会从 render() 函数返回一个空的 <div />
。
这个
元素既没有属性也没有子元素,所以 React 没有理由去更新它,使得 jQuery 插件可以自由的管理这部分的 DOM
class SomePlugin extends React.Component {
componentDidMount() {
this.$el = $(this.el);
this.$el.somePlugin();
}
componentWillUnmount() {
this.$el.somePlugin('destroy');
}
render() {
return <div ref={el => this.el = el} />;
}
}
react-window
和 react-virtualized
是热门的虚拟滚动库。覆盖生命周期方法 shouldComponentUpdate 来进行提速
。该方法会在重新渲染前被触发。其默认实现总是返回 true. 如果你知道在什么情况下你的组件不需要更新,你可以在 shouldComponentUpdate 中返回 false 来跳过整个渲染过程。继承 React.PureComponent 以代替手写 shouldComponentUpdate()。它用当前与之前 props 和 state 的浅比较
覆写了 shouldComponentUpdate() 的实现.
shouldComponentUpdate(nextProps, nextState) {
return true;
}
componentWillUnmount()
-> componentWillMount()
-> componentDidMount()
仅比对及更新有改变的属性
。然后子节点递归。在子元素列表末尾新增元素时,更新开销比较小;
如果只是简单的将新增元素插入到表头,那么更新开销会比较大,不会意识到应该保留后面的,而是会重建每一个子元素 。这种情况会带来性能问题。
通过添加key来解决。
尽量用相同的节点类型和稳定可预测的Key。
render prop 是一个用于告知组件需要渲染什么内容的函数 prop。使用 Props 而非 render。
重要的是要记住,render prop 是因为模式才被称为 render prop ,你不一定要用名为 render 的 prop 来使用这种模式。
将 Render Props 与 React.PureComponent 一起使用时要小心。
如果你在 render 方法里创建函数,那么使用 render prop 会抵消使用 React.PureComponent 带来的优势
。
因为浅比较 props 的时候总会得到 false,并且在这种情况下每一个 render 对于 render prop 将会生成一个新的值。
react根据key来决定是销毁重新创建组件还是更新组件,原则是:
使用index做key存在的问题:
当元素数据源的顺序发生改变时,会重新渲染。而如果使用唯一ID作为key,子组件的值和key均未发生变化,只是顺序发生改变,因此react只是将他们做了移动,并未重新渲染。
很多文章说VitrualDom可以提升性能,这一说法实际上是很片面的。
直接操作DOM是非常耗费性能的,这一点毋庸置疑。但是React使用VitrualDom也是无法避免操作DOM的。
如果是首次渲染,VitrualDom不具有任何优势
,甚至它要进行更多的计算,消耗更多的内存。
VitrualDom的优势在于React的Diff算法和批处理策略
,React在页面更新之前,提前计算好了如何进行更新和渲染DOM。
实际上,这个计算过程我们在直接操作DOM时,也是可以自己判断和实现的,但是一定会耗费非常多的精力和时间,而且往往我们自己做的是不如React好的。
所以,在这个过程中React帮助我们"提升了性能"。
所以,我更倾向于说,VitrualDom帮助我们提高了开发效率,在重复渲染时它帮助我们计算如何更高效的更新,而不是它比DOM操作更快。
跨浏览器兼容
React基于VitrualDom自己实现了一套自己的事件机制,自己模拟了事件冒泡和捕获的过程,采用了事件代理,批量更新等方法,抹平了各个浏览器的事件兼容性问题。
React.createElement(...)
,Babel帮助我们完成了这个转换的过程。props进行处理
,并获取defaultProps对默认props进行赋值
,并且对传入的孩子节点进行处理,最终构造成一个ReactElement对象
(所谓的虚拟DOM)。采用了批处理
、事务等机制
并且对特定浏览器进行了性能优化,最终转换为真实DOM
将所有的事件都代理到document上
,自己模拟了事件冒泡和捕获的过程
,并且进行统一的事件分发。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有