React入门

92 阅读6分钟

参考资料

React简介

  • 由faceBook在2013年5月开源推出
  • 全新的函数式编程风格
  • React 16版本之后称之为 React Fiber
  • chrome插件调试React developer tools 

这么称是因为在React 16版本之后,在底层在事件循环中加入了优先级这样的概念,可以在循环的碎片事件可以执行高优先级的交互。

React的开发环境搭建

官方文档

  1. 引入.js文件来使用React
  2. 通过脚手架工具(如官方提供Create-react-app)来编码构建大型项目和目录。脚手架里的代码并不能直接运行,需脚手架编译之后才能识别运行,使用 Grunt、webpack等帮助编写脚手架。
npx create-react-app my-app
cd my-app
npm start

React 中的响应式设计思想和事件绑定

  • React在创建实例的时候, constructor(){} 是最先执行的

  • this.state 负责存储数据

  • 如果修改state中的内容,不能直接改,需要通过setState进行修改

  • JSX中js表达式用{}包裹

  • 事件绑定需要通过bind.(this)对函数的作用域进行变更

setState(异步)

异步初衷:假如连续三次更新state的数据,React会把三次setState合并成一次setState,只做一次虚拟DOM的比对,更新DOM。这样可以省去额外两次DOM比对带来的性能消耗。

this.setState(() => {
  //更新state数据
},() => {
  //更新state数据完成后执行
})

propTypes  和 defaultProps (参数类型的校验和默认值)

官方文档

// React脚手架内置 这里直接import
import PropTypes from 'prop-types'

TodoItem.propTypes = { // 参数类型校验
  test: PropTypes.string.isRequired, // 必传
  item: PropTypes.string,
  handleItemSDelete: PropTypes.func,
  index: PropTypes.number
}

TodoItem.defaultProps = { // 若父组件没有传参数,可以指定默认值
  test: 'defaultPropsValue'
}

Virtual DOM 与 Diff算法 

传统页面渲染和更新(耗性能)
  1. 先定义state数据
  2. 模板
  3. 数据 + 模板结合,生成真实的DOM来显示
  4. state发生改变
  5. 数据 + 模板结合,生成真实的DOM来显示,替换原始的DOM

Virtual DOM渲染和更新(节省性能)

JSX ==>通过React.createElement方法 ==> Virtual DOM( JS对象 ) ==> 真实的DOM 

优点:

  1. 提升性能
  2. 它使得跨段应用得以实现,如React Native
  1. state数据  
this.state = { value: 'hello world' }
  1. JSX模版
render() {
  return( <div id='abc'><span>{this.state.value}</span></div> )
}
==等价于不使用JSX模版的写法(不推荐)
render() {
return React.createElement("div", {
   id: "abc"
 }, React.createElement("span", null, this.state.value));
}
  1. state数据 + JSX模版结合生成 Virtual DOM( Virtual DOM就是一个JS对象,描述真实的DOM)
Virtual DOM  :['div', {id: 'abc'}, ['span', {}, 'hello world']] 
//虚拟DOM解释: 标签div,属性id='abc',子元素span,没有属性,值为hello world
  1. Virtual DOM的结构生成真实的DOM显示出来
<div id='abc'><span>hello world</span></div>
  1. state数据发生变化
this.setState(() =>({ value: 'Bye bye' }))
  1. state数据 + JSX模版 生成新的 Virtual DOM
