上节课我们对reactdiffing做了一些展开的讨论。接下来开始我们来进入react脚手架的学习。但是我们一味地往前推进课程进度的话,大家会不会发现好像前面的一些基础知识都有所遗忘了?那么我们这节课先来对前面的一些react基础来做一个大致的概括总结。

回顾

在开始复习react基础知识之前呢,我们先来对上节课的内容来做一个简单的回顾。

  • diffing算法会来对比新旧虚拟DOM
  • diffing算法通过key来比对新旧真实DOM
  • diffing算法比对的最小单位是标签
  • 尽量使用数据唯一标识来做key

以上便是我们上节课整体概括下来的知识要点。接下来我们开始复习react前面的一些基础知识

简介

react是一个将我们处理好的数据渲染成HTML视图的这么样一个开源的JavaScript库。通俗一点来说呢就是,你提供数据给react,那么react就帮你渲染成HTML视图。

  • 组件化模式,声明式编码,提高开发效率及组件复用率
  • react navtive中可以使用react语法进行移动端开发
  • 使用虚拟DOM+Diffing算法,尽量减少与真实DOM的交互
  • 要求掌握的知识点:
  • 判断this的指向
  • class
  • ES6语法
  • npm包管理器
  • 理解原型、原型链
  • 数组常用方法
  • 模块化

引入react

在前面的课程中,我们都是通过在html文件中将react本地文件来引入,从而实现引入react库的目的。那么就需要注意一下几点:

  • react库必须要在react-dom之前引入
  • 引入babel库来支持jsx语法
  • 需要有容器来供react来渲染虚拟DOM
  • jsx语法代码部分不能用引号把相关代码扩起来

创建虚拟DOM

我们说了react中会使用虚拟DOM从而尽量避免对真实DOM的直接操作,那么如何创建虚拟DOM

  • 直接使用jsx语法创建虚拟DOM
  • 使用原生js语法来创建虚拟DOM(不需要掌握,天塌下来也用不上)
  • babel库会自动把jsx语法转换为原生js语法创建虚拟DOM,这样浏览器才能识别。
  • jsx创建虚拟DOM的方法是原生js语法创建虚拟DOM的一个语法糖
  • jsx方法创建虚拟DOM更加简洁明了,更加方便快捷

虚拟DOM和真实DOM

当我们学会如何去创建和使用虚拟DOM之后,其实我们还是一直挺疑惑,虚拟DOM到底是什么呢?和真实DOM又有什么区别呢?那么我们又对这两个家伙做了一次对比

  • 虚拟DOM和真实DOM本质上都是js对象,但是console.log打印真实DOM呈现出的是element
  • 虚拟DOM的属性数量或者API数量要远远少于真实DOM,所以虚拟DOM更加轻量级
  • 虚拟DOM只在react内部使用,必须由react映射成真实DOM才呢个呈现在页面上
  • 可以用typeof来获取某变量的类型
  • 可以用instanceof来判断某变量是不是某类型

JSX的语法规则

当我们对比了虚拟DOM和真实DOM之后,我们就要继续往下开始后面的课程了,都知道创建虚拟DOM有两种方法,而最高效而且方便的就是JSX,那么JSX语法是什么样的呢?都有哪些规则呢?

  • jsx是类似于XMLjs扩展语法
  • jsx的本质是React.createElement(compoment,props,...children)方法的语法糖。
  • jsx可以使用HTML标签,但是jsx表达式中的的标签不是HTML/XML标签
  • jsx内需要通过{}来对js表达式进行支持。
  • jsx任何标签中class属性的指定必须用className来指定
  • jsx内联样式必须使用style={{key: value}}的方式来写
  • jsx虚拟DOM只允许有一个根标签。
  • jsx虚拟DOM中的任何一个标签都必须闭合。
  • jsx中的标签在被渲染时会检查标签首字母
    • 如果jsx中标签首字母小写,那么则将该标签渲染为HTML中同名标签,若无同名标签则会报错。
    • 如果jsx中标签首字母大写,那么react就去渲染对应的组件,若该组件没有定义,则报错。

组件与模块

当我们了解了JSX的语法规则之后,又做了一个小练习。但是我们渐渐的发现,好像当我们代码量或者功能点过多时好像也还挺麻烦的,于是react中的另一个核心的东西出现了。就是组件。那么什么是组件,怎么用呢?

  • 向外提供特定功能的js程序,一般就是一个js文件叫做模块
  • 用来实现局部功能效果的代码和资源集合(HTML,CSS,JS等等)叫做组件
  • 当应用使用组件和模块进行编程,就叫组件化和模块化
  • 组件分为函数式组件和类式组件
  • 函数式组件的本质就是一个函数
  • 类式组件本质就是一个类
  • 定义组件首字母必须大写
  • 定义函数式组件必须有返回值
  • 渲染组件必须写成标签形式

复习类的相关知识

