React基础总结

React是什么?

React是什么呢?这里我们来看一下官方对它的解释:用于构建用户界面的JavaScript库。

React是2013年,Facebook开源的JavaScript框架,那么当时为什么Facebook要推出这样一款框架呢?

这个源于一个需求,所产生的bug:

该功能上线之后,总是出现bug,三个消息的数字在发生变化时,过多的操作很容易产生bug。bug是否可以修复呢?当然可以修复,但是Facebook的工程师并不满足于此,他们开始思考为什么会产生这样的问题,在传统的开发模式中,我们过多的去操作界面的细节(需要掌握和使用大量DOM的API),另外关于数据(状态),往往会分散到各个地方,不方便管理和维护。

他们就去思考,是否有一种新的模式来解决上面的问题:

  1. 以组件的方式去划分一个个功能模块

  2. 组件内以jsx来描述UI的样子,以state来存储组件内的状态

  3. 当应用的状态发生改变时,通过setState修改状态,状态发生变化时,UI会自动发生更新

React的特点

声明式编程

  1. 声明式编程是目前整个大前端开发的模式
  2. 它允许我们只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染我们的UI界面

组件化开发

   组件化开发页面目前前端的流行趋势,我们会把复杂的界面拆分成一个个小的组件

多平台适配

  1. 2013年,React发布之初主要是开发Web页面;
  2.  2015年,Facebook推出了ReactNative,用于开发移动端跨平台
  3. 2017年,Facebook推出ReactVR,用于开发虚拟现实Web应用程序

React开发依赖

开发React必须依赖三个库:

  1.  react:包含react所必须的核心代码

  2. react-dom:react渲染在不同平台所需要的核心代码

  3. babel:将jsx转换成React代码的工具

这三个库是各司其职的,目的就是让每一个库只单纯做自己的事情,在React的0.14版本之前是没有react-dom这个概念的,所有功能都包含在react里。为什么要进行拆分呢?原因就是react-native。react包中包含了react和react-native所共同拥有的核心代码。react-dom针对web和native所完成的事情不同:

  1. web端:react-dom会讲jsx最终渲染成真实的DOM,显示在浏览器中
  2. native端:react-dom会讲jsx最终渲染成原生的控件

认识Babel

babel是什么呢?

Babel ,又名 Babel.js。是目前前端使用非常广泛的编辑器、转移器。比如当下很多浏览器并不支持ES6的语法,但是确实ES6的语法非常的简洁和方便,我们开发时希望使用它。那么编写源码时我们就可以使用ES6来编写,之后通过Babel工具,将ES6转成大多数浏览器都支持的ES5的语法。

React和Babel的关系

默认情况下开发React其实可以不使用babel。但是前提是我们自己使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差。那么我们就可以直接编写jsx的语法,并且让babel帮助我们转换成React.createElement。 

引入React依赖

我们在编写React代码时,这三个依赖都是必不可少的。可以通过以下方式添加这三个依赖:

  1. 方式一:直接CDN引入
  2. 方式二:下载后,添加本地依赖
  3. 方式三:通过npm管理(后续脚手架再使用)

通过CDN引入,注意引入的顺序。这里有一个crossorigin的属性,这个属性的目的是为了拿到跨域脚本的错误信息

<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>

  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>

<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
复制代码

在界面上通过React显示一个Hello World,注意:这里我们编写React的script代码中,必须添加 type=”text/babel”,作用是可以让babel解析jsx的语法。

ReactDOM.render函数:

参数一:传递要渲染的内容,这个内容可以是HTML元素,也可以是React的组件

参数二:将渲染的内容,挂载到哪一个HTML元素上

组件化开发

我们说过 ReactDOM.render 第一参数是一个HTML原生或者一个组件,所以我们可以先将之前的业务逻辑封装到一个组件中,然后传入到 ReactDOM.render 函数中的第一个参数。

在React中,如何封装一个组件呢?

