1. React 简介
- React 是一个用于构建用户界面的 javascript 库。
- React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。React 将界面分成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,就成了我们的页面。
- React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
- React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
无论你现在正在使用什么技术栈,你都可以随时引入 React 来开发新特性,而不需要重写现有代码。React 还可以使用 Node 进行服务器渲染,或使用 React Native 开发原生移动应用。
2. React 高性能的体现
React高性能的原理:
在Web开发中我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。
虚拟DOM
React为此引入了虚拟DOM(Virtual DOM)的机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A-B,B-A,React会认为A变成B,然后又从B变成A,UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。
尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要关心在任意一个数据状态下,整个界面是如何Render的。
React Fiber算法
React Fiber算法是在react 16之后发布的一种react 核心算法,React Fiber是对核心算法的一次重新实现(官网说法)。之前用的是diff算法。
在之前React中,更新过程是同步的,这可能会导致性能问题。
当React决定要加载或者更新组件树时,会做很多事,比如调用各个组件的生命周期函数,计算和比对Virtual DOM,最后更新DOM树,这整个过程是同步进行的,也就是说只要一个加载或者更新过程开始,中途不会中断。因为JavaScript单线程的特点,如果组件树很大的时候,每个同步任务耗时太长,就会出现卡顿。
React Fiber的方法其实很简单——分片。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
3. React 特点
- 声明式设计 −React采用声明范式,可以轻松描述应用。
- 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
- 灵活 −React可以与已知的库或框架很好地配合。
- JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
- 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中,react开发的核心是将页面拆分成若干个组件,并且react一个组件中同时耦合了css、js、image,这种模式整个颠覆了过去的传统的方式。
- 单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
4. React 安装
方式一:在项目中直接引入react.js文件。
<div id="app"></div>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel">
ReactDOM.render(
<h1>Hello, world!!</h1>,
document.getElementById('app')
);
</script>
上面的例子中我们引入了三个库: react.min.js 、react-dom.min.js 和 babel.min.js:
- react.min.js - React 的核心库
- react-dom.min.js - 提供与 DOM 相关的功能
- babel.min.js - Babel 可以将 ES6 代码转为 ES5 代码,这样我们就能在目前不支持 ES6 浏览器上执行 React 代码。Babel 内嵌了对 JSX 的支持。通过将 Babel 和 babel-sublime 包(package)一同使用可以让源码的语法渲染上升到一个全新的水平。
方式二:通过 npm 安装
使用 create-react-app 脚手架,可以快速创建一个 react 单页面项目,自动创建的项目是基于webpack + ES6。在终端中执行下面的命令,即可快速创建一个react 项目。
npm install -g create-react-app my-app
国内使用 npm 速度很慢,在终端执行下面的命令,可以将npm镜像切换为淘宝镜像,再进行 npm install 安装
npm config set registry http://registry.npm.taobao.org/
不想全局安装的话,也可以使用npx命令,在终端中执行以下命令,在当前目录下创建react项目,版本要求: Node >= 8.10 和 npm >= 5.6。
npx create-react-app ./
这个安装的过程中,实际上会安装三个东西:
- react:react的顶级库
- react-dom:因为react有很多的运行环境,比如app端的react-native, 我们要在web上运行就使用react-dom
- react-scripts:包含运行和打包react应用程序的所有脚本及配置
生成项目的目录结构如下:
├── README.md //使用方法的文档
├── node_modules //所有的依赖安装的目录
├── package-lock.json //锁定安装时的包的版本号,保证团队的依赖能保证一致。
├── package.json //配置文件
├── public //静态公共目录
└── src //开发用的源代码目录
安装成功后,执行npm start
即可启动项目。
在浏览器中打开 http://localhost:3000/ ,看到下面这个页面,则说明项目创建成功了。
5. 元素渲染
元素是构成 React 应用的最小单位,它用于描述屏幕上输出的内容。
const element = <h1>Hello, world!</h1>;
使用 ReactDOM.render(ele,dom)
方法,将元素渲染到页面中。第一个参数为元素,第二个参数为要渲染的DOM节点。
//基础库,支持jsx
import React from 'react';
//帮助我们把组件渲染到页面上
import ReactDOM from 'react-dom';
ReactDOM.render(
<h1>hello world</h1>,
document.getElementById('root')
);
React 元素都是不可变的。当元素被创建之后,你是无法改变其内容或属性的。
目前更新界面的唯一办法是创建一个新的元素,重新调用ReactDOM.render() 方法。
6. 组件
React 开发的核心是将界面分成了各个独立的小块,每一个块就是组件,一个组件中同时耦合了css、js、image,这些组件之间可以组合、嵌套,就成了我们的页面。
6.1 元素组件
import React from 'react';
import ReactDOM from 'react-dom';
const app = <h1>hello world</h1>
ReactDOM.render(
app,
document.getElementById('root')
);
6.2 函数式组件
- 函数式组件也叫无状态组件,不能维护自己的状态。
- 可以通过标签中的属性传参数。
- 函数中返回的内容,必须要有唯一的根节点。
- 函数式组件有两种写法,使用标签模板渲染(组件名称首字母必须大写)和使用函数渲染。
使用标签模板渲染:
import React from 'react';
import ReactDOM from 'react-dom';
//组件名称必须大写
const App =(props)=> <h1>{props.title},欢迎进入{props.name}的世界</h1>
ReactDOM.render(
<App name="react" title="你好"/>,
document.getElementById('root')
);
使用函数渲染:
import React from 'react';
import ReactDOM from 'react-dom';
const app =(props)=> <h1>{props.title},欢迎进入{props.name}的世界</h1>
ReactDOM.render(
app({
name:'react',
title:'你好'
}),
document.getElementById('root')
);
6.3 ES6 class组件
- 类组件名首字母必须要大写。
- 类组件需要继承React.Component,不继承的话,就是一个普通的js类。
- 必须要有render()这个方法,必须有return。
- 返回的内容,必须要有唯一的根节点。
- 向组件传递参数,可以使用 this.props 对象来获取参数内容。
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
render() {
return (
<div>
<p>{this.props.user.name}</p>
<h1>欢迎来到{this.props.name}的世界</h1>
</div>
)
}
}
ReactDOM.render(
<App name="react" user={name:"zz",age:18}/>,
document.getElementById('root')
);
在render()函数中,返回的内容要有唯一的根节点,如果我们并不想返回最外层的div,可以返回一个空标签<React.Fragment>,在新版本的react中,可以直接用<></>来代替。
//上述中return的内容可以简写为
return (
<React.Fragment>
<p>{this.props.user.name}</p>
<h1>欢迎来到{this.props.name}的世界</h1>
</React.Fragment>
)
//在新版本的react中,上述中return的内容可以简写为
return (
<>
<p>{this.props.user.name}</p>
<h1>欢迎来到{this.props.name}的世界</h1>
</>
)
6.4 复合组件
我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离。
import React from 'react';
import ReactDOM from 'react-dom';
function Name(props) {
return <h1>名字:{props.name}</h1>;
}
function Age(props) {
return <h1>年龄:{props.age}</h1>;
}
function Sex(props) {
return <h1>性别:{props.sex}</h1>;
}
function App() {
return (
<div>
<Name name="张三" />
<Age age="18" />
<Sex sex="男" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('example')
);
6.5 比较老的一种方式
在react16之前的版本(包含16)中,可以使用下面这种方式创建组件,在新版本中用这种方式创建组件的话,需要安装create-react-class插件。
终端中执行以下命令,安装create-react-class插件
yarn add create-react-class -S
import React from 'react';
import ReactDOM from 'react-dom';
import createClass from 'create-react-class';
const App = createClass({
render(){
return <h1>gp18</h1>
}
})
ReactDOM.render(
<App />,
document.getElementById('root')
);
参考资料:https://www.runoob.com/react
7. 高阶组件 HOC
Higher-Order Components,简称 HOC,它就是一个函数,接受一个组件为参数,返回一个新的组件,把传进来的组件做了一个增强。我们可以对传进来的组件进行扩展,比如添加属性和元素。
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
举个例子,如果我们想在多个文章组件的最上方添加日期信息,在下方添加版权信息,可以这么来写:
声明一个高阶组件,例如文件名称为:ArticleHoc.jsx ,内容如下:
/* ArticleHoc.jsx */
import React from 'react';
let WraperName;
//高阶组件接收一个组件为参数
const ArticleHoc = (WraperComponent) => {
//获取参数组件的displayName
WraperName=WraperComponent.displayName || WraperComponent.name || 'Component';
//并返回一个新的组件
return class NewComponent extends React.Component {
constructor(props){
super();
this.state={
date:new Date().toLocaleString()
}
//获取参数组件的prop
//打印props结果为:{title: "React HOC"}
console.log(props);
}
render() {
return (
<>
//给参数添加新的属性,并将原来的属性传递过去
<WraperComponent date={this.state.date} {...this.props}></WraperComponent>
//最终返回的组件中添加新的元素
<div>版权所有:2020-04-02@reactJsHoc</div>
</>
)
}
}
}
//设置高阶函数的displayName,方便调试
ArticleHoc.displayName=`ArticleHoc(${WraperComponentName})`;
export default ArticleHoc;
在组件 Article.jsx 中使用:
import React, { Component } from 'react'
//导入高阶组件
import ArticleHoc from './ArticleHoc'
class Article extends Component {
constructor(props){
super();
//接收所有的 prop
//打印结果为:{date:"2020/4/24 上午7:34:00",title:"React HOC"}
console.log(props);
}
render() {
return (
<div>
<p>标题:{this.props.title}</p>
<p>日期:{this.props.date}</p>
<p>内容:React.js 是一个构件用户界面的库</p>
</div>
)
}
}
//导出的是高阶组件
export default ArticleHoc(Article)
渲染到页面中:
import React from 'react';
import ReactDOM from 'react-dom';
import Article from './Article'
ReactDOM.render(
<Article title="React HOC"/>,
document.getElementById('root')
);
最终在页面上的效果为:
HOC 不会修改传入的组件,也不会使用继承来复制其行为。相反,HOC 通过将组件包装在容器组件中来组成新组件。HOC 是纯函数,没有副作用。
被包装组件接收来自容器组件的所有 prop,同时也接收一个新的用于 render 的 data prop。HOC 不需要关心数据的使用方式或原因,而被包装组件也不需要关心数据是怎么来的。
HOC 创建的容器组件会与任何其他组件一样,会显示在 React Developer Tools 中。为了方便调试,设置displayName,最常见的方式是用 HOC 包住被包装组件的显示名称,以表明它是 HOC 的产物。
使用高阶组件时要注意:
- 不要试图在 HOC 中修改组件原型(或以其他方式改变它),要使用组合的方式,通过将组件包装在容器组件中实现功能。
- HOC 应该透传与自身无关的 props。在组件原型中添加
{...props}
将组件中原有的 prop 传递过去。 - HOC 通常可以接收多个参数。
- 不要在 render 方法中使用 HOC。
- 高阶组件中的 Refs 不会被传递,如果将 ref 添加到 HOC 的返回组件中,则 ref 引用指向容器组件,而不是被包装组件。
高阶组件总结:
- 高阶组件是一个函数;
- 函数的参数是一个组件;
- 返回了一个新组件;
- 新的组件是对传入的组件功能做了一个增强