我们刚一引入组件这个概念的时候,按照惯例都是先学习简单的,所以就先介绍了函数式组件,但是实际上,类组件才是我们的重中之重,倒不是说函数式组件不重要,也重要,但是类组件更重要。那么工欲善其事,必先利其器。所以我们在学习类组件之前复习了一下原生js中类的相关知识

  • 类中的构造器并不是必须要写的
  • 要对实例进行一些初始化的操作才需要写构造器
  • 如果A继承了B类,那么A类的构造器方法必须在第一步就调用super方法
  • 类中定义的方法是放在了类的原型对象上供实例对象来使用。
  • 类的实例对象在调用方法时会优先从自身原型对象上查找方法,如果没有找到则通过原型链去父类的原型对象中去查找

类组件

在我们终于复习完了关于类的相关知识之后,那么我们就要正式开始学习类组件了,那么类组件有是什么样子的呢?都有哪些规则呢?

  • 要使用类组件就要定义类
  • 类组件创建的类必须继承React.Component
  • 类组件创建的类里面必须要有一个render方法
  • render方法一定要有返回值。
  • render方法存放在类的原型对象中
  • react在渲染类组件时会自动实例化类

组件的三大核心属性

我们其实在刚一接触到组件的时候就提到过组件有三大核心属性。那么这三大属性都是什么呢?各种都有什么用途和规则?

  • 组件有简单组件和复杂组件之分
  • 简单组件没有状态
  • 复杂组件有状态
  • 状态里面存储页面所需要的数据,数据更新则驱动页面展示
  • 状态在类组件实例对象上
  • 我们所说的组件的三大属性,实际上是组件实例的三大属性
  • state属性
  • 如果通过state驱动页面需要借助构造器方法
  • 构造器方法的参数官方规定的是props
  • 构造器方法要将props参数传给super
  • state要传一个对象
  • 通过this来调用state
  • react不支持直接修改state,必须通过setState方法来修改state
  • setState存放在React.Component的原型对象上
  • setState方法接收的参数是一个对象
  • setState方法并不是直接全局替换掉原来的state而是合并
  • 整个过程中类组件的构造器方法只被调用一次
  • render方法被调用1 + n
  • 标准写法比较繁琐,在实际开发中会拖慢我们的进度,所以要进行精简
  • 精简后的代码结构分为三个部分,就目前来说,不需要构造器,至于后面要不要我们再说
    • 赋值语句直接初始化状态
    • render方法
    • 自定义方法,所有自定义方法全部使用赋值语句加箭头函数来写
  • 箭头函数自身没有this
  • 箭头函数中如果要使用this,那么就会去查找箭头函数外层的this并作为自己的this来使用
  • props属性
  • props是一个对象
  • props是通过外面往组件内传入数据
  • props在组件的实例对象上可以通过this调用
  • props可以在组件标签处通过标签属性传入
  • props可以在标签中使用展开运算符展开一个对象来批量传入属性
  • props在使用展开运算符时必须确保被展开对象中的属性和解构props时的变量一致才能正常取数
  • 展开运算符在原生js语法中不能展开对象
  • babelreact库支持仅在组件标签批量传入属性时展开对象
  • props限制并不是必须的
  • 限制props必须引入prop-types
  • 限制可以避免一些不必要的错误
  • 限制有三种场景
    • 限制属性是否必须非空
    • 限制属性类型
    • 限制属性默认值
  • 限制属性类型为函数时,不能用function而要用func
  • propsTypesdefaultProps必须是组件类自身的属性,不是实例对象的属性
  • static关键字给类自身添加属性
  • props是只读的
  • 类组件不写构造器完全可以,而且能不写就尽量不写
  • 构造器中props传给super和不传给super的区别就是在构造器中能否通过this访问到props
  • 函数式组件目前只能使用props
  • 函数式组件使用参数来接收props
  • 函数式组件可以限制props但是只能写在函数体外
  • refs属性
  • refs是一个收集各个标签ref属性来作为标识的一个对象
  • refs可以代替document.getElementById方法来获取页面节点
  • refs可以配合事件处理来完成相应的功能需求。
  • 回调式refs不会自动收集到this.refs
  • 字符串式refs有问题,后期可能会废弃
  • 回调式refs所传入的ref属性是一个回调函数
  • 回调式refs是将传入的标签节点传入回调函数,并通过回调函数将标签节点挂在实例自身
  • 回调式refs会在页面第一次被渲染时调用一次
  • 如果回调式refs是内联函数形式,那么在state更新之后,会被重新调用两次
  • 内联函数形式的回调式refs调用次数对程序不会有影响
  • 类组件绑定函数的方式可以解决refs被多次调用的问题
  • createRef可以返回一个容器用于存放被ref属性标识的DOM节点
  • createRef返回的容器“专人专用”
  • 如果多个标签要被标识,就要创建多个容器

react事件绑定与事件处理

