@tsingwong
2016-10-13T08:28:36.000000Z
字数 6921
阅读 902
React
如今,Web 开发朝着 组件化
的趋势发展。主要有现今比较流行的AngularJS和React为代表。
假如,HTML是一个轮子,AngularJS的组件化是给轮子加了个金边;而React是重新造了个轮子JSX
。
由于HTML的DOM操作非常消耗资源,React引入了虚拟DOM。开发人员操作虚拟DOM,React在必要的时候将它们渲染到真正的DOM上。比较类似游戏开发中,使用的双缓冲区
帧重绘。
引入虚拟DOM的另一个好处是,容易引入不同的渲染引擎。例如,将应用代码渲染到HTML的DOM,或者nodejs服务端的无头DOM(这个地方不清楚什么意思,等以后看了nodejs之后再来填坑吧), 还有就是IOS/Android平台组件——即React Native
。
引入React库之后,就可以通过React对象来操作。即:在虚拟DOM上创建元素,然后将他们渲染到真实DOM上。
createElement(type, [props], [children...]): 在虚拟DOM上创建指定的React元素
type
:指定要创建的元素类型,可以是字符串或者是React组件类型等。当使用字符串时,这个参数应该是标准的HTML标签名称,如p、div、canvas等。props
:是可选的JSON对象,用来指定元素的附加属性,如CSS样式。chikdren
:可以把它理解成通过这些子元素参数,构造虚拟DOM树。render(element, container, [callbcak]):将虚拟DOM上的对象渲染到真实DOM上。
element
:React元素或者HTML的元素。container
:真实DOM中的HTML元素,作为渲染的目标容器,其内容被render()方法的执行所改变。callback
:可选函数,当渲染完成或更新后被执行。
<div id="example"></div>
<script type="text/babel">
var el = React.createElement(
"ul",
null,
React.createElement("li", null, "China"),
React.createElement("li", null, "Japan"),
React.createElement("li", null, "Korea")
);
ReactDOM.render(el, document.querySelector('#example'), alert('Hello World!'));
</script>
上面的例子是在虚拟DOM中创建了一个具有三个li子元素的ul元素,然后将虚拟DOM上的元素渲染到真实DOM上的example容器里。
虚拟DOM是React的基石。
在React中,应用程序在虚拟DOM上操作,每次需要渲染的时候,会先比较当前DOM和待渲染内容的差异,然后再决定如何最优的更新DOM,该过程被称为 reconciliation。
除此之外,虚拟DOM还可以提供一致的开发方式来开发服务器端应用、Web应用和手机应用。通过虚拟DOM,配置不同的渲染器,将虚拟DOM内容渲染到不同的平台上。
组件的设计目的是提高代码复用率
、降低测试难度
、代码复杂度
。
高内聚低耦合
,易于对单个组件进行测试在React中定义一个组件非常容易。组件就是一个实现预定义接口的JavaScript类。
组件的基本形式是:
React.createClass(meta)
参数meta
:一个实现预定义接口的JavaScript对象,用来对React组件原型进行拓展。至少需要一个实现render()方法,而这个方法,必须且只能返回一个有效的React元素。
若组件由多个元素构成,那么必须在外边包一个顶层元素,然后返回这个顶层元素。如:
render:function(){
return React.createElement(
"div",null,
React.createElement("div",null,"header"),
React.createElement("div",null,"content"),
React.createElement("div",null,"footer"),
React.createElement("div",{className: "ez-led"},"footer2")
);
}
注:React组件名称的首字母必须大写。还有一点是class属性使用className代替,for属性使用htmlFor代替。因为class和for是JavaScript的保留字。
组件本质上是状态机
,输入确定,输出一定确定。
状态发生转换时会触发不同的钩子函数,从而让开发者有机会做出相应。
组件生命周期分为三个状态:
React分别为每个状态提供了两种处理函数。will
函数在进入状态之前调用,did
函数在进入状态之后调用,三种状态共计五种处理函数。
componentWillMount()
componentDidMount()
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object preProps, object prevState)
componentWillUnmount()
此外还有两种特殊状态的处理函数
// 已加载组件收到新的参数时调用
componentWillReceiveProps(object nextProps)
// 组件判断是否重新渲染时调用
shouldComponentUpdate(object nextProps, object nextState)
初始化阶段有以下五个钩子函数:
运行中阶段有以下五个钩子函数:
销毁阶段有一个钩子函数:
JSX(JavaScript XML),是对JavaScript语法的拓展,它有助于在JavaScript代码中以类似HTML的形式创建React元素。
JSX有以下特点:
简单说,就是每当你需要使用createElement()
时,就可以把这个函数调用部分用渲染目标HTML代替(如同上面提到的,这里的class属性仍然需要使用className):
var Comp = React.creatClass({
render: function() {
var e =
<div>
<div className="ez-led">Hello React!</div>
<div className="ez-led">React Hello!</div>
</div>;
retrun e;
};
});
ReactDOM.render(
<Comp />,
document.querySelector("#content")
);
JSX语法:
JSX中不能使用if语句,于是有四种方法来代替:
JSX引入三个非DOM属性:dangerouselySetInnerHTML、 ref、 key
dangerouselySetInnerHTML:在JSX中直接插入HTML代码
ref:父组件引用子组件
key:提高渲染性能(diff算法)
diff算法流程:
1. 节点是否相同?不同直接结束使用新节点,相同的话继续;
2. 是否为自定义节点?不是的话比较属性,增加新属性;是自定义节点,重新渲染,比较结果。
JSX的引用方法与JavaScript略有不同:
1.指定脚本类型
// 内联
<script type="text/jsx">...</script>
// 外联
<script type="text/js" src="a.js"></script>
2.引入JSX语法转换库
在HTML中使用JSX,还需要引入JSX语法转换库JSXTransform.js
。该库加载之后,将在DOM树构造完成后(通过监听DOMContentLoaded
事件)处理JSX脚本:
1. 搜索DOM树中的script节点,类型为text/jsx进行后续处理
2. 读取script节点的内容,将其转化成JavaScript代码
3. 构造一个新的script元素,设置其内容为转化后的代码,并追加到DOM的head元素中
JSXTransform.js
引入后通过全局对象JSXTranformer
提供了API接口,可以使用transform()方法来模拟这个语法自动转换过程。
<script type="text/javascript">
// 模拟JSX语法转换
var transform = function() {
var el =document.querySelector("#demo"),
// 将原始JSX代码转化为JS代码
jsxt = JSXTransformer.transform(el.text);
var headEl = document.querySelector("head"),
scriptEl = document.createElement("script");
// 设置新的script元素的内容为转换后的代码
scriptEl.text = jsxt.code;
scriptEl.type = "text/javascript";
// 将新的script元素添加到文档的head元素
headEl.appendChild(scriptEl);
这里需要提到的一点是
<script type="text/jsx"></script>
与<script type="text/jsx"></script>
。
前者需要使用其自身用于JSX语法解析的编译器jSTransform
,
后者需要第三方Babel
的JSX编译器实现。
React官方在2015年6月发布声明,不再维护jSTransform
,所以建议还是都使用后者。
props = properties
属性:一个事物的性质与关系,往往与生俱来,无法自己修改的。
一般是由父组件传递过来的,想当于出生就具有的,但无法自己修改。
属性用法:
1. 传入键值对,值可以是字符串,或是用{}包裹的JS求值表达式、数组、变量等
<HelloWorld name="tsingwong"></HelloWorld>
2. 传入"..."+对象,自动将对象中的属性和值当做属性赋值
var props = {
one: "123",
two:321
}
<HelloWorld {...props}></HelloWorld>
3. 调用React提供的setProps方法设置属性,实际中不提倡使用,一般不会在组件内部修改属性
var instance = React.render(<HelloWorld></HelloWorld>,
document.getElementById("example");
instance.setProps({name: "Tim"});
state
状态:事物所处的状况
状态是由事物自行处理、不断变化的。
状态用法:
getInitialStateP: 初始化每个实例特有的状态
setState:更新组件状态
相似点:
1. 都是纯JS对象;
2. 都会触发render更新;
3. 都具有确定性。
属性状态对比 | 属性 | 状态 |
---|---|---|
能否从父组件获取初始值? | √ | X |
能否由父组件修改? | √ | X |
能否在组件内部设置默认值? | √ | √ |
能否在组件内部修改? | X | √ |
能否设置自组件的初始值? | √ | X |
能否修改子组件的值 | √ | X |
注:便于记忆,状态只跟自己相关,由自己维护;组件不能修改属性
组件在运行时需要修改的数据是状态。
事件处理函数主要分为两种
事件处理函数绑定到事件上
onClick={this.handleClick}
触摸类事件,仅在移动端触发:
- onTouchCancel
- onTouchEnd
- onTouchMove
- onTouchStart
键盘类事件:
- onKeyDown
- onKeyPress
- onKeyUp
剪切类事件:
- onCopy
- onCut
- onPaste
表单类事件:
- onChange
- onInput
- onSubmit
焦点类事件:
- onFoucs
- onBlur
UI元素类事件:
- onScroll
滚轮事件:
- onWheel
鼠标事件:
- onClick
- onContextMenu:右键——即上下文菜单
- onDoubleClick
- onMouseDown
- onMouseEnter
- onMouseLeave
- onMouseMove
- onMouseOut
- onMouseOver
- onMouseUp
handleChange: function(event) {
console.log(event.target.value);
}
组件协同本质上是对组件的一种组织、管理方法。
目的:
方法:
1. 组件嵌套: 在垂直方向上,由下至上的提取——代码的封装
2. Mixin:在水平方向上抽离——代码的复用
组件嵌套的本质是父子关系。
间接双向通信
父组件传递属性给子组件,
子组件以父组件的事件处理函数,子组件触发父组件函数(委托)
组件嵌套优点:
组件嵌套缺点:
Mixin = 一组方法
Mixin的目的是横向抽离出组件的相似代码
相关概念:面向切面编程、插件
Mixin优点:
Mixin缺点: