- react 基本概念解析
- react 的组件声明周期
- react 高阶组件,context, redux 等高级进阶
特别提醒:
新版本更新
React.createClass({ ..... }) 为es5 的写法,新版已经被废弃
class { ..... } 为 es6 的写法,推荐使用create-react-app 工程为自动创建新 react 工程的工具项目,该工具包含了打包、 测试、sourceMap、等功能。
react 使用全家桶(
react 项目中不可或缺的工具
)
React 在开发/生产 阶段需要一大堆的工具库辅助开发
编译:babel
状态管理工具:redux
单页应用:react-routedangerouslySetInnerHTML 用于设置不受保护的HTML
<div dangerouslySetInnerHTML = {{ __html: this.state.不受保护的代码片段 }}></div>-
好的 React 命名习惯
- 自定义的组件使用 首字母大写的驼峰命名 (eg:
InputComponent
..... ) - 自定义的属性使用 小驼峰命名 ( eg: this.state.
getDate
..... ) - 事件尽可能以 handle... 开头命名 (eg: handleChange....)
- 组件私有的自定方法以下划线 _..... 开头命名 (eg: _getLocalDate...... )
- 自定义的组件使用 首字母大写的驼峰命名 (eg:
react 概念大全
事件系统 + 表单应用 + 样式处理 + 组件通讯 + 组件抽象 + 组件性能优化 + 动画 + 自动化测试
一:react 基本概念解析
- react 描述 DOM 的 方式 (JSX)jsx 其实就是 JavaScript 对象
-
在 javascript 中使用类似于 HTML 结构和信息的语法为 JSX 语法。
很容易转换编译成为 于 HTML 信息的 JavaScript 表示形式( `HTML 的 对象形式` )。 可以根据平台 编译成为 第三方页面 UI 结构
ReactDOM 解析
ReactDOM 是 react 提供的 WEB 页面构建对, 在 移动端 该对象是 ReactApp
ReactDom 有三个方法:
findDomNode( this ) // 用于获取页面中的DOM 元素
UnmountComponentAtNode()
render() // 用于页面渲染-
ref 和 findDomNode 获取页面元素辨析
ref 添加到 Component 上获取的是 Component 实例,
ref 添加在 原生的 HTML 上获取的是 DOM 节点findDomNode 当参数是 DOM 时 就返回一个 DOM( 貌似没什么用 ) findDomNode 当参数是 Component 时返回 Component 中的 render 中的 DOM <div> <div ref={div => { this._div = div }}>app </div> <Child ref={child => this._child = child}/> <button onClick={()=> { console.log(ReactDOM.findDOMNode(this._div) === this._div); console.log(ReactDOM.findDOMNode(this._child)); }}>log refs </button> </div>
特点
1. react 使用 jsx 描述 DOM / UI 结构
2. jsx 预防在编译阶段 会转换为 DOM 的 JavaScript 表现形式
3. ReactDom 对象只有一个作用 ---> 负责将 JavaScript 对象 渲染成为 真正的页面 DOM
4. 在渲染过程中 react-dom( web ) ,react-canvas( canvas ) ,react-app (react-native) 可分别将该 JS 对象渲染到不同的平台上
- render 函数中都可以写什么
render 函数能且只能 返回一个 jsx 对象, 就是必须 返回一个值,并且这个值 是一个 类似于 DOM 的 jsx 语法
-
jsx 结构中可以使用 { } ( 大括号 ) 插入 表达式
{ } 大括号中 可以放置任何表达式内容( 包括 函数,jsx 结构,变量,对象,表达计算式 )
... render () { const isGoodWord = true return ( <div> React 小书 { // jsx 中的注释 只能写在 大括号中 // 下面表示一个三目表达式 // 这里不能写 JavaScript 的 多行表达式 isGoodWord ? <strong> is good</strong> : <span> is not good</span> } { // 下面表示一个自执行函数,函数中可以做任何你想做的事,也可以有多行代码 (function() { .... doSomething }) } </div> ) } ...
jsx 结构中的 null 表示什么都不显示,相当于忽略了该表达式的插入,可以使用这个
特性来 隐藏元素
-
jsx 结构整体当做变量用于 参数传递或者使用
jsx 语法其实就是一个个 js 的对象,所以可以使用任何变量能使用的方式传递或者使用它们
... render () { const isGoodWord = true const goodWord = <strong> is good</strong> // 这是一个 jsx 变量 const badWord = <span> is not good</span> // jsx 变量还可以当做 函数参数传递 return ( <div> React 小书 {isGoodWord ? goodWord : badWord} </div> ) } ...
-
在 render 的 {} 中 可以使用 map 等
返回 DOM LIST
的函数方法// 必须返回一个 表示 DOM LIST const arr = [1, 2, 3, 4, 5]; render () { return ( <select> this.arr.map((item) => <option>item</option> ) </select> ) } ...
jsx 中的 三元表达式 其实就是一句完整的表达式,所以可以被 识别
-
jsx 语法中 注释的写法
注释只能写在 JavaScript 表达体 中(
即只能 写在 { } 大括号中
),写在 大括号外面的注释不被识别
要是真的想写注释,可以给某个或者某条语句外面添加一个 大括号,然后在进行注释 组件嵌套
组件和组件之间可以 嵌套使用。但是所有的自定义组件都必须是以 大写字母开头的组件。
-
事件系统
每个 jsx 元素上面都可以自己定义 on* 类型的事件监听函数,只能用在普通的 html 元素标签上,不能使用在自定义的元素标签上
每个事件监听函数 都有一个默认的 event 对象。
一个经过 react 包装的 event 对象
react 的事件代理机制
react 将事件处理函数统一绑定在最外层上面
react 对于原生的 DOM 节点可以书写原生的事件
** 特别注意 **:因为 react 的事件代理机制,注意 stopPropagation() 阻止事件冒泡的时机
-
注意 每个 事件监听函数 若内部有使用到 this 的情况,需要在声明函数的地方,动态的绑定 this
// bind this 的时候 可以 传递默认的 参数 render () { return ( <h1 onClick={this.handleClickOnTitle.bind(this, 'Hello')}>React 小书</h1> ) }
react 的 this 自动绑定区域
A. class 内部自动绑定 this (class 的 { } 大括号这一层级 自动绑定 this
)
B. 构造函数 constructor 中没有自动绑定this
C. 箭头函数自动绑定this
D. 使用 bind 显示绑定 this
-
state 的 使用
- 每次调用 setState 都会触发 react 的重新渲染,并且重新调用 render 方法 **
- setState 函数执行时可以接受一个参数(prevState)
- 多个 setState 执行需要合并时 需要 prevState 参数的帮忙。并且
react 会将 多个 setState 操作合并为一个最终的状态进行一次性渲染
- react 自己的 diff 算法保证只会渲染更改的地方
- props 的使用
props 带来更好的复用性和 可配置性
defaultProps设置组件默认的 props, 和 this.props 获取配置参数,设定 组件的默认 props 数据
组件内部一旦传递进 props
不可以在组件内部更改 props
。
但是 组件内部 可以获取 props. 将其赋予 state 或者 其他变量进行数据的增删改。
组件的生命周期
- 前端的状态数据管理, - 状态提升
将组件之间共享的数据抽象并提取出来放到最近的 公共父节点上,然后通过 props 将数据传递给子组件, 这样就是一个状态数据的提升。
那么问题来了? 怎样管理 多个层次的组件 所依赖的状态呢 ?
( ** 请大家关注 context 和 redux 的相关内容 ** )
- 组件的基础生命周期
react 将组件渲染 并塞到 DOM 元素中然后绘画在页面上的过程称为组件的挂载
所以 由挂载的过程你可以很好的理解下面的组件处理过程
constructor() // 初始化状态数据
componentWillMount() // 组件的挂载过程
render()
// 构造 DOM 元素 插入页面
componentDidMount() // 组件的挂载过程
。。。。。
// 即将从页面中删除元素
componentWillUnmount()
// 从页面中删除元素
以上 的几个生命周期 都是 组件的 第一阶段的状态管理函数
,
- 一般的 getDefaultProps 和 getInitialState 都会放在 constructor 中完成
- 组件的隐藏 会触发 页面的 componentWillUnmount() 函数,所以,在页面被隐藏时要注意剩余数据 的处理。
- componentWillMount() 函数多用于组件的启动,eg: ajax, localStore数据的拉取,定时器的启动,等工作。
- componentDidMount() 函数用于依赖 DOM 的事件动作或者其他。eg: 动画的启动需要依赖 DOM. componentWillMount 的时候 DOM 还没有挂载完成,所以动画的启动需要放在 DidMount 函数中完成
-
组件更新的生命周期
shouldComponentUpdate(nextProps, nextState)
返回一个 boolean 数据
。 用于控制组件是否 重新渲染。
componentWillReceiveProps(nextProps). 多用于处理父组件传递过来的 props
componentWillUpdate(); 组件开始更新之前调用。
componentDidUpdate(); 组件更新并且重新渲染到 DOM 之后调用。 ref 的作用
ref 属性用于获取已经挂载的 DOM 节点( 类似于为元素添加 id 属性,用于表示元素
)
refs 用于引用已经 标识的 DOM 节点,获取其 对象的 属性 和 方法
** 注意: ** 多余 的 DOM 操作 会产生代码的噪音,不推荐大量使用 ref 操作 DOM
ref 版本改动:标识 DOM 节点的 ref由原来的字符串改为一个回调函数
旧版本 <input ref="myinput" />
新版本 <input ref={(input)=>{this.myinput=input;}}/>可以额外有一个参数
参数有妙用
- props.children 和 自定义标签元素中 添加 额外的 HTML DOM 元素
普通的标签元素中内嵌的 DOM 元素可以直接添加。但是自定义标签组件怎讲将内嵌的 DOM 内容传递到下一层呢?
我们使用 props.children 可以获取到上层的 自定义标签中的内嵌的 DOM 元素
-
jsx 中任何的 html 格式的 代码都会被转义掉,为了防止 xss 攻击
为了显示 带有 html 标签的元素我们需要用到元素的 dangerouslySetInnerHTML (
这个只是元素的一个属性
)..... render() { renturn ( <div className="edit-html" // 按照下面的方法设置元素的 html 的内容 dangerouslySetInnerHtml={ { __html: this.state.content } } > ) } .....
-
propsTypes 和 组件的参数验证
JavaScript 是弱类型的语言,但是为了 ** 防止和校验上一层传递过来数据的正确性,我们使用 PropTypes 来做数据类型的校验相当于为数据添加数据类型,强类型的数据
**import React, { Component, PropTypes } from 'react' .... class ChildComponent extends Component { // 下面这些是固定的 书写格式 static propsTypes = { parentAttr: PropTypes. object, parentArr: PropTypes.array } ......... } ......
-
高阶组件就是一个函数,你传递给他一个组件对象,他返回给你一个经过包装的组件对象
** 相当于在原始组件之上 添加了一层经过封装的父组件 **功能性的封装
** 作用:**
组件状态提升
将子组件需要操作的数据交给父组件来管理,然后通过 props 向下传递数据。此为状态提升
当某个数据/状态 被多个组件依赖/使用 时,应该通过状态提升来管理-
高阶组件本质就是为了
组件之间的复用
,将重复的代码 操作放在高阶组件中,下面的基本组件就可以复用了比如数据的 ajax 的读取,事件的统一操作等
高阶组件中 通过 props 传递数据
-
高阶组件可以很好的实现 Smart 组件( 依赖数据操作的组件/ 高阶组件 ) 和 Dumb 组件( 只需要上层数据props的纯组件 ) 的分离
Smart 组件和 Dumb 组件( 或者说高阶组件 )的出现,很好的实现了 react 的 mvc 结构,达到了高层次的 组件的复用。
高阶组件知识点
1. 高阶组件 本质就是一种 组件的 mixin 模式
1. es7 的装饰着模式可以用来扩展高阶组件
2. 一个 Smart 组件可以被多个高阶组件装饰,并且按照顺序执行
-
react 的 context 作为全局的对象,有自己独特的写法
** context 必须和 propType 强制数据类型 共同使用 **
强制数据类型
import React, { Component, PropTypes } from 'react'
......
static propsTypes = {
themeColor: PropTypes. string
}-
定义context 时的写法
import React, { Component, PropTypes } from 'react' .... class ParentComponent extends Component { // 下面这些是固定的 书写格式 static propsTypes = { themeColor: PropTypes. string } ......... // 这里设置了 本组件包括其 所有子组件的 context getChildContext () { return { themeColor: this.state.themeColor } } } ......
context 在定义时,必须有两个步骤
声明属性的数据类型
设置获取全局 Context 的方法 getChildContext-
使用 context
import React, { Component, PropTypes } from 'react' .... class ChildComponent extends Component { static propsTypes = { themeColor: PropTypes. string } render () { return ( <div style={{ color: this.context.themeColor }}>context 的获取使用地方</div> ) } }
context 在使用时必须有两个步骤
声明要使用的 context 数据类型
使用 this.context.属性 获取属性的值
context 使用步骤繁杂是有原因的
1. context 作为全局拥有的对象,必须保证其数据的正确性。
2. react 官方不推荐使用 context, 进而希望大家能够使用更为友好的Redux, Mobx等
React 组件进阶
-
react 理念分析
React 的组件本质是创建了一个对象,由框架本身添加了 10 个声明周期函数(
其实就是普通的函数,只是有执行顺序、触发时机、优化处理等特点
)
具体参见 React 的最初写法 React.createClass({ .... }), 中间是一个对象声明周期函数,就是对象中的普通函数
传值 + 操作处理 + 返回值-
react 特点:
1. 简洁: 足够的简洁,其本身只有自己的 api
2. 非声明式的单项数据绑定
3. ** 面向 HTML5, 专注视图层操作 ** 将 JavaScript 和 HTML5 整合在一起。
4. 高性能的密集 DOM 操作
5. 支持后端渲染。
6. 跨平台,一种书写,能够编译出多种结果 ** ReactNative **react 函数式编程 ( 小函数编程 )
1. 尽可能抽象出公共使用的小函数。
2. 在一个庞杂的 逻辑体 中能够提取出 功能小函数 -
组件化
狭义的组件化:ui组件 ---> 具体的组件( Tabs 组件 / Dropdown 组件 )
广义的组件化: 业务数据 + UI 组件的组合使用组件化的发展进程
DOM操作为主
( 根据逻辑改变 DOM 结构 )
--> MVC( 数据和界面解耦 )
--> 小组件( 数据 + 界面 + 样式的结合体 eg: angular 指令 )
----> HTML5 的引入( javascript + HTML + css 合为一体)
HTML Templetes --> custom Element --> shadow DOM --> HTML Imports
-
** 非常重要:**
** 组件封装的进程其实就是面向对象的思想逐步成熟** (通过实例化的方法制造对象
)一个组件的对象实例: ( 基本的封装性 + 简单的声明周期 + 明确的数据流动 )
-
表单组件
表单组件分为受控组件和非受控组件
受控组件:依靠 state 和内部事件 改变状态的组件,有很强的耦合性,复用性不高非受控组件,通过 defaultProps, props 传值的组件,复用性好 非受控组件可以通过 事件和 ref 向上传递数据
组件通讯 ---> 事件订阅
EventEmitter-
组件性能优化
引入纯函数( pureRender 函数或者 Smart组件 )
使用 数据对比决定 纯函数组件是否渲染
shouldComponentUpdate()
-
引入不可变对象 或者 es6 对象扩展 进行数据对比
Immutable 不可变对象,对 Immutable 对象的 增 删 改 都会生成一个新对象,类似于对象的深拷贝,但是比对象的深拷贝效率要高 es6 对象扩展 和 对象/数组的解构赋值,也能够得到一个新的对象
-
Immutable 解析
** Immutable 可以提供一种全新的数据对比和生成形式,类似于 es6 的对象扩展 和 解构赋值 **
Immutable 三种数据类型 Map: 对应于 `Object`, 键值对集合 List : 对应于 `Array` 有序的重复列表 ArrayList: 无序不可重复的列表 // 特性应用实例 import { Map, List, Arraylist } from 'immutable' .... this.setSatate({ data: Map({ times: 0 }) // 得到一个新的 Immutable 对象 })
Immutable 方法应用实例 ( Immutable.is 可以用来进行比较,比 === 全等的效率高 )
import { is } from 'immutable' ..... shouldComponentUpdate(nextProps, nextState) { if( is( nextProps.attr, nextState.attr ) ) return false; ...... }
.