使用类的方式封装组件

  1. 定义一个类(类名大写,组件的名称是必须大写的,小写会被认为是HTML元素),继承自React.Component

  2. 实现当前组件的render函数(render当中返回的jsx内容,就是之后React会帮助我们渲染的内容)

数据依赖

数据在哪里定义?在组件中的数据,我们可以分成两类:

  1. 参与界面更新的数据:当数据变量时,需要更新组件渲染的内容
  2. 不参与界面更新的数据:当数据变量时,不需要更新将组建渲染的内容

参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的state中。

  • 可以通过在构造函数中 this.state = {定义的数据}
  • 当数据发生变化时,可以调用 this.setState 来更新数据,并且通知React进行update操作(在进行update操作时,就会重新调用render函数,并且使用最新的数据,来渲染界面)

事件绑定

事件绑定中的this。在类中直接定义一个函数,并且将这个函数绑定到html原生的onClick事件上,当前这个函数的this指向的是谁呢?

默认情况下是undefined。很奇怪,居然是undefined;因为在正常的DOM操作中,监听点击,监听函数中的this其实是节点对象(比如说是button对象);因为React并不是直接渲染成真实的DOM,我们所编写的button只是一个语法糖,它的本质React的Element对象;那么在这里发生监听的时候,react给我们的函数绑定的this,默认情况下就是一个undefined; 

在绑定的函数中,可能想要使用当前对象,比如执行 this.setState 函数,就必须拿到当前对象的this,就需要在传入函数时,给这个函数直接绑定this,类似于下面的写法: 改变文本

认识JSX

这段element变量的声明右侧赋值的标签语法是什么呢?

它不是一段字符串(因为没有使用引号包裹),它看起来是一段原生HTML,但是我们能在js中直接给一个变量赋值html吗?其实是不可以的,如果我们把 type=”text/babel” 去除掉,那么就会出现语法错误;它到底是什么呢?其实它是一段jsx的语法;

JSX是什么?

  • JSX是一种JavaScript的语法扩展(eXtension),也在很多地方称之为JavaScript XML,因为看起就是一段XML语法
  • 它用于描述我们的UI界面,并且其完成可以和JavaScript融合在一起使用

为什么React选择了JSX

  • React认为渲染逻辑本质上与其他UI逻辑存在内在耦合;比如UI需要绑定事件(button、a原生等等); 比如UI中需要展示数据状态,在某些状态发生改变时,又需要改变UI;

  • 它们之间是密不可分,所以React没有讲标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件(Component);

JSX的书写规范

  • JSX的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个div原生(或者使用Fragment);
  • 为了方便阅读,我们通常在jsx的外层包裹一个小括号(),这样可以方便阅读,并且jsx可以进行换行书写;
  • JSX中的标签可以是单标签,也可以是双标签;(注意:如果是单标签,必须以/>结尾)

JSX的使用

 JSX嵌入变量

  • 情况一:当变量是Number、String、Array类型时,可以直接显示

  • 情况二:当变量是null、undefined、Boolean类型时,内容为空

  • 情况三:对象类型不能作为子元素(not valid as a React child)

JSX嵌入表达式

  • 运算表达式

  • 三元运算符

  • 执行一个函数

jsx绑定属性

  • 比如元素都会有title属性
  • 比如img元素会有src属性
  • 比如a元素会有href属性
  • 比如元素可能需要绑定class
  • 比如原生使用内联样式style

React事件绑定

如果原生DOM原生有一个监听事件,我们可以如何操作呢?

  • 方式一:获取DOM原生,添加监听事件;
  • 方式二:在HTML原生中,直接绑定onclick;

在React中是如何操作呢?React中的事件监听,这里主要有两点不同

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写
  • 需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行

this的绑定问题

在事件执行后,我们可能需要获取当前类的对象中相关的属性,这个时候需要用到this,如果我们这里直接打印this,也会发现它是一个undefined。

为什么是undefined呢?

原因是btnClick函数并不是我们主动调用的,而且当button发生改变时,React内部调用了btnClick函数;而它内部调用时,并不知道要如何绑定正确的this;

如何解决this的问题呢?

