一、开发前的准备
1. 开发环境
(1)Node.js
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。React 应用并不依赖于 Node.js 运行,但是开发过程中的一些编译过程(比如 npm,Webpack 等)都需要在 Node 环境下运行。因此,开发 React 应用前,应确保已经安装了 Node.js。
(2)NPM
NPM 是 Node 的一个包管理工具,每个包都是一个模块,能够使你轻松下载、管理模块依赖和版本。同样的,在使用 React 开发应用时,会依赖很多模块,这些模块就可以通过 NPM 进行下载。由于 NPM 已集成到了 Node.js 中,因此不用单独下载。
2. 开发工具
(1)Webpack
Webpack 是一个前端资源加载和打包工具。Webpack 提供了模块化的开发方式,将各种静态资源视为模块,如 JavaScript、CSS、图片等,并通过 Webpack 生成优化过的代码。同样在开发 React 应用时也要用到 Webpack 来进行模块打包。
(2)Babel
Babel 是一个 JavaScript 编译器,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
由于我们在开发 React 应用中,会用到很多 ES6 的语法,但目前浏览器并不完全支持,因此在 Webpack 编译阶段,利用 Babel 将 ES6 及其以后的语法编译成 ES5 语法。
3. 开发依赖
开发 React 必须依赖三个库:
● react
:包含 react 所必须的核心代码
● react-dom
:react 渲染在不同平台所需要的核心代码
● babel
:将 jsx 转换成 React 代码的工具
react-dom 针对 web 和 native 所完成的事情不同:
● web端
:react-dom 会讲 jsx 最终渲染成真实的 DOM,显示在浏览器中
● native端
:react-dom 会讲 jsx 最终渲染成原生的控件(比如 Android 中的 Button,iOS 中的 UIButton)。
Babel ,又名 Babel.js,是目前前端使用非常广泛的编辑器、转移器。比如当下很多浏览器并不支持 ES6 的语法,但是确实 ES6 的语法非常的简洁和方便,我们开发时希望使用它。那么编写源码时我们就可以使用 ES6 来编写,之后通过 Babel 工具,将 ES6 转成大多数浏览器都支持的 ES5 的语法。
React
和Babel
的关系:
● 默认情况下开发 React 其实可以不使用 babel。
● 但是前提是我们自己使用 React.createElement 来编写源代码,它编写的代码非常的繁琐和可读性差。
● 那么我们就可以直接编写 jsx(JavaScript XML)的语法,并且让 babel 帮助我们转换成 React.createElement。
二、React 脚手架
现代的前端项目已经越来越复杂了:
● 不会再是在 HTML 中引入几个 css 文件,引入几个编写的 js 文件或者第三方的 js 文件这么简单;
● 比如 css 可能是使用 less、sass 等预处理器进行编写,我们需要将它们转成普通的 css 才能被浏览器解析;
● 比如 JavaScript 代码不再只是编写在几个文件中,而是通过模块化的方式,被组成在成百上千个文件中,我们需要通过模块化的技术来管理它们之间的相互依赖;
● 比如项目需要依赖很多的第三方库,如何更好的管理它们(比如管理它们的依赖、版本升级等);
为了解决这些问题,前端脚手架就出现了。例如 babel、webpack、gulp 等等,可以通过它们来进行转换规则、打包依赖、热更新等等。总之,脚手架让项目从搭建到开发,再到部署,整个流程变得快速和便捷。
目前比较流行的的框架 Vue,它的脚手架是 vue-cli;React 的脚手架就是 create-react-app。它们的作用就是帮助我们生成一个通用的目录结构,并且已经将我们所需的工程环境配置好了。目前,这些脚手架都是使用 node.js 编写的,并且都是基于 Webpack 的,它们需要运行在 Node 环境下。
上面我们也说到了 NPM 了,它是 Node 的一个包管理工具,每个包都是一个模块,能够使你轻松下载、管理模块依赖和版本。同样的,在使用 React 开发应用时,会依赖很多模块,这些模块就可以通过 NPM 进行下载。由于 NPM 已集成到了 Node.js 中,因此不用单独下载。
除了 NPM,还有一个比较出名的 Node 包管理工具 yarn,它是为了弥补 NPM 的一些缺陷而出现的,早期的 NPM 存在很多的缺陷,比如按安装依赖速度很慢,版本依赖混乱等等一系列的问题,虽然后面的版本进行了很多的升级和改进,但还是很多人喜欢使用 yarn,React 脚手架也默认使用 yarn。
安装 yarn:
1 | npm install -g yarn |
安装好 yarn 就可以使用了,下面来看一下 npm 和 yarn 命令的对照表:
安装完之后,就可以安装 React 的脚手架了:
1 | npm install -g create-react-app |
安装好之后,可以运行:create-react-app --version
查看版本号,如果版本号正常展示,证明安装成功。
三、创建 React 项目
这里我们借助上面所说的的 React 脚手架 Create React App 创建 React 应用。该脚手架已经将 Webpack、Babel 等工具的配置做了封装,无需开发者做配置,提供了一个零配置的现代构建。
Create React App 对于开发环境版本有一定的要求,具体如下:
npm 版本 >= 5.6
node 版本 >= 8.10
快速搭建 React 应用需要三个步骤:
1. 创建 React 项目;
2. 启动项目;
3. 暴露配置项。
(1)创建 React 项目
1 | create-react-app my-demo |
这里项目名不能使用驼峰的形式(即不能包含大写字母),不然会有以下报错:name can no longer contain capital letters。
(2)启动项目
1 | cd my-demo |
当看到以下界面时,说明你的 React 应用就已经安装好了。
1 | ├── README.md 文档 |
在 src 文件夹有一个serviceWorker.js
文件,在 public 文件有一个manifest.json
文件,他们涉及到了一个知识点——PWA,PWA 全称为 Progressive Web App,即渐进式 WEB 应用;一个 PWA 应用首先是一个网页, 可以通过 Web 技术编写出一个网页应用;添加上 App Manifest 和 Service Worker 来实现 PWA 的安装和离线等功能;这种 Web 存在的形式,我们也称之为是 Web App
。
PWA 解决了哪些问题呢?
● 可以添加至主屏幕,点击主屏幕图标可以实现启动动画以及隐藏地址栏;
● 实现离线缓存功能,即使用户手机没有网络,依然可以使用一些离线功能;
● 实现了消息推送;
还有类似一系列类似于 Native App 相关的功能。
(3)暴露配置项
1 | npm run eject |
在初始化好的项目中,Webpack
等配置默认都是隐藏的,要想看到这些配置,就要执行以上命令来暴露项目的配置项。
暴露配置项之后,项目根目录又多出两个文件:scripts
和config
,他们都是被隐藏的配置项,同时package.json
中的配置项也会变多,一些隐藏的配置项都暴露了出来。
四、React 初体验
下面我们就来用 react 实现一下 Hello World:
1 | 打开 src/index.js 文件,可以看到 render 的模版是 App.js,代码如下: |
其中,root
是 index.html
模版里的元素,渲染出来的 App 组件放在此处。
下面我们来尝试修改 App.js
文件:
1 | // src/App.js |
修改完保存之后,页面就会自动刷新,显示如下:
至此,我们就完成了一个 React 应用的创建。
那 React 的数据在哪里定义呢,Vue
是在data
中定义数据,而React
是在state
中定义数据。
在组件中的数据,我们可以分成两类:
● 参与界面更新的数据:当数据变量时,需要更新组件渲染的内容
● 不参与界面更新的数据:当数据变量时,不需要更新将组建渲染的内容
参与界面更新的数据我们也可以称之为是参与数据流,这个数据是定义在当前对象的 state 中
● 我们可以通过在构造函数中 this.state = {定义的数据}
● 当我们的数据发生变化时,我们可以调用 this.setState
来更新数据,并且通知 React 进行 update 操作
● 在进行 update 操作时,就会重新调用render
函数,并且使用最新的数据,来渲染界面
1 | class App extends React.Component { |
五、初识 JSX
1. 什么是 JSX
我们来看一个变量的声明:
1 | const element = <h1>Hello, world!</h1>; |
这个变量声明既不是字符串也不是 HTML,它被称为 JSX,是一种 JavaScript 的语法扩展(eXtension),也在很多地方称之为 JavaScript XML,因为看起就是一段 XML 语法。它用于描述我们的 UI 界面,并且其完成可以和 JavaScript 融合在一起使用;
2. 为什么使用 JSX
那为什么 React 要选择使用 JSX 呢?
(1) React 认为渲染逻辑本质上与其他 UI 逻辑存在内在耦合
● 比如 UI 需要绑定事件(button、a 原生等等);
● 比如 UI 中需要展示数据状态,在某些状态发生改变时,又需要改变 UI;
(2)它们之间是密不可分,所以 React 没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件(Component);
其实,JSX 是嵌入到 JavaScript 中的一种结构语法;
JSX 的优点:
● 使用熟悉的语法定义 HTML 元素,提供更加语义化的标签,使用 JSX 编写模板更简单快速;
● 更加直观:JSX 让小组件更加简单、明了、直观;
● 抽象了 React 元素的创建过程,使得编写组件变得更加简单;
● JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化;
● JSX 是类型安全的,在编译过程中就能发现错误;
● 防注入攻击,所有的内容在渲染之前都被转换成了字符串,可以有效地防止 XSS(跨站脚本) 攻击。
3. JSX 书写规范
JSX 的书写规范如下:
● JSX 的顶层只能有一个根元素,所以我们很多时候会在外层包裹一个 div 原生(或者使用 Fragment,文档片段);
● 为了方便阅读,我们通常在 jsx 的外层包裹一个小括号(),这样可以方便阅读,并且 jsx 可以进行换行书写;
● JSX 中的标签可以是单标签,也可以是双标签;注意:如果是单标签,必须以/>结尾;
● 在使用变量时,我们可以将其放在一个大括号中,大括号内放置任何有效的 JavaScript 表达式
下面来是一个计数器的例子:
1 | class App extends React.Component { |
4. JSX 注释
JSX 的注释和其他框架略有不同,通常有三种方法:
1 | render() { |
5. JSX 嵌入变量
在 JSX 中嵌入变量时,通常使用一个大括号包裹,不同类型的变量的显示效果是不一样的:
(1)情况一:当变量是 Number、String、Array 类型时,可以直接显示
(2)情况二:当变量是 null、undefined、Boolean 类型时,内容为空;
● 如果希望可以显示 null、undefined、Boolean,那么需要转成字符串;
● 转换的方式有很多,比如toString
方法、和空字符串拼接
,String(变量)
等方式;
(3) 情况三:对象类型不能作为子元素(not valid as a React child)
1 | class App extends React.Component { |
6. JSX 嵌入表达式
在 JSX 中嵌入表达式:
● 运算表达式
● 三元运算符
● 进行函数调用
1 | render() { |
7. JSX 绑定属性
常见的绑定的属性有以下几种:
● 元素都会有 title 属性
● img 元素会有 src 属性
● a 元素会有 href 属性
● 元素可能需要绑定 class
● 原生使用内联样式 style
我们可以使用大括号来给元素绑定属性:
1 | class App extends React.Component { |
注意:
● 上面我们在给元素绑定class
时,由于我们写的是 JSX 语法,它是和JavaScript
的语法混在一起的,所以我们如果直接使用 JavaScript 中的关键字class
就不太好,所以我们可以使用className
来代替class
,在label
标签中,通常使用的是的for
来绑定属性,但是 for 也是 JavaScript 中的关键字,所以使用htmlFor
来代替。
● 如果我们想要给className
添加值,就要使用 JavaScript 来给他动态添加。
● 在给元素绑定style
时,需要注意,外层的大括号是用 JSX 语法往内部嵌套 JavaScript 代码。而内部的大括号是一个对象,它里面是键值对,表示元素的样式属性及属性值。
● 在给元素绑定style
时,如果元素的属性值是一个值,就要加上双引号。当属性是由多个单词组成的时候,需要用驼峰命名法来表示,例如:fontSize
。
8. JSX 绑定事件
在 JSX 中绑定事件时,可以使用以下方式:
1 | <button onClick={this.btnClick}>按钮</button> |
可以看出 React 元素的事件处理和 DOM 元素的很相似,但存在一些语法上的差异:
● React 的事件采用驼峰式命名,而不是纯小写的方式;
● 使用 JSX 语法时,需要传入一个函数作为事件处理函数,而不是一个字符串;
● 使用函数时不能加括号,不然会直接执行。
需要注意的是,如果我们绑定的事件函数中,如果使用到了当前组件对象中的属性,我们无法使用 this 直接获取 state 中的数据。这是因为,我们当前调用的函数,不是在元素上直接调用它的,而是 React 的内部发现这个按钮绑定了一个事件,就会在内部对这个函数做了一个回调,他拿到这个方法 btnClick 之后,就对其使用了btnClick.call()
,并为其传递了一个参数undefined
,也就是在执行这个函数的时候,给它绑定了一个动态的this
,this
指向了undefined
。
注意:在 React 中不能使用 return false
的方式阻止事件的默认行为,必须要显式的调用事件对象的 preventDefault
方法来阻止事件的默认行为。
这里补充一下this 的指向问题:
在类中直接定义一个函数,并且将这个函数绑定到html
原生的onClick
事件上,当前这个函数的this
默认情况下指向是undefined
。因为在正常的 DOM 操作中,监听点击,监听函数中的 this 其实是节点对象(比如说是button
对象)。
因为 React 并不是直接渲染成真实的 DOM,我们所编写的 button 只是一个语法糖
,它的本质 React 的 Element 对象。那么在这里发生监听的时候,react 给我们的函数绑定的 this,默认情况下就是一个 undefined;
我们在绑定的函数中,可能想要使用当前对象,比如执行 this.setState
函数,就必须拿到当前对象的 this,我们就需要在传入函数时,给这个函数直接绑定 this,类似于下面的写法:
1 | <button onClick={this.changeText.bind(this)}>改变文本</button> |
我们希望绑定事件函数中的 this 指向这个组件对象,这里有三种解决方案:
(1)显示绑定
就像上面所说的,使用 bind 方法将这个函数的 this 绑定到当前组件对象的 this 上
1 | <button onClick={this.btnClick.bind(this)}>按钮</button> |
也可以在构造方法中,将该方法进行重新赋值
1 | constructor(props) { |
这种方法固然是可以实现,但是可能会产生很多重复的代码。
(2)使用箭头函数定义函数
因为箭头函数永远不绑定 this,它里面的 this 会读取上一层作用域的属性,如果没有找到就逐层向上查找。
1 | btnClick = () => {}; |
(3)传入一个箭头函数,在箭头函数中调用需要执行的函数(推荐使用)
1 | <button |
它的原理就是,当我们点击按钮时,就会执行箭头函数中的函数体,也就是执行事件绑定的方法。当执行这个方法的时候,由于箭头函数是不绑定 this 的,所以它会向上层作用域的render
中进行查找,这样就会将this
隐式绑定在事件函数上面。
在执行事件函数时,有可能我们需要获取一些参数信息:比如event
对象、其他参数
(1)情况一:获取event
对象
● 很多时候我们需要拿到event
对象来做一些事情(比如阻止默认行为)
● 假如我们用不到this
,那么直接传入函数就可以获取到 event 对象;
(2)情况二:获取更多参数
● 有更多参数时,我们最好的方式就是传入一个箭头函数,主动执行的事件函数,并且传入相关的其他参数;
9. React 条件渲染
某些情况下,界面的内容会根据不同的情况显示不同的内容,或者决定是否渲染某部分内容:
● 在 vue 中,我们会通过指令来控制:比如v-if
、v-show
;
● 在 React 中,所有的条件判断都和普通的JavaScript
代码一致;
常见的条件渲染的方式有以下三种:
● 方式一:条件判断语句
,适合逻辑较多的情况
● 方式二:三元运算符
,适合逻辑比较简单
● 方式三:与运算符&&
,如果条件成立,渲染&&后面的组件;如果条件不成立,什么内容也不渲染;
1 | render() { |
在 Vue 中,v-if
是对元素隐藏时,会将元素送 DOM 树上删除掉;而v-show
只是对元素进行隐藏,其原理就是给元素添加display:none
,它更适用于频繁切换某个元素的显示/隐藏的情况,会大大的节省开销。
那在上面我们对 React 元素的显示和隐藏都相当于 Vue 中v-if
,元素在隐藏之后就不会出现在 DOM 树上,我们可以用 React 来实现一下 Vue 中 v-show 指令的效果:
1 | class App extends React.Component { |
这样,元素隐藏之后还是会在 DOM 树上,只是不会显示在页面上,节省了开销,提高了页面的性能。
10. React 列表渲染
在实际的开发过程中,我们通常会将请求的大量数据渲染成列表,在 Vue 中,我们可以使用v-for
进行列表的渲染,而在 React 中需要我们通过 JavaScript 代码的方式组织数据,转成 JSX。
在 React 中,展示列表最多的方式就是使用数组的 map 方法,使用它来遍历数组元素,也可以在遍历的过程中对数组元素进行操作;除此之外,为我们还会用到 filter 方法过滤数据,使用 slice 方法截取数据等等。
1 | <ul> |
需要注意的是,在渲染是,我们需要给渲染项添加一个 key,不然会报错:warning:Each child in a list should have a unique “key” prop.
在 React 中,key 属性是给 React 自己用的一个特殊属性,就是说即使为一个组件设置 key 之后,我们也无法获取这个组件的 key 值。它是一种身份标识,每个 key 对应一个组件。key 的值必须保证唯一且稳定,它类似于数据库中的主键 id 一样,有且唯一。 key 和 React 中的 diff 算法密切相关。
11. React 表单处理
React 的组件分为受控组件和非受控组件:
● 受控组件:由 React 来管理表单元素的值,同时表单元素的变化需要实时映射到 React 的 state 中,这个类似于双向数据绑定。不同的表单元素,React 控制方式是不一样的,如 input 用 value 来控制,checkbox 用 checked 来判断是否选中等。
● 非受控组件:非受控组件,表单数据将交由 DOM 节点来处理。React 提供了一个 ref 属性,用来从 DOM 节点中获取表单数据。
受控组件:
常见的受控组件有文本框、单选框、复选框、下拉列表等。
(1)文本框
文本框包含类型为 text
、 number
的 input
元素和 textarea
元素。这些被受控的主要原理是通过元素的 value
属性设置表单元素的值,通过表单元素的 onChange
事件监听值的变化,并同步到 React 组件的 state 中。
当用户输入框内容改变的时候, onChange
事件会被触发,进而相应的处理函数会把值的变化通过 setState 同步到组件的 state 中,同时由于 state 值的变化,又会重新渲染表单,进而实现对表单元素状态的控制。
(2)单选框
在 HTML 中, <input type="radio">
标签可以创建单选框,由标签上的 checked
属性决定是否选中,在 React 中也是一样。
(3)复选框
在 HTML 中, <input type="checkbox">
标签可以创建复选框,由标签上的 checked
属性决定是否选中,在 React 中也是一样。
(4)下拉列表
在 HTML 中, <select>
标签可以创建下拉列表,选中项由 <option>
上的 selected
属性决定。需要注意的是 React 并不会使用 selected 属性,而是在根 <select>
标签上使用 value
属性。
注意:select 标签可以使用多选,只需在标签中加一个 multiple = {true}
,同时 React 状态变量使用数组,当然 onChange
处理函数也需按数组处理。
1 | class Register extends React.Component { |
非受控组件:
受控组件保证了表单元素的状态统一由 React 管理,但同时每个表单元素都需要通过 onChange
事件来绑定处理函数,然后将更改同步到 React 组件的 state
中,这个过程还是比较繁琐的。一种可替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。React 提供了一个 ref
属性,用来从 DOM 节点中获取表单数据。
1 | class Login extends React.Component { |
在填写完表单之后,我们通常会对表单进行校验,以确保表单数据格式准确。最常见的两种检验就是失去焦点校验和提交时校验。
(1)失去焦点校验
在失去焦点时加上校验规则,那么就需要监听 onBlur 事件,然后在 onBlur 事件中做验证规则处理。
1 | // 失去焦点校验 |
这里我们定义了 validator
对象,对象中有一个 onBlur()
函数, onBlur()
函数用来处理失去焦点的验证。在 input
中,加入了 {...this.validator}
,这样写是为了以后的扩展性(比如可以在 validator
中加入其他的处理函数),当然也等同于 onBlur={onBlur}
。
在表单校验的主逻辑中,只获取了校验结果和提示内容,至于如何验证都交给了 verify
函数去处理,这样我们主逻辑代码就很清晰了。这里我们将校验规则与校验规则的处理放在了单独的文件中,这样主逻辑代码看起来更加简洁,在主逻辑代码中,我们只管使用相应的校验规则,而不用关心校验规则是如何进行的。
(2)提交时校验
如果只对单个输入框失去焦点时进行校验,并不能保证提交时都是正确的数据,所以还需要在点击提交时,再次校验规则。
1 | // 提交时校验 |
六、JSX 是如何变成 DOM 的?
React 官网中对 JSX 的定义:
JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力。
那关键问题就是JSX 是如何在 JavaScript 中生效的,官方的解释:
JSX 会被编译为 React.createElement(), React.createElement() 将返回一个叫作“React Element”的 JS 对象。
实际上,JSX 仅仅只是 **React.createElement(component, props, ...children)**
函数的语法糖。所有的 jsx 最终都会被转换成React.createElement
的函数调用。这也就意味着,我们写的 JSX 其实写的就是 React.createElement
,虽然它看起来很像 HTML。这也印证了 JSX 充分具备 JavaScript 的能力。
React 内部处理 createElement 的源码如下:
1 | export function createElement(type, config, children) { |
入参:createElement 需要传递三个参数:
(1)参数一:type
,表示当前 ReactElement 的类型;
● 如果是标签元素,那么就使用字符串表示 “div”;
● 如果是组件元素,那么就直接使用组件的名称;
(2)参数二:config
,以对象形式传入,组件所有的属性都会以键值对的形式存储在 config
对象中;
(3)参数三:children
,存放在标签中的内容,以children
数组的方式进行存储;
1 | // jsx -> babel -> React.createElement() |
createElement 的处理流程:
简单来说,React.createElement
就是一个“转换器”,将用户输入的参数,进行一定的格式化,最终通过调用ReactElement
来实现元素的创建。
出参:createElement
执行到最后会 return 一个针对 ReactElement 的调用。下面是 ReactElement 的源码:
1 | const ReactElement = function (type, key, ref, self, source, owner, props) { |
根据上面的源码可以看出,ReactElement 实际上就做了一件事,将传入的参数进行组装,将它们组装进element
对象中,并将它返回给了React.createElement
,最终 React.createElement
又把它交回到了开发者手中。
整体的流程图如下:
通过 React.createElement
最终创建出来一个ReactElement
对象。
React 利用 ReactElement 对象组成了一个 JavaScript 的对象树,对象树就是虚拟 DOM(Virtual DOM)。下面是一个 ReactElement 对象的示例代码:
1 | render() { |
上面就是生成的对象树,也就是虚拟 DOM,那虚拟 DOM 是如何转化为真实的 DOM 结构的呢?它靠的就是render
函数。在每一个 React 项目的入口文件中,都少不了对 React.render
函数的调用。
ReactDOM.render 方法的入参规则:
1 | ReactDOM.render( |
ReactDOM.render 方法可以接收 3 个参数,其中第二个参数就是一个真实的 DOM 节点,这个真实的 DOM 节点充当“容器”的角色,React 元素最终会被渲染到这个“容器”里面去。上面所说的 App 组件,它对应的 render 调用是这样的:
1 | ReactDOM.render(<App />, document.getElementById("app")); |
需要注意的是,这个真实的 DOM 一定是确实存在的,比如在 App 组件对应的 index.html 中,已经提前预置 了 id 为 root 的根节点:
1 | <body> |
那为什么要操作虚拟 DOM,而不是操作真实的 DOM 呢?
原因如下:
● 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试;
● 操作真实 DOM 性能较低:传统的开发模式会进行频繁的 DOM 操作,而这一的做法性能非常的低;
DOM 操作性能非常低:
● 首先,document.createElement
本身创建出来的就是一个非常复杂的对象;
● 其次,DOM 操作会引起浏览器的回流和重绘
,所以在开发中应该避免频繁的 DOM 操作;
虚拟 DOM 可以帮助我们从命令式编程转到了声明式编程的模式。
下面是 React 官方的说法:Virtual DOM 是一种编程理念。
● 在这个理念中,UI 以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的 JavaScript 对象
● 我们可以通过ReactDOM.render
让 虚拟 DOM 和 真实 DOM 同步起来,这个过程中叫做协调(Reconciliation);
这种编程的方式赋予了 React 声明式的 API:
● 只需要告诉 React 希望让 UI 是什么状态;
● React 来确保 DOM 和这些状态是匹配的;
● 不需要直接进行 DOM 操作,只可以从手动更改 DOM、属性操作、事件处理中解放出来;
七、案例练习
下面就来做一个简单的案例,其要求如下:
● 在界面上以表格的形式,显示一些书籍的数据
● 在底部显示书籍的总价格
● 点击+或者点击-可以增加或减少书籍数量(如果为 1,那么不能继续-)
● 点击移除按钮,可以将书籍移除(当所有书籍移除完毕之后,显示:购物车为空~)
页面效果如下:
代码实现:
1 | <style> |
1 | class App extends React.Component { |
通常我们会将渲染相关的函数放在 render 函数的上方,将功能性的函数放在 render 函数的下方。
需要注意, React 中设计原则中有一条: state 中的数据的不可变性,所以我们不能直接操作数组中元素,需要对其进行拷贝在进行操作