在我们学习三大属性时不知道大家还记不记得,我们的点击事件中出了一些问题。到了后来我们学完三大属性之后又对事件处理做了一些介绍。

  • 原生js添加点击事件有三种方法
  • addEventListener方法
  • onclick方法
  • onclick属性配合回调函数
  • react中允许使用原生js中的方法
  • react不推荐使用addEventListeneronclick,大力推崇onClick属性配合回调函数
  • reactonClick属性中click首字母必须大写
  • onClick属性不能用引号括起来,需要用{}括起来
  • onClick属性必须是函数,所以函数名后面不可以加()
  • react类组件中推荐将类组件需要用到的函数全部包含在类里,以方法的形式来写
  • 类组件中的方法要通过this调用
  • 类方法赋值给变量或者作为事件监听的回调的话,对该方法的调用属于函数直接调用而不是通过类的实例对象调用
  • 类中所有方法都默认局部开启了strict模式
  • bind(this)做了两件事
  • 生成新函数
  • 修改新函数中的this指向
  • 实例对象调用属性和方法会先从自身查找,自身没有才会顺着原型链去查找原型对象
  • this.notify = this.notify.bind(this)是将原型对象上的notify方法生成新函数并修改this指向之后存放在实例对象自身
  • 不要过度使用ref
  • 事件监听要严格注意大小写
  • 通过event.target获取到事件发生的DOM元素

受控组件与非受控组件

随着我们学习的东西越来越多,我们所接触到的需求也就会越来越复杂。那么我们就遇到一些需求需要使用受控组件来完成,那么有受控组件自然也就会有非受控组件。那么这俩又分别是什么东西呢?

我们通过一个表单的案例得出了结论: 现用现取则是非受控,而在输入过程中就取值并维护到state中的就是受控。

高阶函数

我们在学习完受控和非受控组件之后我们又通过案例学习了关于高阶函数的使用

  • 高阶函数有两个评判标准,满足其中一个就可以
  • A 函数接收的参数是一个函数
  • A 函数的返回值是一个函数
  • 通过函数调用继续返回函数的方式实现多次接收函数最后统一处理的函数形式叫做函数的柯里化。

引出生命周期

当然前面几节或许有人在觉得这仿佛是题外话,那么我们就来说说不是题外话的东西。生命周期。我们通过一个案例来引出了生命周期的一个简约的轮廓。

  • 组件渲染就是组件挂载,清空组件就是组件卸载
  • 组件在挂载完成之后会自动调用componentDidMount方法
  • 组件在卸载之前会自动调用componentWillUnmount

旧版生命周期

  • 单一组件挂载到卸载整个生命周期中有 5 个生命周期钩子被按顺序调用
  • constructor构造器方法
  • 在组件即将挂载之前调用componentWillMount方法
  • 调用render方法来挂载组件
  • 完成组件挂载后立即调用componentDidMount
  • 即将卸载前调用componentWillUnmount方法
  • 组件中各个生命周期钩子的定义顺序对程序执行没有影响,生命周期钩子执行的顺序依赖与生命周期的流程
  • 组件完成挂载并执行完了componentDidMount之后按顺序执行了 4 个生命周期钩子
  • 通过shouldComponentUpdate方法判断是否允许更新
  • 通过componentWillUpdate方法做更新前的准备工作
  • 通过render方法更新组件
  • 更新组件之后立即调用componentDidUpdate方法
  • shouldComponentUpdate必须返回bool类型
  • 如果没有写shouldComponentUpdate方法,react会自动补齐该方法,并且默认该方法返回true
  • forceUpdatesetState少一个shouldComponentUpdate环节
  • forceUpdatesetState的本质区别就是forceUpdate不对state做任何更改
  • forceUpdate不受shouldComponentUpdate方法的限制
  • 只有当父组件的render方法被重新调用才会触发更新流程
  • 只有当父组件的render方法被重新调用才会触发componentWillReceiveProps的调用
  • 当父组件被重新调用的时候,子组件不会被重新挂载
  • componentWillReceiveProps方法在子组件接收到props之前调用

新旧生命周期的对比

在深入学习新版生命周期之前,我们把新旧生命周期在一起对比着介绍了一下。

  • 新版本生命周期可以使用旧版本的钩子
  • 新版本中废弃了三个旧版本中的钩子,使用这三个钩子要加UNSAFE前缀
  • componentWillMount
  • componentWillUpdate
  • componentWillReceiveProps
  • 新版本中新增了两个钩子
  • getDerivedStateFromProps
  • getSnapshotBeforeUpdate

新版生命周期

那么既然做了对比,新版生命周期中的变化是什么样子的呢

  • 仅当state值在任何时候都完全取决于props是才使用getDerivedStateFromProps钩子
  • 派生state会导致代码冗余且组件难以维护
  • 使用了派生state之后,将无法再正常通过state驱动页面更新
  • getSnapshotBeforeUpdate钩子在rendercomponentDidUpdate
  • getSnapshotBeforeUpdate钩子必须要有返回值,可以为不是undefined的任何值
  • getSnapshotBeforeUpdate钩子的返回值会顺流传给componentDidUpdate
  • componentDidUpdate会接收组件更新前的snapshotpropsstate

Diffing算法

其实以上的这些内容就已经算是学完了react的基础知识了,但是我们还是对开篇所说的diffing算法有点好奇,那么我们就展开讨论了一些关于diffing算法的内容。

  • diffing算法会来对比新旧虚拟DOM
  • diffing算法通过key来比对新旧真实DOM
  • 尽量使用数据唯一标识来做key

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/review_react_basic/