方案一:bind给btnClick显示绑定this

方案二:使用 ES6 class fields 语法

方案三:事件监听时传入箭头函数(推荐)

事件参数传递

在执行事件函数时,有可能我们需要获取一些参数信息:比如event对象、其他参数

  • 情况一:获取event对象

很多时候我们需要拿到event对象来做一些事情(比如阻止默认行为)

假如我们用不到this,那么直接传入函数就可以获取到event对象;

  • 情况二:获取更多参数

有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;

React条件渲染

某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容:

在vue中,我们会通过指令来控制:比如v-if、v-show; 在React中,所有的条件判断都和普通的JavaScript代码一致;

常见的条件渲染的方式有哪些呢?

  • 方式一:条件判断语句

适合逻辑较多的情况

  • 方式二:三元运算符

适合逻辑比较简单

  • 与运算符&&

适合如果条件成立,渲染某一个组件;如果条件不成立,什么内容也不渲染; 

React列表渲染

真实开发中我们会从服务器请求到大量的数据,数据会以列表的形式存储:比如歌曲、歌手、排行榜列表的数据;比如商品、购物车、评论列表的数据;比如好友消息、动态、联系人列表的数据;

在React中并没有像Vue模块语法中的v-for指令,而且需要我们通过JavaScript代码的方式组织数据,转成JSX。很多从Vue转型到React的同学非常不习惯,认为Vue的方式更加的简洁明了;但是React中的JSX正是因为和JavaScript无缝的衔接,让它可以更加的灵活;

如何展示列表呢?

在React中,展示列表最多的方式就是使用数组的map高阶函数;

很多时候我们在展示一个数组中的数据之前,需要先对它进行一些处理:

比如过滤掉一些内容:filter函数

比如截取数组中的一部分内容:slice函数

我们会发现在前面的代码中只要展示列表都会报一个警告:

这个警告是告诉我们需要在列表展示的jsx中添加一个key。

JSX的本质

实际上,jsx 仅仅只是 React.createElement(component, props, …children) 函数的语法糖。所有的jsx最终都会被转换成React.createElement的函数调用。

createElement需要传递三个参数:

  • 参数一:type

当前ReactElement的类型;如果是标签元素,那么就使用字符串表示 “div”;如果是组件元素,那么就直接使用组件的名称;

  • 参数二:config

所有jsx中的属性都在config中以对象的属性和值的形式存储

  • 参数三:children

存放在标签中的内容,以children数组的方式进行存储;当然,如果是多个元素呢?React内部有对它们进行处理

直接编写jsx代码

我们自己来编写React.createElement代码:

我们就没有通过jsx来书写了,界面依然是可以正常的渲染。

另外,在这样的情况下,你还需要babel相关的内容吗?不需要了

所以,type=”text/babel”可以被我们删除掉了;

所以,可以被我们删除掉了;

虚拟DOM的创建过程

我们通过 React.createElement 最终创建出来一个 ReactElement对象:

这个ReactElement对象是什么作用呢?React为什么要创建它呢?

  • 原因是React利用ReactElement对象组成了一个JavaScript的对象树;
  • JavaScript的对象树就是大名鼎鼎的虚拟DOM(Virtual DOM);

如何查看ReactElement的树结构呢?我们可以将之前的jsx返回结果进行打印;注意下面代码中我打jsx的打印;

而ReactElement最终形成的树结构就是Virtual DOM

为什么使用虚拟DOM

为什么要采用虚拟DOM,而不是直接修改真实的DOM呢?

  • 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试;
  • 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低;

DOM操作性能非常低:

  • 首先,document.createElement本身创建出来的就是一个非常复杂的对象;
  • 其次,DOM操作会引起浏览器的回流和重绘,所以在开发中应该避免频繁的DOM操作;

频繁操作DOM的问题

我们举个例子:比如我们有一组数组需要渲染:[0, 1, 2, 3, 4],我们会怎么做呢?我们可以通过ul和li将它们展示出来。

后来,我们又增加了5条数据:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

  • 方式一:重新遍历整个数组(不推荐)
  • 方式二:在ul后面追加另外5个li

