ReactJS的学习笔记
React介绍
React只是一个view层的解决方案,它做的主要事情就是
- 前端控件组件化
- 利用 props 形成单向的数据流
- 根据 state 的变化来更新 view
- 利用虚拟 DOM 来提升渲染性能
React通过props和state去渲染界面,所以有个很形象的描述UI = f(props,state)
.
初期, react基本的操作无外乎就是围绕着,Component和ReactDOM进行编写和渲染。 以前, react并没有分的太细化, 不过在version 0.14版本后, ReactDOM和Component就分开了, 主要还是因为React Native的缘由. 不过, 两者分开过后,造成的影响来说, 还是不小的. 主要影响到三个方面:
-
ReactDOM.render(): 渲染UI层
-
ReactDOM.findDOMNode(): 获取节点
-
ReactDOM.renderToString(): 后端运用的转义String方法
this.props
在组件复用中,是占很重要的地位的, 可以说,他是parent和children 组件通信的重要通道, 父组件赋值, 子组件处理
react 提供另外一个状态属性this.state
. React 通过改变父组件本身的state
, 会导致DOM的重新渲染, 相当于重新调用ReactDOM.render()
.
能够引起state变化的动作有
- 页面上用户的活动
- 外部例如服务器
每个组件都有自己的setState
方法来改变组件当前的state,可以把更改state的逻辑写在各自的组件中,但是当项目逻辑愈发复杂是,这样就存在问题,对于开发人员很难理清state和view之间的关系。最好的解决方案值对引起state变化的情况进行统一管理。
JSX语法
- 嵌套规则
标签可以任意的嵌套 - 标签闭合
标签必须严格闭合,否则无法编译通过
很简单, 就是所有标签必须闭合. 比如像[站外图片上传中……(10)]这样的, 后面的/ 可以写可不写. 但是, 现在在JSX中,所有的都必须写.
- 驼峰命名的属性
在JSX中, 给tag添加属性时,需要使用驼峰命令. 即
// HTML
<input type="text" name="usrname" maxlength="10">
// JSX
<input type="text" name="usrname" maxLength="10"><br>
但像,这样的data-set 使用dash连接的就不需要额外的注意. 另外, react 特别强调了class需要写为className. 因为在React内容,class 存在冲突, 所以强制性将class property改为了className.
- 表单输入
一旦你设置在input的value属性. 后面你所有的输入都是无效的.
<input type="search" value="React" />
这里就需要使用onChange和this.state来帮助我们进行重绘.
class Search extends Component {
constructor(){
super();
this.state={
value:"React"
}
}
render() {
return ( <div>
Search Term: <input type="search" value={this.state.value} onChange={this.handleChange.bind(this)} /> </div>
)
}
handleChange(event){
this.setState({
value:event.target.value
})
}
}
通过输入,触发onChange, 然后onChange 触发this.setState, 重新渲染DOM
JSX所有语法注意点总结如下:
JSX组件
JSX 组件分为 HTML 组件和 React 组件
关于Virtual DOM的理解
React的意思是,我提供一个Component,然后你只管给我数据,界面的事情完全不用你操心,我保证会把界面变成你想要的样子。
你可以把一个React的Component想象成一个Pure Function,只要你给的数据是[1, 2, 3],我保证显示的是[1, 2, 3]。没有什么删除一个Element,添加一个Element这样的事情。NO。你要我显示什么就给我一个完整的列表。
Virtual DOM和DOM是啥关系呢?
首先,Virtual DOM并没有完全实现DOM,Virtual DOM最主要的还是保留了Element之间的层次关系和一些基本属性。因为DOM实在是太复杂,一个空的Element都复杂得能让你崩溃,并且几乎所有内容我根本不关心好吗。所以Virtual DOM里每一个Element实际上只有几个属性,并且没有那么多乱七八糟的引用。所以哪怕是直接把Virtual DOM删了,根据新传进来的数据重新创建一个新的Virtual DOM出来都非常非常非常快。(每一个component的render函数就是在做这个事情,给新的virtual dom提供input)
所以,引入了Virtual DOM之后,React是这么干的:
你给我一个数据,我根据这个数据生成一个全新的Virtual DOM,然后跟我上一次生成的Virtual DOM去 diff,得到一个Patch,然后把这个Patch打到浏览器的DOM上去。完事。
最后,回到为什么Virtual Dom快这个问题上。
其实是由于每次生成virtual dom很快,diff生成patch也比较快,而在对DOM进行patch的时候,我能够根据Patch的内容,优化一部分DOM操作,比如之前1.2里的那个例子。
重点就在最后,哪怕是我生成了virtual dom,哪怕是我跑了diff,但是我根据patch简化了那些DOM操作省下来的时间依然很可观。所以总体上来说,还是比较快。
而更糟糕的是,我们(以及很多框架)在调用DOM的API的时候做得不好,导致整个过程更加的慢。React的Virtual Dom解决的是这一部分问题,它并不能解决DOM本身慢的问题。
比如说,现在你的list是这样,
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
你想把它变成这样
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
通常的操作是什么?
先把0, 1,2,3这些Element删掉,然后加几个新的Element 6,7,8,9,10进去,这里面就有4次Element删除,5次Element添加。
而React会把这两个做一下Diff,然后发现其实不用删除0,1,2,3,而是可以直接改innerHTML,然后只需要添加一个Element(10)就行了,这样就是4次innerHTML操作加1个Element添加,比9次Element操作快多了吧?
作者:EMayej Bee
链接:http://www.zhihu.com/question/29504639/answer/44680878
来源:知乎
著作权归作者所有,转载请联系作者获得授权。
Flux组件
Flux简介
Flux 的思维模型1Flux 的思维模型2
其中 Dispatcher 是 Flux 的核心枢纽,它相当于是一个事件分发器,将那些分散在各个组件里面的逻辑代码收集起来,统一在 Dispatcher 中进行处理
完整的Flux处理流程如下
- 用户通过与 view 交互或者外部产生一个 Action
- Dispatcher 接收到 Action 并执行那些已经注册的回调,向所有 Store 分发 Action
- 通过注册的回调,Store 响应那些与他们所保存的状态有关的 Action。
- Store 会触发一个 change 事件,来提醒 controller-views 数据已经发生了改变。
- Controller-views 监听这些事件并重新从 Store 中获取数据。这些 controller-views 调用他们自己的 setState() 方法,重新渲染自身以及组件树上的所有后代组件。
Flux优点
Flux 把所有的 View 都视作愚民,Store 视作资源的拥有者为统治者,统治者需要提供资源(数据)给平民,但是如果平民企图对资源修改(Mutation),必须得先通知给统治者,让统治者决定是否做处理。
我们为 Flux 中的概念分配角色
- View: 平民
- Action: 资源修改操作
- Dispatcher: 审核官
- Store: 统治者
一个企图修改资源的操作可以描述为:
View Require Mutation -> Action -> Dispatcher -> Store -> Mutate Handler
平民提交 Mutation 请求,由审核官控制,审核通过后递交给统治者,统治者再分配给亲信做资源 Mutation
合而治之的策略也等于中心化控制策略, 作为统治者既要懂得放权利(资源的分配),也要懂得控制权利(资源的修改),这种收缩自如的合理性是 Flux 简洁的根本。
同时这种思维带来的优点如下:
- View 的独立性和简单性:View 自身的逻辑简单,不需要知道太多事情,只关心上级传来的数据,这种模式使得 View 是低耦合的,简洁的。
- 高可维护性:中心化控制知道所有对资源的操作,如果发生 bug, 可以很快定位问题
Flux实例
学习flux的一些浅显理解
结合github上flux的flux-todomvc项目进行介绍
- Actions
根据我们的需求在这个文件里定义不同的 action 函数,但这里的函数并不涉及逻辑的处理,这里函数只是告诉我们的 Dispatcher,用户进行了什么操作。所以我们只需要给 Dispatcher 传的一个对象,对象里一个必要的属性就是 actionType。如果用户进行这个操作有给我们传的参数的话。那参数也会放在这个对象里。
import AppDispatcher from '../dispatcher/AppDispatcher';
import TodoConstants from '../constants/TodoConstants';
var TodoActions = {
create (text) {
AppDispatcher.dispatch({
actionType: TodoConstants.TODO_CREATE,
text: text
});
},
// other actions
}
export default TodoActions;
当我们执行 AppDispatcher.dispatch 这个方法,并传给他一个有 actionType 属性的对象时,他就会在大喊,“有人做了一个操作呀,这个操作就是 xxx (actionType 的值),还带了个参数,你们哪个来处理一下呀”
- Constants
actionType: TodoConstants.TODO_CREATE
,这个TodoConstants
其实就是我们操作的名字,相当于一个常量,定义在 Constants 里方便管理和调用而已。 - Store
- Store 是一个保存数据的地方
- Store 是一个充满逻辑的地方
var TodoActions = {
/**
* @param {string} text
*/
create: function(text) {
AppDispatcher.dispatch({
actionType: TodoConstants.TODO_CREATE,
text: text
});
},
- Store 是一个响应 Dispatcher 呼喊的地方
在 Store 里,我们通过 Dispatcher “注册”了一个回调函数,每当我们调用 dispatch 函数的时候,就是 Dispatcher 大喊的时候,我们根据不同的 actionType,来调用我们不同的逻辑处理函数,像这样
import AppDispatcher from '../dispatcher/AppDispatcher';
import TodoConstants from '../constants/TodoConstants';
AppDispatcher.register((action) => {
var text;
switch(action.actionType) {
case TodoConstants.TODO_CREATE:
text = action.text.trim();
if (text !== '') {
create(text);
TodoStore.emitChange();
}
break;
// other case
}
});
- Store 是一个鞭策 Controller View 改变的地方
- store学会喊一句 TodoStore.emitChange();
- 为controller view助听器
addChangeListener (callback) { this.on(CHANGE_EVENT, callback) }
import assign from 'object-assign';
var EventEmitter = require('events').EventEmitter;
var TodoStore = assign({}, EventEmitter.prototype, {
areAllComplete () {
for (var id in _todos) {
if (!_todos[id].complete) {
return false;
}
}
return true;
},
getAll () {
return _todos;
},
emitChange () {
this.emit(CHANGE_EVENT);
},
addChangeListener (callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener (callback) {
this.removeListener(CHANGE_EVENT, callback);
}
});
export default TodoStore;
- Controller View
TodoApp.react.js
当组件渲染完成后,就绑定了 Store 的addChangeListener
,并回调了自己的onChange
方法。
Redux介绍
titleredux 可以理解为基于 flux 和其他一些思想(Elm,函数式编程)发展出来的前端应用架构库,作为一个前端数据状态容器体现,并可以在 React 和其他任何前端框架中使用。
前端基本框架图
Redux 就是用来确保 state 变化的可预测性,主要的约束有:
- state 以单一对象存储在 store 对象中
- state 只读
- 使用纯函数 reducer 执行 state 更新
state 为单一对象,使得 Redux 只需要维护一棵状态树,服务端很容易初始化状态,易于服务器渲染。state 只能通过 dispatch(action) 来触发更新,更新逻辑由 reducer 来执行。
区别点 | flux | redux |
---|---|---|
store | 多个store,在store中执行更新逻辑 | 只有一个 ,更新逻辑不再store中执行,而是放在reducer 中 |
dispatcher | 有 | 无 |
React Reflux
Action、Store和组件这三者是通过事件机制响应变化的,构建组件的时候首先需要监听Store的状态。先定义Action和Store
React Reflux
╔═════════╗ ╔════════╗ ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝ ╚════════╝ ╚═════════════════╝
^ │
└──────────────────────────────────────┘
var TodoActions = Reflux.createActions([
'addItem'
]);
var TodoStore = Reflux.createStore({
items: [1, 2],
listenables: [TodoActions],//store监听actions的行为
onAddItem: function (model) {
$.post('/server/add', {data: model}, function (data) {
this.items.unshift(data);
this.trigger(this.items);
});
}
});
var TodoComponent = React.createClass({
mixins: [Reflux.listenTo(TodoStore, 'onStatusChange')],
getInitialState: function () {
return {list: []};
},
onStatusChange: function () {
this.setState({list: TodoStore.items});
},
render: function () {
return (
<div>
{this.state.list.map(function (item) {
return <p>{item}</p>
})}
</div>
)
}
});
React.render(<TodoComponent />, document.getElementById('container'));
区别点 | flux | reflux |
---|---|---|
store | 多个store,在store中执行更新逻辑 | stores可以监听actions的行为,无需进行冗杂的switch判断,stores可以监听actions的行为,无需进行冗杂的switch判断 |
dispatcher | 有 | 通过内部拓展actions的行为,移除了单例的dispatcher |
React
React/React Native 的ES5 ES6写法对照表
参考资料
精益 React 学习指南 (Lean React)序
精益 React 学习指南 (Lean React)- 1.1 React 介绍
最新版React入门
玩物圈前端技术栈总结(React+Redux)
Redux 中文文档
Redux 介绍
浅谈 React、Flux 与 Redux