React是什么
React 是一个采用声明式,高效而且灵活的用来构建用户界面的框架。
环境搭建
由于React依赖nodejs环境需要先下载NodeJs环境,https://nodejs.org/en/download/
安装完成后通过命令行查看安装是不是成功
node -v
国内调用npm较慢的情况下可以使用淘宝的镜像
npm config set registry https://registry.npm.taobao.org
npm install -g create-react-app
create-react-app my-app
cd my-app
npm start
React使用
组件与组件之间的通讯方式
1. 父与子组件的通讯方式:父组件引入子组件并设置参数将自身的参数传递给子组件中,子组件中可以通过props中取到。
2. 子组件与父组件通讯方式:子组件需要拿到父组件的方法对象然后调用父组件的方法即可,需要注意当父组件传递方法给子组件的时候需要将自身绑定到方法上,这样才能保证在子组件调用父组件的方法进行操作时能够将this转换为父组件对象,然后调用父组件的方法。
优化点
设置参数可以使用异步操作
设置State的时候会传递一个参数prevSate代表改变之前的一个数据
state修改的时候需要使用setState方法进行修改操作
import React, {Component, Fragment} from 'react'
import Item from './Item'
/**
* 列表
*/
class List extends Component {
constructor(props){
super(props);
this.handleContentChange = this.handleContentChange.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
this.state = {
content:"",
list : []
}
}
render() {
return (
<Fragment>
<div>
<input value = {this.state.content} onChange={this.handleContentChange}/>
<input type="button" onClick={this.handleButtonClick} value="提交"/>
</div>
<div>
<ul>
{
this.getItem()
}
</ul>
</div>
</Fragment>
)
}
/**
* 获取列表项
*/
getItem() {
return this.state.list.map((item, index) => {
{/** 注释下*/}
return (
<Item
key={index}
content={item}
index={index}
handleDeleteItem={this.handleDeleteEvent.bind(this)}
/>
)
});
}
/**
* 处理删除事件
*/
handleDeleteEvent(index) {
this.setState((prevState) => {
const list = [...prevState.list];
list.splice(index, 1);
return {list}
});
// const contentList = [...this.state.list];
// contentList.splice(index, 1);
// this.setState({
// list : contentList
// });
}
/**
* 处理内容变更事件
*/
handleContentChange(e) {
const value = e.target.value;
this.setState(() => ({
content : value
}));
// this.setState({
// content : e.target.value
// });
}
/**
* 处理button点击事件
*/
handleButtonClick() {
this.setState((prevState) => ({
content : "",
list : [...prevState.list, prevState.content]
}));
}
}
export default List;
import React, {Component, Fragment} from 'react'
import ReactDom from 'react-dom'
class Item extends Component {
constructor(props) {
super(props);
this.handleDeleteItem = this.handleDeleteItem.bind(this);
}
render() {
return (
<div onClick={this.handleDeleteItem}>
{this.props.content}
</div>
)
}
/**
* 处理删除item
*/
handleDeleteItem() {
this.props.handleDeleteItem(this.props.index);
}
}
export default Item
React虚拟Dom
React中提出了虚拟Dom的概念,框架的意义在于为你掩盖底层的 DOM 操作,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架可以比纯手动的优化 DOM 操作更快,因为框架的 DOM 操作层需要应对任何上层 API 可能产生的操作,它的实现必须是普适的。
我是觉得虚拟Dom在屏蔽人工手动变更Dom节点内容方面是确实有一个独创性的进步。看到很多文章中说React的虚拟Dom比原生的Dom快,虽然讲了一堆什么Diff算法、什么虚拟Dom数据结构,但是始终没有看到为什么React的虚拟Dom实现就一定比原生的快。我看下面讲解的还是更有依据一些。这块也可以参考这篇文章https://www.zhihu.com/question/31809713
浏览器的虚拟DOM与真实DOM的区别(注意:需不需要虚拟DOM,其实与框架的DOM操作机制有关):
虚拟DOM不会进行排版与重绘操作
虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分(注意!),最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
真实DOM频繁排版与重绘的效率是相当低的
虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部(同2)
使用虚拟DOM的损耗计算:
总损耗 = 虚拟DOM增删改 + (与Diff算法效率有关)真实DOM差异增删改 + (较少的节点)排版与重绘
直接使用真实DOM的损耗计算:总损耗 = 真实DOM完全增删改 + (可能较多的节点)排版与重绘
总之,一切为了减弱频繁的大面积重绘引发的性能问题,不同框架不一定需要虚拟DOM,关键看框架是否频繁会引发大面积的DOM操作
webkit引擎的处理流程,一图胜千言:
所有浏览器的引擎工作流程都差不多,如上图大致分5步:创建DOM tree –> 创建Style Rules -> 构建Render tree -> 布局Layout –> 绘制Painting
第一步,用HTML分析器,分析HTML元素,构建一颗DOM树。
第二步:用CSS分析器,分析CSS文件和元素上的inline样式,生成页面的样式表。
第三步:将上面的DOM树和样式表,关联起来,构建一颗Render树。这一过程又称为Attachment。每个DOM节点都有attach方法,接受样式信息,返回一个render对象(又名renderer)。这些render对象最终会被构建成一颗Render树。
第四步:有了Render树后,浏览器开始布局,会为每个Render树上的节点确定一个在显示屏上出现的精确坐标值。
第五步:Render数有了,节点显示的位置坐标也有了,最后就是调用每个节点的paint方法,让它们显示出来。
当你用传统的源生api或jQuery去操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。比如当你在一次操作时,需要更新10个DOM节点,理想状态是一次性构建完DOM树,再执行后续操作。但浏览器没这么智能,收到第一个更新DOM请求后,并不知道后续还有9次更新操作,因此会马上执行流程,最终执行10次流程。显然例如计算DOM节点的坐标值等都是白白浪费性能,可能这次计算完,紧接着的下一个DOM更新请求,这个节点的坐标值就变了,前面的一次计算是无用功。
即使计算机硬件一直在更新迭代,操作DOM的代价仍旧是昂贵的,频繁操作还是会出现页面卡顿,影响用户的体验。真实的DOM节点,哪怕一个最简单的div也包含着很多属性,可以打印出来直观感受一下:
虚拟DOM就是为了解决这个浏览器性能问题而被设计出来的。例如前面的例子,假如一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地的一个js对象中,最终将这个js对象一次性attach到DOM树上,通知浏览器去执行绘制工作,这样可以避免大量的无谓的计算量
webkit引擎渲染这块的文章摘自链接:https://www.jianshu.com/p/616999666920
虚拟Dom的结构
https://evilmartians.com/chronicles/optimizing-react-virtual-dom-explained
这篇文章中讲解了虚拟Dom的结构的一些概念,对于一般的Dom元素,可以对其进行抽象,
比如说下面这样一个div的标签,我们可以抽象为三个元素
1. 类型:div
2. 参数:className="test"
3. 内容:helloTest
一参数type
。标识带有HTML中这个标签的类型是什么。
二参数是所有元素属性(attributes
)。没有的话也可以是空的。
三参数是元素的子元素children
。标签的文本也算作一个child。
这个div被转换成“正经”的JavaScript代码,其实是一个带有一些参数的函数调用:
import React, { Component } from 'react'
class Test extends Component {
render() {
//return <div className="test">helloTest</div>
return React.createElement('div', {className:'test'}, 'helloTest')
}
}
export default Test;
允许添加多个子元素,你可以通过数组的形式进行添加子元素
例如上面的例子可以改为
React.createElement('div', {className:'text'}, ['helloTest',React.createElement('p'), 'HI'])
<div>
helloTest
<p/>
HI
</div>
React.createElement函数
function React.createElement<{
className: string;
}, any>(type: "object" | "input" | "div" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | ... 96 more ... | "webview", props?: React.ClassAttributes<...> & {
...;
}, ...children: React.ReactNode[]): React.DetailedReactHTMLElement<...> (+7 overloads)
Diff算法
等更新,先放一部分前辈们的总结看下
https://zhuanlan.zhihu.com/p/20346379?refer=purerender
https://zhuanlan.zhihu.com/p/33255775