React 是 Facebook 在 2013 年开源 JavaScript 库。它把界面抽象成一个个组件,经过组合这些组件,开发者能够获得功能丰富的页面。同时引入了 JSX 语法,使得复用组件变得容易,且结构清晰。而且有了组件这层的抽象,代码和真实渲染目标分离,除了能够在浏览器端渲染到 DOM 开发网页外,还能用于原生移动应用的开发。html
React 并非完整的 MVC/MVVM 框架,它专一于 View(视图)层解决方案。与模板引擎不一样,React 又是一个包括 View 和 Controller 的库。前端
React 把真实 DOM 树转换成 JavaScript 对象树,也就是 Virtual DOM。每次数据更新,对比先后的 Virtual DOM,对发生变化的部分作批量更新,提高性能。而且 Virtual DOM 能够方便地与其余平台集成,好比 react-native 就是基于 Virtual DOM 渲染原生控件的。react
命令式编程是给计算机下命名,而函数式编程,对应的是声明式编程。声明式编程的本质是 lambda 演算,好比咱们要操做一个数组里的每一个元素,返回一个新数组。咱们的作法是构建一个 f 函数(规则)做用在数组上,而后返回新数组。这样,计算能够被重复利用。编程
JSX 是 react 为了方便 View 层组件化,承载构建 HTML 结构化页面职责的而创立的语言(语法)。react-native
在 react 中建立的虚拟元素能够分为两类,DOM 元素(DOM element)与组件元素(component element)。分别对应着原生 DOM 元素和自定义元素。数组
当使用 JavaScript 来描述 Web 页面的 HTML 元素时,能够表示为纯粹的 JSON 对象。例如,描述一个按钮浏览器
<button class="btn btn-blue">
<em>Confirm</em>
</button>
复制代码
->性能优化
{
type: 'button',
props: {
className: 'btn btn-blue',
children: [{
type: 'em',
props: {
children: 'Confirm'
}
}]
}
}
复制代码
在 react 中,处处可见的元素并非真实的实例,它们只是页面的描述对象。bash
React 还能够自定义组件元素。 类好比下架构
const Button = ({ color, text }) => {
return {
type: "button",
props: {
className: `btn btn-${color}`,
children: [
{
type: "em",
props: {
children: text
}
}
]
}
};
};
复制代码
Button 其实也能够做为元素而存在,方法名对应了元素类型,参数对应了元素属性。 这也是 React 的核心思想之一,咱们可让 DOM 元素、组件元素嵌套、组合,最后用递归渲染的方式构建出彻底的 DOM 元素树。 可是这种写法不容易阅读和维护了,JSX 语法就应运而生了。
const Button = ({ color, text }) => {
<button className={`btn btn-${color}`}>
<em>{text}</em>
</button>;
};
复制代码
JSX 将 HTML 语法直接加入到 JavaScript 代码中,再经过翻译器转换成纯 JavaScript 后再由浏览器执行。
JSX 的官方定义是类 XML 语法的 ECMAScript 扩展。
使用类 XML 语法,咱们能够清晰地看到 DOM 树状结果及其属性。只不过它被包裹在 JavaScript 的方法中
const List = () => (
<ul> <li>1</li> <li>2</li> </ul>
);
复制代码
须要注意几点
const c = () => (<span>1</span><span>2</span>);
复制代码
会报错,最外层没有包裹,显然没法转译成 React.createElement 方法调用
<div></div>
、<div />
小写字母对应 DOM 元素,大写字母对应组件元素 此外,还有一些特殊的标签值得讨论,好比注释和 DOCTYPE 头 JSX 仍是 JavsScript,依然能够用简单的方法使用注释,在子元素位置使用注释要用{}包起来。 对于经常使用于判断浏览器版本的条件注释
<!--[if IE]>
<p>work in IE</p>
<![endif]-->
复制代码
须要用 JavaScript 判断来实现
{
(!!window.ActiveXObject || 'ActiveXObject' in window) ?
<p>work in IE</p> : ''
}
复制代码
DOCTYPE 头是一个很是特殊的标志,通常会在 React 服务端渲染时用到。DOCTYPE 是没有闭合的,咱们没法渲染它。常见的作法是构造一个保存 HTML 的变量,将 DOCTYPE 和整个 HTML 标签渲染后的结果串联起来。
DOM 元素属性是标准规范属性,但 class 和 for 因为是关键字,由 className 和 htmlFor 替代。 组件元素属性是彻底自定义的属性,也能够理解为实现组件所须要的参数。通常采用小驼峰写法。 此外还有一些特有的属性表达
<Checkbox checked={true} />
复制代码
能够简写成
<Checkbox checked />
复制代码
<Checkbox checked={false} />
复制代码
能够省略为
<Checkbox />
复制代码
const data = { name: "foo", value: "bar" };
const component = <Component {...data} />; 复制代码
<div d="xxx">content</div>
复制代码
须要使用 data-前缀,这和 HTML 标准也是一致的
<div data-attr="xxx">content</div>
复制代码
然而在自定义标签中,任意属性都是被支持的
<CustomComponent d="xxx" />
复制代码
<Person name={true ? 1 : 2} />
复制代码
©
不会正确显示。 能够经过如下方法解决 1.直接使用 UTF-8 字符 2.使用 Unicode 编码 3.使用功数组组装<div>{['cc ', <span>©</span>, ' 2015']}</div>
4.直接插入原始的 HTMLReact 还提供了 dangerouslySetInnerHTML 属性。它避免了 React 转义字符,请在肯定必要的状况下使用它
<div dangerouslySetInnerHTML={{ __html: "cc © 2015" }} />
复制代码
在 MVC架构出现以前,组件主要分为两种
封装的基本思路就是面向对象思想。交互基本上以操做 DOM 为主。逻辑上是结构上哪里须要变,咱们就操做哪里。如下是几项规范标准组件的信息。
这个阶段,前端在应用级别没有过多复杂的交互。传统组件的主要问题在于结构、样式与行为没有很好地结合,不一样参数下的逻辑可能会致使不一样的渲染逻辑,这时就会存在大量的 HTML 结构与 style 样式的拼装。逻辑一旦复杂,开发及维护成本至关高。
因而分层思想引进了,出现了 MVC 架构。View 只关心怎么输出变量,因此就诞生了各类各样的模板语言。让模板自己承载逻辑,能够帮咱们解决 View 上的逻辑问题。对于组件来讲,能够将拼装 HTML 的逻辑部分解耦出去,解决了数据与界面耦合的问题。
模板做为一个 DSL,也有其局限性。在 Angular 中,咱们看到了在 HTML 上定义指令的方式。
W3C 将相似的思想制定成了规范,称为 Web Components。它经过定义 Custom Elements(自定义元素)的方式来统一组件。每一个自定义元素能够定义本身对外提供的属性、方法,还有事件,内部能够像写一个页面同样,专一于实现功能来完成对组件的封装。
Web Components 由 4 个组成部分:HTML Templates 定义了以前模板的概念,Custom Elements 定义了组件的展示形式,Shadow DOM 定义了组件的做用域范围、能够囊括样式,HTML Imports 提出了新的引入方式。
事实上,它仍是须要时间的考验的。由于诸如如何包装在这套规范之上的框架,如何得到在浏览器端的所有支持,怎么与现代应用架构结合等等。但它倒是开辟了一条罗马大道,告诉咱们组件化能够这样去作。
React 的本质就是关心元素的构成,React 组件即为组件元素。组件元素被描述成纯粹的 JSON 对象,意味着可使用方法或是类来构建。React 组件基本上由 3 个部分组成-属性(props)、状态(state)以及生命周期方法。
React 组件能够接收参数,也可能有自身状态。一旦接收到的参数或自身状态有所改变,React 组件就会执行相应的生命周期方法,最后渲染。
1.React 与 Web Components 从 React 组件上看,它与 Web Components 传达的理念是一致的,但二者的实现方式不一样:
React 在纯 JavaScript 上下了工夫,将 HTML 结构完全引入到 JavaScript 中。这种作法褒贬不一,但有效地解决了组件所要解决的问题之一。
2.React 组件的构建方法 React 组件基本上由组件的构建方式、组件内的属性状态与生命周期方法组成。
React 组件构建上提供了 3 种不一样的方法:React.createClass、ES6 classes 和无状态函数。
React.createClass 用 React.createClass 构建组件是 React 最传统、也是兼容性最好的方法。
const Button = React.createClass({
getDefaultProps() {
return {
color: "blue",
text: "Confirm"
};
},
render() {
const { color, text } = this.props;
return (
<button className={`btn btn-${color}`}> <em>{text}</em> </button>
);
}
});
复制代码
ES6 classes ES6 classes 的写法是经过 ES6 标准的类语法的方式来构建方法:
import React, { Component } from "react";
class Button extends Component {
contructor(props) {
super(props);
}
static defaultProps = {
color: "blue",
text: "Confirm"
};
render() {
const { color, text } = this.props;
return (
<button className={`btn btn-${color}`}> <em>{text}</em> </button>
);
}
}
复制代码
与 createClass 的结果相同的是,调用类实现的组件会建立实例对象。
咱们很容易联想到组件抽象过程当中也可使用继承的思路。在实际应用中,咱们极少让子类去继承功能组件。继承牵一发而动全身。在 React 组件开发中,经常使用的方式是将组件拆分到合理的粒度,用组合的方式合成业务组件。
说明:React 的全部组件都继承自顶层类 React.Component。它的定义很是简洁,只是初始化了 React.Component 方法,声明了 props、context、refs 等,并在原型上定义了 setState 和 foreUpdate 方法。内部初始化的生命周期方法与 createClass 方式使用的是同一个方法建立的。
无状态函数 使用无状态函数构建的组件称为无状态组件
function Button({ color = "blue", text = "Confirm" }) {
return (
<button className={`btn btn-${color}`}> <em>{text}</em> </button>
);
}
复制代码
无状态组件只传入 props 和 context 两个参数;也就是说,它不存在 state,也没有生命周期方法,组件自己即上面两种 React 组件构建方法中的 render。不过,像 propTypes 和 defaultProps 仍是能够经过向方法设置静态属性来实现的。
无状态组件不像上述两种方法在调用时会建立新实例,它建立时始终保持了一个实例,避免了没必要要的检查和内存分配。
在 React 中,数据是自顶向下单向流动的,即从父组件到子组件。
state 与 props 是组件中最重要的概念。若是顶层组件初始化 props,那么 React 会向下遍历整棵组件树,从新尝试渲染全部相关的子组件。state 只关心组件本身内部的状态,这些状态只能在组件内改变。把组件当作一个函数,props 就是它的参数,内部由 state 做为函数的内部参数,返回一个 Virtual DOM 的实现。
在 React 中,state 为组件内部状态。当组件内部使用 setState 方法时,该组件会尝试从新渲染。
值得注意的,setState 是一个异步方法,一个生命周期内全部的 setState 方法会合并操做。
咱们思考一个常规的 Tabs 组件,对于 activeIndex 做为 state,就有两种不一样的视角。
固然,实现组件时,能够同时考虑兼容这两种
constructor(props) {
super(props);
const currProps = this.props;
let activeIndex = 0;
// 来源于须要外部更新的
if (activeIndex in currProps) {
activeIndex = currProps.activeIndex;
// 来源于使用内部更新的
} else if ('defaultActiveIndex' in currProps) {
activeIndex = currProps.defaultActiveIndex;
}
this.state = {
activeIndex,
prevIndex: activeIndex,
};
}
复制代码
props 是 React 用来让组件之间互相联系的一种机制,通俗地说就像方法的参数同样。
props 的传递过程,对于 React 组件来讲很是直观。React 的单向数据流,主要的流动管道就是 props。props 自己是不可变的。组件的 props 必定来自于默认属性或经过父组件传递而来。
React 为 props 提供了默认配置,经过 defaultProps 静态变量的方式来定义。
static defaultProps = {
classPrefix: 'tabs',
onChange: () => {},
};
复制代码
在 React 中有一个重要且内置的 props——children,它表明组件的子组件集合。
实现的基本思路以 TabContent 组件渲染 TabPane 子组件集合为例来说
getTabPanes() {
const { classPrefix, activeIndex, panels, isActive } = this.props;
return React.Children.map(panels, (child) => {
if (!child) { return; }
const order = parseInt(child.props.order, 10);
return React.cloneElement(child, {
classPrefix,
isActive,
children: child.props.children,
key: `tabpane-${order}`,
});
});
}
复制代码
它是经过 React.Children.map 方法遍历子组件,同时利用 React 的 cloneElement 方法克隆到 TabPane 组件,最后返回这个 TabPane 组件集合。
React.Children 是 React 官方提供的一系列操做 children 的方法。它提供诸如 map、forEach、count 等实用函数。 使用 getTabPanes
render () {
return (<div>{this.getTabPanes()}</div>);
}
复制代码
假如咱们把 render 方法中的 this.getTabPanes 方法中对子组件的遍历直接放进去
render() {
return (<div>{React.Children.map(this.props.children, (child) => {...})}</div>)
}
复制代码
这种调用方式称为 Dynamic Children(动态子组件)。
也能够将子组件以props的形式传递。通常咱们会用这种方法让开发者定义组件的某一个prop,让其具有多种类型,来作到简单配置和自定义配置组合在一块儿的效果。
this.props.onChange(activeIndex, prevIndex)
复制代码
触发了onChange prop回调函数给父组件必要的值。
propTypes用于规范props的类型与必需的状态。它会在开发环境下,对组件的prop值的类型做检查。
static propTypes = {
classPrefix: React.PropTypes.string,
}
复制代码
propTypes有不少类型支持,不只有基本类型,还包括枚举和自定义类型。
1.组件的挂载 这个过程主要作组件状态的初始化,咱们推荐如下面例子为模板写初始化组件:
import React, { Component, PropTypes } from 'react';
class App extends Component {
static propTypes = {
// ...
}
static defaultProps = {
// ...
}
constructor(props) {
super(props)
this.state = {
// ...
}
}
componentWillMount() {
// ...
}
componentDidMount() {
// ...
}
render () {
return <div>This is a demo</div>
}
}
复制代码
若是咱们在componentWillMount中执行setState方法,组件会更新state,但组件只渲染一次。所以,这是无心义的执行,彻底能够放在constructor初始化state中。 若是咱们在componentDidMount中执行setState方法,组件会再次更新,不过在初始化过程就渲染了两次组件,这并非一次好事。但实际状况,有一些场景必须这么作,好比须要获取组件的位置。 2. 组件的卸载 componentWillUnmount,咱们经常会执行一些清理方法,好比事件回收、清除定时器。
更新过程指的是父组件向下传递props或组件自身执行setState方法时发生的一系列更新动做。
import React, { Component, PropTypes } from 'react'
class App extends Component {
componentWillReceiveProps(nextProps) {
// this.setState({})
}
shouldComponentUpdate(nextProps, nextState) {
// return true
}
componentWillUpdate(nextProps, nextState) {
}
componentDidUpdate(prevProps, prevState) {
}
render() {
}
}
复制代码
若是组件自身的state更新了,会依次执行shouldComponentUpdate、componentWillUpdate、render和componentDidUpdate
shouldComponentUpdate接收须要更新的props和state,让开发者增长判断逻辑,不须要更新方法最终返回false便可,这是性能优化的手段之一。
无状态组件是没有生命周期方法的,这也意味着它没有shouldComponentUpdate。渲染该类组件,每次都会从新渲染。
componentWillUpdate方法提供的是须要更新的props和state,而componentDidUpdate提供更新前的props和state。
注意不能在componentWillUpdate执行setState方法,会致使循环执行render。
若是组件是由父组件更新props而更新的,那么在shouldComponentUpdate以前会先执行componentWillRecieveProps方法。此方法能够做为React在props传入后,渲染以前setState的机会,在此方法中调用setState是不会二次渲染的。
componentWillReceiveProps(nextProps) {
if ('activeIndex' in nextProps) {
this.setState({
activeIndex: nextProps.activeIndex
})
}
}
复制代码
ReactDOM中的API很是少,只有findDOMNode、unmountComponentAtNode和render。 1.findDOMNode Reactz提供的获取DOM元素的方法有两种,其中一种就是ReactDOM提供的findDOMNode:
DOMElement findDOMNode(ReactComponent component)
复制代码
当组件被渲染到DOM后,findDOMNode返回该React组件实例相应的DOM节点。它能够用于获取表单的value以及用于DOM的测量。
class App extends Component {
componentDidMount() {
const dom = ReactDOM.findDOMNode(this)
}
render() {}
}
复制代码
ReactComponent render(
ReactElement element,
DOMElement container,
[function callback] ) 复制代码
该方法把元素挂载到container中,而且返回element的实例(即refs引用)。若是是无状态组件,render会返回null。当组件装载完毕时,callback被调用。
与render相反,React还提供了一个不多使用的unmountComponentAtNode方法来进行卸载操做。
unstable_renderSubtreeIntoContainer。它能够更新组件到传入的DOM节点。它与render方法相比,区别在因而否传入父节点。
另外一个ReactDOM中的不稳定方法unstable_batchedUpdates是关于setState更新策略的。
它是React组件中很是特殊的prop,能够附加到任何一个组件上。组件被调用时会新建一个该组件的实例,而refs就会指向这个实例。
findDOMNode和refs都没法用于无状态组件中,无状态组件挂载只是方法调用,没有新建实例。
调用HTML5 Audio/Video的play方法和input的focus方法,React就无能为力了,须要使用相应的DOM方法来实现。
还有组件之外区域(通常指document、body)的事件绑定、DOM的尺寸计算。
《深刻React技术栈》