-
React不是一个完整的MVC框架,最多可以认为是MVC中的V(View),甚至React并不非常认可MVC开发模式;
- React的服务器端Render能力只能算是一个锦上添花的功能,并不是其核心出发点,事实上React官方站点几乎没有提及其在服务器端的应用;
- React的虚拟DOM原理:在Web开发中,我们总需要将变化的数据实时反应到UI上,这时就需要对DOM进行操作。而复杂或频繁的DOM操作通常是性能瓶颈产生的原因(如何进行高性能的复杂DOM操作通常是衡量一个前端开发人员技能的重要指标)。React为此引入了虚拟DOM(Virtual DOM)的 机制:在浏览器端用Javascript实现了一套DOM API。基于React进行开发时所有的DOM构造都是通过虚拟DOM进行,每当数据变化时,React都会重新构建整个DOM树,然后React将当前 整个DOM树和上一次的DOM树进行对比,得到DOM结构的区别,然后仅仅将需要变化的部分进行实际的浏览器DOM更新。而且React能够批处理虚拟 DOM的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从A变成B,然后又从B变成A,React会认为UI不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟DOM树,但是因为虚拟DOM是内存数据,性能是极高的,而对实际DOM进行操作的仅仅是 Diff部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的DOM元素,而只需要 关心在任意一个数据状态下,整个界面是如何Render的。
- jsx语法:HTML 语言直接写在 JavaScript 语言之中,不加任何引号,这就是 JSX 的语法,它允许 HTML 与 JavaScript 的混写。React不是一个新的模板语言,JSX只是一个表象,没有JSX的React也能工作。jsx语法与javascript并不兼容,需要通过babel-loader来解析。
- 组件化:构建可组合的组件(组件:对数据和方法的简单封装,封装起来的具有独立功能的UI部件),是代码复用、测试和关注分离。React推荐以组件的方式去重新思考UI构成,将UI上每一个功能相对独立的模块定义 成组件,然后将小的组件通过组合或者嵌套的方式构成大的组件,最终完成整体UI的构建。MVC的思想让你做到视图-数据-控制器的分离,那么组件化的思考方式则是带来了UI功能模块之间的分离。
组件的生命周期
组件的生命周期分成三个状态:
-
Mounting:已插入真实 DOM
-
Updating:正在被重新渲染
-
Unmounting:已移出真实 DOM
React 为每个状态都提供了两种处理函数,will 函数在进入状态之前调用,did 函数在进入状态之后调用,三种状态共计五种处理函数。
-
componentWillMount()
-
componentDidMount()
-
componentWillUpdate(object nextProps, object nextState)
-
componentDidUpdate(object prevProps, object prevState)
- componentWillUnmount()
React是基于组件化的开发,那么组件化开发最大的优点是什么?毫无疑问,当然是复用,下面我们来看看React中到底是如何实现组件的复用的,这里我们还写一个例子来说吧,代码如下:
- 事件系统(除了原生DOM事件)
- Ajax功能
- 数据层
- Flux:用于管理数据流,描述单向数据流,只有从模型到视图的数据流动,定义组件之间,组件和数据模型之间如何通信。且包含某些特定的事件和监听器,dispacher和某个js事件库。
- dispacher本质是一个事件系统,全局的分发器,负责广播和注册callback,有且只有一个全局的dispacher。
- Store:可以存在多个Store存储不同数据类型,用于响应dispacher事件,并且进行响应的数据处理,是应用中唯一知道如何更新数据的地方,只有store可以注册callback。View永远不应该调用App.Dispatcher.register,Dispatcher的存在就是为了将消息从View传递到Store,然后Store触发change事件,通过第三方js事件传递到View。
Redux 可以用这三个基本原则来描述:
- 单一数据源
- State 是只读的
- 为了描述 action 如何改变 state tree ,你需要编写 reducers。
Redux 的灵感来源于 Flux 的几个重要特性。和 Flux 一样,Redux 规定,将模型的更新逻辑全部集中于一个特定的层(Flux 里的 store,Redux 里的 reducer)。Flux 和 Redux 都不允许程序直接修改数据,而是用一个叫作 “action” 的普通对象来对更改进行描述。
而不同于 Flux ,Redux 并没有 dispatcher 的概念。原因是它依赖纯函数来替代事件处理器。纯函数构建简单,也不需额外的实体来管理它们。你可以将这点看作这两个框架的差异或细节实现,取决于你怎么看 Flux。Flux 常常被表述为 (state, action) => state
。从这个意义上说,Redux 无疑是 Flux 架构的实现,且得益于纯函数而更为简单。
和 Flux 的另一个重要区别,是 Redux 设想你永远不会变动你的数据。你可以很好地使用普通对象和数组来管理 state ,而不是在多个 reducer 里变动数据。正确且简便的方式是,你应该在 reducer 中返回一个新对象来更新 state, 同时配合 object spread 运算符提案 或一些库,如 Immutable。
Action
首先,让我们来给 action 下个定义。
store.dispatch()
将 action 传到 store。
type
字段来表示将要执行的动作。多数情况下,type
会被定义成字符串常量。当应用规模越来越大时,建议使用单独的模块或文件来存放 action。
除了 type
字段外,action 对象的结构完全由你自己决定。参照 Flux 标准 Action 获取关于如何构造 action 的建议。
index
来引用特定的任务。而实际项目中一般会在新建数据的时候生成唯一的 ID 作为数据的引用标识。
index
就比把整个任务对象传过去要好。
Action 创建函数
Reducer
Action 只是描述了有事情发生了这一事实,并没有指明应用如何更新 state。而这正是 reducer 要做的事情。
设计 State 结构
在 Redux 应用中,所有的 state 都被保存在一个单一对象中。建议在写代码前先想一下这个对象的结构。如何才能以最简的形式把应用的 state 用对象描述出来?
以 todo 应用为例,需要保存两种不同的数据:
- 当前选中的任务过滤条件;
- 完整的任务列表。
Action 处理
之所以称作 reducer 是因为它将被传递给 Array.prototype.reduce(reducer, ?initialValue)
方法。保持 reducer 纯净非常重要。永远不要在 reducer 里做这些操作:
- 修改传入参数;
- 执行有副作用的操作,如 API 请求和路由跳转;
- 调用非纯函数,如
Date.now()
或Math.random()
。
明白了这些之后,就可以开始编写 reducer,并让它来处理之前定义过的 action。
undefined
,此时我们可借机设置并返回应用的初始 state。
注意:
-
不要修改
state
。 使用Object.assign()
新建了一个副本。不能这样使用Object.assign(state, { visibilityFilter: action.filter })
,因为它会改变第一个参数的值。你必须把第一个参数设置为空对象。你也可以开启对ES7提案对象展开运算符的支持, 从而使用{ ...state, ...newState }
达到相同的目的。 - 在
default
情况下返回旧的state
。遇到未知的 action 时,一定要返回旧的state
。
Object.assign
须知
Object.assign()
是 ES6 特性,但多数浏览器并不支持。你要么使用 polyfill,Babel 插件,或者使用其它库如 _.assign()
提供的帮助方法。
switch
和样板代码须知
switch
语句并不是严格意义上的样板代码。Flux 中真实的样板代码是概念性的:更新必须要发送、Store 必须要注册到 Dispatcher、Store 必须是对象(开发同构应用时变得非常复杂)。为了解决这些问题,Redux 放弃了 event emitters(事件发送器),转而使用纯 reducer。
switch
来决定是否使用它。如果你不喜欢
switch
,完全可以自定义一个
createReducer
函数来接收一个事件处理函数列表,参照
"减少样板代码"。
注意每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state
参数都不同,分别对应它管理的那部分 state 数据。
combineReducers()
工具类,combineReducers()
所做的只是生成一个函数,这个函数来调用你的一系列 reducer,每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理,然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象。
Store
在前面的章节中,我们学会了使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。
Store 就是把它们联系到一起的对象。Store 有以下职责:
- 维持应用的 state;
- 提供
getState()
方法获取 state; - 提供
dispatch(action)
方法更新 state; - 通过
subscribe(listener)
注册监听器; - 通过
subscribe(listener)
返回的函数注销监听器。
再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store。
combineReducers()
将多个 reducer 合并成为一个。现在我们将其导入,并传递
createStore()
。
createStore()
的第二个参数是可选的, 用于设置 state 初始状态。这对开发同构应用时非常有用,服务器端 redux 应用的 state 结构可以与客户端保持一致, 那么客户端可以将从网络接收到的服务端 state 直接用于本地数据初始化。