前端知识库Reactjs进阶系列(组件的加载过程)

简介: 最近在项目中遇到react的组件多次渲染的问题,最后虽然顺利解决了但也同时发现了自己对于react生命周期的不熟悉,于是便找出react的文档重新去了解下,重新学习之后总结为以下两个主要知识点:react组件的整个渲染流程 react组件更新的注意点。

前言


最近在项目中遇到react的组件多次渲染的问题,最后虽然顺利解决了但也同时发现了自己对于react生命周期的不熟悉,于是便找出react的文档重新去了解下,重新学习之后总结为以下两个主要知识点:react组件的整个渲染流程 react组件更新的注意点。

生命周期流程


网络异常,图片无法展示
|

由上图可以了解到react组件的生命周期大致可以分为三个阶段:

  • 挂载(初始化)阶段
  • 更新(运行中)阶段
  • 卸载阶段

DOM挂载阶段

  • defaultProps:给父组件传递过来的props赋初期值
Demo.defaultProps={
   demo:"test"
}
  • constructor:数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。
1.只要使用了constructor()就必须写super(),否则会导致this指向错误。
   2.在constructor生命周期中可以直接设置state的值。
  constructor(props) {
    super(props);
    //只有此种状态可以直接设置state
    this.state = {};
  }
  • componentWillMount:用于挂在之前,在组件的整个生命周期内只执行一次,此时可以进行setState
  • componentDidMount:用于组件在DOM成功渲染之后执行,此生命周期中可以获取真实的dom元素,在组件的整个生命周期内只执行一次,另外官方文档建议在这个生命周期适合进行异步请求
import axios from 'axios'
componentDidMount(){
   document.querySelector("#root")
   //异步请求
   aixos.get('/demo')
        .then(res=>{})
}

为什么官方建议在componentDidMount生命周期中进行异步请求?

正常点逻辑是在越早的生命周期进行请求越好,例如vue中建议在created(beforeCreated时data还没挂载)生命周期中进行异步请求,而在react中componentDidMount之前还有componentWillMount和constructor,为何不再这两个生命周期中调用呢,首先我们进行的异步请求一般都会将获取的数据渲染到组件上及调用setState,我们下面以这个为基础来分析。

  • constructor中为何不建议进行异步请求,个人认为原因有两点:1.不符合我们日常的编码规范 2.此处进行setState无意义。
  • componentWillMount将会在16.x慢慢被废弃掉,所以不建议使用
  • 在此生命周期中进行异步请求因为此生命周期调用setState会进行render渲染

综上所述还是在componentDidMount中进行异步请求比较合理。

组件挂载中是如何将虚拟DOM转化成真实DOM的?

在组件挂载之前React组件是一个JavaScript对象也就是我们所说的虚拟DOM。React以二叉树的形式表示整个UI结构的方式创建自己的虚拟DOM。它将虚拟DOM树保存到内存中。结构如下

//react组件中html
<div class="demo">
  <p class="item">ui components</p>
</div>
//JavaScript表示都数据结构
{
  tagName: 'div', // 节点标签名
  props: { // DOM的属性,用一个对象存储键值对
    class: 'demo'
  },
  children:[
     {tagName:"p",props:{class:"item"}}
  ]
  }

虚拟DOM vs 直接操作真实DOM?

在正常的前端页面中,如果是整个页面数据都发生变化,此时我们通过重置整个html是合理都,但是如果仅仅是页面某一快数据发生变化,那我们此时重置整个页面都操作就时分浪费性能了。对于虚拟DOM而言只用更新对象上相关数据后对页面都部分进行重新渲染即可。比较如下:

  • 原生DOM: render html string + 重新创建所有 DOM 元素
  • Virtual DOM: render Virtual DOM + diff + 必要的 DOM 更新

DOM更新

当我们使用setState或者父组件的props改变时候会触发组件的更新,此时生命周期如下:

  • componentWillReceiveProps(方法即将过时,不在过多介绍)
  • shouldComponentUpdate:参数如下nextProps(即将更新的props), nextState(即将更新的state) 注意:如果 返回值为 false,则不会调用 componentDidUpdate()。
shouldComponentUpdate(nextProps,nextState){
    console.log('----should update---')
    return false
}
componentDidUpdate(){
    console.log('-----did update------')
}
  • componentWillUpdate(方法即将过时,不在过多介绍)
  • componentDidUpdate :组件更新时会触发次钩子函数。例如:setState或父元素的props改变。
  • render
1.在react组件中同事多次执行setState最终只会render一次
2.方法是 class 组件中唯一必须实现的方法
3.当被调用时,返回以下类型之一:React元素 数组或fragments 字符串或数值类型(被渲染成文本节点) 布尔类型或 null(不渲染)
4.shouldComponentUpdate() 返回 false,则不会调用 render()
5.render为纯函数,不修改组件 state 的情况下每次调用都返回相同的值,并且不会与浏览器交互。

为何在react不能直接改变state?

直接使用this.state是能够改变state的值的但是并不会触发组件的更新,这样会导致两个问题:组件页面的内容和组件的状态不一致 组件内的状态不可控。所以我们在进行状态更新时候需要调用setState方法来进行。

为何直接给state赋值不起作用呢?

根据上一个问题我们知道了setState都执行步骤,如何我们直接给状态赋值是不会将状态放入state中的。因此就不会触发react的更新。