Virtual DOM  :['div', {id: 'abc'}, ['span', {}, 'Bye bye']]
  1. 比较更新前的 Virtual DOM与更新后的 Virtual DOM( Diff算法
// 区别 hello world ==> Bye bye
Virtual DOM['div', {id: 'abc'}, ['span', {}, 'hello world']]
Virtual DOM['div', {id: 'abc'}, ['span', {}, 'Bye bye']]

// 然后直接操作DOM,修改span中的内容

Diff算法(同层比对)

两个虚拟DOM作比对,找到差异之后去更新真实DOM。首先比较最顶层DOM,假如一致,再去比较第二层,假如不一致(即使下面所有的DOM都一致),则停止比较,把原始虚拟DOM对应的节点DOM全部删除,重新生成新的DOM并替换原始DOM。

**** ****

key值

官方文档

key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中每一个元素赋予一个确定的标识。

假设有个数组list = ['a', 'b', 'c', 'd', 'e'],页面首次渲染的时候,映射成5个虚拟DOM节点,生成虚拟DOM树。

然后新增数据list = ['a', 'b', 'c', 'd', 'e', 'z'] ,state数据发生变化,生成新的虚拟DOM树。两个虚拟DOM树作比较。但如果两个虚拟DOM的节点没有key值,节点之间的关系很难确定,如下左图。

此时如果给每个节点设置key值。很容易就能发现区别,增加了z节点,如下右图。

所以也可以解释为什么循环列表的时候,不建议使用数组的index值作为key值。

list = ['a', 'b', 'c', 'd', 'e']
 //index 0    1    2    3    4
list.splice(0,1)
list = ['b', 'c', 'd', 'e']
//index  0    1    2    3
// 事实上,list只是删除了字符串'a'
// 但是如果把index作为key值,则虚拟DOM对比的时候会发现虚拟DOM节点全部改变了。

ref的使用(尽量少用)

官方文档

// JSX
<input onChange={this.handleInputChange.bind(this)} ref={(input) => {this.input = input}} />
  // 或者 ref=‘input’
handleInputChange(e) {
	console.log(e.target.value) // JSX中的input标签
    console.log(this.input) // JSX中的input标签
        // 或者this.refs.input
    console.log(e.targe.value === this.input.value) // true
}

生命周期函数

组件一旦被创建constructor会被自动调用也可以理解为生命周期函数,但是属于ES6不归属于React

生命周期函数:是指在某一时刻组件会自动调用执行的函数。React生命周期函数如下:


Initialzation:初始化

在constructor里初始化数据 state 和 props


Mounting:挂载

只在组件第一次被挂载页面时执行

  • componentWillMount 在组件即将被(还没有)挂载到页面的时刻自动执行
  • render 挂载渲染页面
  • componentDidMount 在组件被挂载到页面之后自动执行

Updation:组件更新

state或者props发生变化

  • componentWillReceiveProps (只在props发生改变时执行)

一个组件要从父组件接受参数props,

然后只要父组件的render函数被重新执行了,子组件的这个生命周期函数就会被执行

如果这个组件第一次存在于父组件中,不会执行。如果这个组件之前已经存在于父组件中,才会执行

  • shouldComponentUpdate  组件被更新之前自动执行,要求返回一个布尔值,组件要更新吗?

  • componentWillUpdate  组件被更新之前自动执行,而且是在shouldComponentUpdate 之后执行。如果shouldComponentUpdate返回true,它才执行,返回false,它不会执行。

  • render

  • componentDidUpdate 组件更新完成之后执行


Unmounting 把组件从页面上卸载

  • componentWillUnmount 在组件即将被从页面中卸载时执行

所有的生命周期函数都可以不存在,但是render必须存在,React.Component默认内置了其他所有的生命周期函数,唯独没有内置render生命周期函数,所以组件必须自己来定义render

子组件render执行的场景:

  1. state 或者 props发生改变的时候

  2. 当前组件的父组件render重新执行的时候(性能不好,可以使用生命周期函数优化,解决办法如下:)

生命周期函数使用场景

  1. 解决父组件render重新执行时,使得子组件render不必要的执行
// 子组件
shouldComponentUpdate(nextProps, nextState) { 
	if (nextProps.content !== this.props.content) {
  	return true
  } else { 
  	return false
` }
}
  1. ajax请求
// componentWillMount也是没有问题的,
// 但是服务器端或者react native里更高级的东西时,可能会和其他技术产生冲突
// constructor也可以,但是建议还是放在componentDidMount
componentDidMount() {
	// api接口请求
}

性能优化

官方文档

// 1 绑定作用域放在constructor
constructor() {
	this.handleMethods = this.handleMethods.bind(this)
}

// 2 this.setState方法
// 3 虚拟DOM、同层比对、key值的使用
// 4 shouldComponentUpdate的使用避免不必要的render渲染