1. Reat简介
专注于视图层
React不是完整的MVC/MVVM框架,它专注于视图层,提供清晰,简洁的视图层解决方案。听起来和模板引擎很像,但是React又是一个包括view和controller的库。
React不像其他框架提供很多复杂的概念和繁琐的API,它以Minimal API Interface为目标,只提供组件化相关的非常少量的API.
Vitrual DOM(虚拟DOM)
我们知道在页面中的结构对应着一个DOM树。在传统开发中,在更新页面的时候,需要手动的操作DOM。如图:
但是,总所周知,操作DOM的代价是什么昂贵的。因为操作DOM性能消耗是很大的,而且这样的代码也很难维护(使用过jQuery的同学应该有所体会)。
在react中,React把真实DOM树转为JavaScript对象树,也就是所谓的虚拟DOM。
在每次更新后,会重新计算Virtual DOM,并和上一次的Virtual DOM进行对比,对发生的部分进行批量更新。在React的生命周期中,提供了一个shouldComponentUpdate生命周期回调,通过判断可以减少数据变化后不必要的Virtual DOM的对比过程,提高性能。
但是进行虚拟DOM的比较也是需要消耗性能的,React还有另外一个亮点,就是方便和其他平台进行集成。
函数式编程
之前,一直以命令式编程为主,命令式编程解决的是做什么的问题,就像是给电脑下命令。而函数式编程,对应的是声明式编程。如果是人的思考方式,则是构建一个规则,然后应用该规则。
回到UI界面上,React把过去不断重复构建UI的过程抽象成了组件,且在给定参数的情况下约定渲染对应的UI界面。react能充分利用很多函数式方法去减少冗余代码。此外它本身就是简单函数,所以易于测试。可以说,函数式编程才是react精髓。
2. JSX语法
说到react大家肯定会想到JSX语法。React为方便view层组件化,承载了构建HTML结构化页面的职责。与一般的JavaScript模板语言有着异曲同工之处,但是不同的是,react是通过创建与更新虚拟元素来管理整个Virtual DOM的。
其中,虚拟元素可以理解为真实元素的对应,它的构建与更新都是在内存中完成,并不会渲染到真实的DOM中去。在React中创建的虚拟元素可以分为两类,DOM元素和组件元素。
- DOM元素
web页面是由一个个HTML元素嵌套组合而成,当使用JavaScript来描述这些元素的时候,这些元素可以被表示成JSON对象:
<button class="btn btn-blue">
<em>confirm</em>
</button>
其中包括了元素的类型和属性,如果转为JSON对象,那么依然包括元素的类型以及属性:
{
type: 'button',
props: {
className: 'btn btn-blue',
children: [{
type: 'em',
props: {
children: 'confirm'
}
}]
}
}
如上,我们就可以在JavaScript中创建Virtual DOM元素了。
在react中,到处都是可以复用的元素,这些元素并不是真实的实例,它只是让react告诉开发者想要在屏幕上显示什么。我们无法通过方法去调用这些元素,他们只是不可变的描述对象。
- 组件元素
我们封装一下上面的button按钮:
const Button = ({color, text}) => {
return {
type: 'button',
props: {
className: `btn btn-${color}`,
children: {
type: 'em',
children: text
}
}
}
}
自然,我们要生成DOM元素中具体的按钮时,就可以方便的调用Button({color: 'blue', text: 'confirm'})来创建。
仔细思考这个可以发现,Button方法其实也可以作为元素而存在,方法名对应了DOM元素类型,参数对应了DOM元素属性。这样构建的元素就是自定义类型的元素,或者称为组件元素。我们用JSON结果来描述:
{
type: Button,
props: {
color: 'blue',
text: 'confirm'
}
}
这也是react的核心思想之一。因为有了公共的表达方法,我们就可以让元素彼此嵌套。这些层层封装的组件元素,就是所谓的react组件。我们最终可以用递归的方式构建出完全的DOM树。
我们再看一个封装的更深的例子,为Button元素再封装一次,它由一个方法构建:
const dangerButton = ({text}) => {
type: Button,
props: {
color: 'red',
children: text
}
}
直观的看,dangerButton从视觉上为我们定义了一个'危险的按钮',接着,我们可以轻松的运用,继续封装新的组件元素:
const deleteButon = () => ({
type: 'div',
props: {
children: [{
type: 'p',
props: {
children: 'Are you sure?',
}
}, {
type: dangerButton ,
props: {
children: 'confirm'
}
}, {
type: Button,
props: {
color: 'blue',
children: 'cancle'
}
}]
}
})
如上,清晰的表示了一个功能模块。但是在结构复杂的时候,它就力不从心了。此时JSX语法出现。假如使用JSX表示:
const DeleteAccount = () => (
<div>
<p>are you sure?</p>
<DangerButton>Confirm</DangerButton>
<Button color="blue">Cancel</Button>
</div>
)
如你所见,JSX 将 HTML 语法直接加入到 JavaScript 代码中,再通过翻译器转换到纯
JavaScript 后由浏览器执行。
JSX基本语法
JSX 的官方定义是类 XML 语法的 ECMAScript 扩展。它完美地利用了 JavaScript 自带的语法
和特性,并使用大家熟悉的 HTML 语法来创建虚拟元素。
- XML基本语法
使用类 XML 语法的好处是标签可以任意嵌套,我们可以像 HTML 一样清晰地看到 DOM 树
状结构及其属性。
注意:定义标签时,只允许被一个标签包裹,标签一定要闭合。
- 元素类型
我们讲到两种不同的元素:DOM 元素和组件元素。对应规则是 HTML 标签首字母是否为小写字母,其中小写首字母对应 DOM 元素,而组件元素自然对应大写首字母。等到依赖的组件元素中不再出现组件元素,我们就可以将完整的 DOM 树构建出来了。
JSX 还可以通过命名空间的方式使用组件元素,以解决组件相同名称冲突的问题,或是对一
组组件进行归类。
const App = () => (
<MUI.RaisedButton label="Default" />
)
- 元素属性
class 属性改为 className;for 属性改为 htmlFor 。
- JavaScript 属性表达式
属性值要使用表达式,只要用 {} 替换 "" 即可。
- 展开属性
如果事先知道组件需要的全部属性,JSX 可以这样来写:const component = <Component name={name} value={value} />;
如果你不知道要设置哪些 props,那么现在最好不要设置它:const component = <Component />;
component.props.name = name;
component.props.value = value;
上述这样是反模式,因为 React 不能帮你检查属性类型(propTypes)。这样即使组件的属性
类型有错误,也不能得到清晰的错误提示。
这里,可以使用 ES6 rest/spread 特性来提高效率:
const component = <Component name={data.name} value={data.value} />;```
可以写成:
const component = <Component {...data} />`
3. React组件
React组件基本上由三个部分组成-属性,状态,生命周期。
react可以接受参数,也可以有自身状态,一旦接受到的参数或者自身状态改变,react组件就会执行相应的生命周期方法,最后渲染。
- react组件的构建方法
react组件基本上由组件的构建方式,组件内的属性状态与生命周期方法组成。
(1) 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>
);
}
});
从表象上看, React.createClass 方法就是构建一个组件对象。当另一个组件需要调用 Button
组件时,只用写成 <Button /> ,就可以被解析成 React.createElement(Button) 方法来创建 Button实例,这意味着在一个应用中调用几次 Button,就会创建几次 Button 实例。
(2) ES6 classes
import React, { Component } from 'react';
class Button extends Component {
constructor(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.Component 。它的定义非常简洁,只是初始化了
React.Component 方法,声明了 props 、 context 、 refs 等,并在原型上定义了 setState 和forceUpdate 方法。内部初始化的生命周期方法与 createClass 方式使用的是同一个方法
创建的。
(3)无状态函数
使用无状态函数构建的组件称为无状态组件。
function Button({ color = 'blue', text = 'Confirm' }) {
return (
<button className={`btn btn-${color}`}>
<em>{text}</em>
</button>
);
}
无状态组件只传入 props 和 context 两个参数;也就是说,它不存在 state ,也没有生命周期方法,组件本身即上面两种 React 组件构建方法中的 render 方法。不过,像 propTypes 和
defaultProps 还是可以通过向方法设置静态属性来实现的。在适合的情况下,我们都应该且必须使用无状态组件。无状态组件不像上述两种方法在调用时会创建新实例,它创建时始终保持了一个实例,避免了不必要的检查和内存分配,做到了内部优化。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。