setState是同步的还是异步的呢?

  • setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。
  • setState本身的代码是同步的,知识因为react的合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值

组件更新原理

在react中的组件加载中,更新操作是组件不可避免的操作,在以上内容中我们已经大概了解了setState的执行步骤,本节主要来了解过在更新过程中react是通过什么方式实现其复杂的组件新。

为何setState触发组件更新可能是异步对?

执行步骤:

  • 在函数中调用this.setState({demo:"demo"})
  • 此时奖setState中的新状态存入pending队列中
  • 判断状态是否需要批量更新
  • 如果是则等所有带新状态都保存在pending在更新(updateComponent)
  • 如果不是则开始更新新状态

以上是我们在调用setState时候的执行步骤,下面我们来了解下react调用setState是如何触发组件更新的:

  • setState挂载到组件原型链上,
ReactComponent.prototype.setState = function (partialState, callback) {
  !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? "development" !== 'production' ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : _prodInvariant('85') : void 0;
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};   
  • 执行setState时调用enqueueSetState

什么情况下setState是同步的呢?

在我们使用React封装的事件时会进入一个事务,这是不会直接触发batch update更新 。 而当我们使用原生的事件机制时(比如 addEventListener ),由于缺少了React的封装,会使得 setState 直接触发 batch update更新,从而同步更新state。

DOM销毁

  • componentWillUnmount:组件销毁时触发的生命周期。

在react生命周期中componentWillUnmount虽然我们很少在此处进行操作,但是在某些场景下可以发挥很大到作用比如在离开组件时候结束一个定时器或是解绑一个全局事件。

总结


通过上文的总结,我对react对组件加载有了更深刻对认识,其过程大概可分为三大阶段:挂载,更新,卸载。对于react组件的加载我认为以下几点是需要我们关注的:挂载阶段的虚拟Dom是如何渲染成真实DOM的,更新时候setState做了什么。


相关文章
|
5天前
|
缓存 前端开发 搜索推荐
如何优化前端性能:提升网页加载速度的5个技巧
在当今互联网时代,网页加载速度是用户体验和搜索引擎排名的关键因素之一。本文将介绍5个有效的技巧,帮助前端开发人员优化网页性能,提升用户体验。
|
24天前
|
缓存 监控 前端开发
优化前端性能:提升网页加载速度的10个技巧
在当今互联网时代,网页加载速度成为用户体验的重要指标之一。本文将介绍10个有效的技巧,帮助前端开发人员优化网页性能,提升用户的访问体验。
|
24天前
|
前端开发 JavaScript UED
如何优化前端网页加载速度:最佳实践与技巧
本文探讨了如何通过优化前端网页加载速度来提升用户体验和网站性能。从资源压缩和合并、减少HTTP请求、优化图片、使用CDN加速、采用异步加载和延迟加载等方面介绍了一系列最佳实践和技巧,帮助开发者更好地优化前端性能,提升网页加载速度。
|
24天前
|
缓存 前端开发 JavaScript
如何优化前端网页加载速度:关键步骤与技巧
在当今互联网时代,网页加载速度对用户体验至关重要。本文将介绍如何通过优化前端代码和调整网页资源加载顺序等关键步骤,有效提升网页加载速度,从而提升用户满意度和网站流量。
|
1天前
|
前端开发 搜索推荐 UED
【Flutter前端技术开发专栏】Flutter中的高级UI组件应用
【4月更文挑战第30天】探索Flutter的高级UI组件,如`TabBar`、`Drawer`、`BottomSheet`,提升应用体验和美观度。使用高级组件能节省开发时间,提供内置交互逻辑和优秀视觉效果。示例代码展示了如何实现底部导航栏、侧边导航和底部弹出菜单。同时,自定义组件允许个性化设计和功能扩展,但也带来性能优化和维护挑战。参考Flutter官方文档和教程,深入学习并有效利用这些组件。
【Flutter前端技术开发专栏】Flutter中的高级UI组件应用
|
1天前
|
XML JSON 前端开发
【Flutter前端技术开发专栏】Flutter中的图片、视频与网络资源加载
【4月更文挑战第30天】Flutter是谷歌的开源前端框架,因其高性能、流畅UI和多端运行能力受开发者喜爱。本文聚焦于Flutter中的资源加载:使用`Image`组件加载静态、网络和本地图片;通过`video_player`库加载和播放视频;利用`http`包进行网络资源请求。掌握这些技巧将有助于提升Flutter应用的开发效率和质量。
【Flutter前端技术开发专栏】Flutter中的图片、视频与网络资源加载
|
3天前
|
缓存 前端开发 算法
前端需要加载一个大体积的文件时,可以这么优化
前端需要加载一个大体积的文件时,可以这么优化
|
3天前
|
前端开发 UED
🌟前端分页加载/懒加载预览PDF🌟
🌟前端分页加载/懒加载预览PDF🌟
|
3天前
|
缓存 前端开发 JavaScript
如何优化前端网页加载速度:实用技巧大揭秘
在当今互联网时代,快速加载的网页是用户体验的关键。本文将介绍一些实用的前端优化技巧,从减少HTTP请求到使用CDN加速,帮助开发人员提高网页加载速度,提升用户满意度。
|
6天前
|
前端开发 JavaScript 应用服务中间件
前端 vite+vue3——写一个随机抽奖组件
前端 vite+vue3——写一个随机抽奖组件
19 1