上面这段代码的性能怎么样呢?非常低效。因为我们通过 document.createElement 创建元素,再通过 ul.appendChild(li) 渲染到DOM上,进行了多次DOM操作;对于批量操作的,最好的办法不是一次次修改DOM,而是对批量的操作进行合并;(比如可以通过DocumentFragment进行合并);而我们正式可以通过 Virtual DOM来帮助我们解决上面的问题;

声明式编程

虚拟DOM帮助我们从命令式编程转到了声明式编程的模式

React官方的说法:Virtual DOM 是一种编程理念。

在这个理念中,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象

我们可以通过ReactDOM.render让 虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调(Reconciliation);

这种编程的方式赋予了React声明式的API:

  • 你只需要告诉React希望让UI是什么状态;
  • React来确保DOM和这些状态是匹配的;
  • 你不需要直接进行DOM操作,可以从手动更改DOM、属性操作、事件处理中解放出来;

React脚手架

React脚手架本身需要依赖node,所以我们需要安装node环境

官网地址:nodejs.org/en/download…,此处就不过多介绍安装步骤,安装后只要在命令行输入node -v显示版本就说明安装成功。

最后一个需要安装的是创建React项目的脚手架

npm install -g create-react-app

创建React项目

现在,我们就可以通过脚手架来创建React项目了。创建React项目的命令如下:create-react-app 项目名称

注意:项目名称不能包含大写字母,另外还有更多创建项目的方式,可以参考GitHub的readme。创建完成后,进入对应的目录,就可以将项目跑起来:npm start

目录结构分析

了解PWA

整个目录结构都非常好理解,只是有一个PWA相关的概念:

  • PWA全称Progressive Web App,即渐进式WEB应用;
  • 一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用;
  • 随后添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能;
  • 这种Web存在的形式,我们也称之为是 Web App;

PWA解决了哪些问题呢?

  • 可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏;
  • 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能;
  • 实现了消息推送;等等一系列类似于Native App相关的功能;

React的组件化

组件化是React的核心思想,也是重点,组件化提供了一种抽象,让我们可以开发出一个个独立可复用的小组件来构造我们的应用。任何的应用都会被抽象成一颗组件树。

组件化思想的应用:

  • 有了组件化的思想,我们在之后的开发中就要充分的利用它。
  • 尽可能的将页面拆分成一个个小的、可复用的组件。
  • 这样让我们的代码更加方便组织和管理,并且扩展性也更强。

React的组件相对于Vue更加的灵活和多样,按照不同的方式可以分成很多类组件:

  • 根据组件的定义方式,可以分为:函数组件(Functional Component )和类组件(Class Component);
  • 根据组件内部是否有状态需要维护,可以分成:无状态组件(Stateless Component )和有状态组件(Stateful Component);
  • 根据组件的不同职责,可以分成:展示型组件(Presentational Component)和容器型组件(Container Component);

这些概念有很多重叠,但是他们最主要是关注数据逻辑和UI展示的分离:

  • 函数组件、无状态组件、展示型组件主要关注UI的展示;
  • 类组件、有状态组件、容器型组件主要关注数据逻辑;

类组件

类组件的定义有如下要求:

  • 组件的名称是大写字符开头(无论类组件还是函数组件)
  • 类组件需要继承自 React.Component
  • 类组件必须实现render函数

在ES6之前,可以通过create-react-class 模块来定义类组件,但是目前官网建议我们使用ES6的class类定义。

使用class定义一个组件:

  • constructor是可选的,我们通常在constructor中初始化一些数据;
  • this.state中维护的就是我们组件内部的数据;
  • render() 方法是 class 组件中唯一必须实现的方法;

render函数的返回值

当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:

  • React 元素:通常通过 JSX 创建。例如,
    会被 React 渲染为 DOM 节点, 会被 React 渲染为自定义组件;无论是

    还是 均为 React 元素。

    © 版权声明
    THE END
    喜欢就支持一下吧
    点赞0 分享