[React] 02 - Intro: why react and its design pattern

为啥使用React,给我个理由


 

过去css

须要手动更新DOM、费力地记录每个状态;既不具有扩展性,又很难加入新的功能,就算能够,也是有着冒着很大的风险。html

不过,使用这种开发方式很难打造出极佳的用户体验。由于不管每次用户想要作点什么,都须要向服务端发送请求并等待服务端的响应,这会致使用户失去在页面上所积累的状态前端

Reactreact

它引入了一种新的方式来处理浏览器DOM。程序员

在任什么时候间点,React都能以最小的DOM修改来更新整个应用程序。web

React本质上只关心两件事:1). 更新DOM;2). 响应事件。ajax

 

  • 只关心View

每次状态改变时,使用JavaScript从新渲染整个页面会很是慢,这应该归咎于读取和更新DOM的性能问题。React运用一个虚拟的DOM实现了一个很是强大的渲染系统,在React中对DOM只更新不读取。算法

React不处理Ajax、路由和数据存储,也不规定数据组织的方式。它不是一个Model-View-Controller框架。若是非要问它是什么,他就是MVC里的“V”。React的精简容许你将它集成到各类各样的系统中 。编程

  • 工做状态

React以渲染函数为基础。这些函数读入当前的状态,将其转换为目标页面上的一个虚拟表现。redux

只要React被告知状态有变化,他就会从新运行这些函数,计算出页面的一个新的虚拟表现,接着自动把结果转换成必要的DOM更新来反映新的表现。【不须要reset方式的completely update】

这种方式看上去应该比一般的JavaScript方案——按须要更新每个元素——要慢,可是React确实是这么作的:它使用了很是高效的算法,计算出虚拟页面当前版本和新版间的差别,基于这些差别对DOM进行必要的最少更新

公认的性能瓶颈

  1. React赢就赢在了最小化了重绘
  2. 而且避免了没必要要的DOM操做

 

 

背后的思想:一步一步进化到 Redux


 

"React除了可以组件化开发ui,还完彻底全实现了先后端的隔离"。客官,此话怎讲?

Ref: 怎样理顺react,flux,redux这些概念的关系,开发中有必要使用它们吗?

Ref: 理解 React,但不理解 Redux,该如何通俗易懂的理解 Redux?

 

  • 原始社会的问题

1. 反复刷新页面,尤为是内容复杂的页面,对浏览器的渲染性能消耗很大。

2. 由交互产生的不少细腻的前端数据,其实也很难交给后台处理,由于这是咱们无处安放的临时状态

 

  • Ajax技术的出现
正是因为这两个显著缺陷,才致使ajax技术的出现。自从有了ajax通讯和局部页面更新的机制之后,不用担忧页面总体刷新致使的用户体验问题了!
因而前端开发大踏步地进入web2.0时代,大量交互细腻,内容丰富的SPA(single page application)也应运而生。

1. 根据后台数据排版生成页面,如今只是最基本的工做。

2. 当用户进行某种交互操做引发页面状态变化以后,为了不页面总体刷新,咱们须要当心翼翼地将各个相关的页面局部元素拣选出来,作适当修改,再放回原处,动做通常不会太优雅。

 

  • 从上面的例子咱们得出两个经验

1. 根据肯定的交互状态(state),一古脑儿决定页面的呈现(view),这种“单向流”的开发状态对程序员来讲是思惟清晰、比较轻松的;一旦咱们须要不断手动更新view,而且改变state和view的代码还纠缠在一块儿,咱们的心里每每是崩溃的。

2. 为了让前端开发不感到崩溃,就把全部state交给后台来维护,简单粗暴地经过从新加载页面来实现view的更新是不靠谱的,咱们须要找到新的方法,来实现view的自动更新。

 

  • “改变state,让view自动更新”的开发“触发”思想

我脑补着facebook的某个程序员在一个月黑风高的晚上坐在公司电脑前,抿了一口浓浓的咖啡,忽然灵光一现,伴着屏幕上忽明忽暗的幽幽蓝光,在文本编辑器里写下这么一行文字:

可不能够把浏览器里的DOM tree克隆一份完整的镜像到内存,也就是所谓的“virtual DOM”,当页面的state发生变化之后,根据最新的state从新生成一份virtual DOM(至关于在内存里“刷新”整个页面),将它和以前的virtual DOM作比对(diff),而后在浏览器里只渲染被改变的那部份内容,这样浏览器的性能损耗和用户体验不就都不成问题了吗?

而咱们知道在绝大部分网页应用中js引擎的性能和内存彻底没有被充分利用,咱们正好能够火力全开,利用js的这部分性能红利,实现内存中virtual DOM的diff工做,完美!

因而React横空出世!伴随着react的崛起,相似于redux这些专一于管理state的轻量级框架也变得煊赫一时起来。

 

决定页面呈现的state能够经过模块属性(props)从父模块传递到子模块。

这种"树状"分流机制,有点像植物将营养(state)从根部不断运输到细枝末叶的过程。

 

  • 架构的进化

* 传统MVC

1. 前端开发的Model至关于后台数据的镜像或缓存池,它和服务器端MVC中的Model概念一脉相承;

2. View对应页面的呈现,主要指的是和html、css相关的代码,它和服务器端MVC中的View概念也很是相近。

3. 显著的差异来自于controller:在后台应用中,用户和服务器之间的交互是经过http请求实现的,所以后台controller的表达形式是http请求的handler,而且和router(定义网站的url规则)紧密相关; 而前端应用中,用户和网页之间的交互主要是经过操做事件(例如点击鼠标、键盘输入等)实现的,所以前端的controller这里能够简单理解为各类交互事件的handler。

 

* 问题表象

修改Model的Controller代码像一把黄豆同样散落在了各个View组件的内部。

 

* 问题缘由

若是能够用某种方式把这些散落的代码单独收拢到一块儿,是否是就让这可让这张图示恢复秩序呢?好,咱们顺着这个思路想下去。

不是MVC模式错了,而是咱们压根缺乏了一个和用户交互行为有关的action抽象!所以,对model的具体操做才无法从各个view组件中被剥离出来,放到一处。 

 

* Flux思想 - 单向流思想

1. flux与react没有直接的关系,两者是彻底独立的概念。

2. flux不是一个js库,而是一种前端代码的组织思想,好比说 redux库能够认为是一种flux思想的实现

 
server和client是远程通讯的关系,所以为了尽可能 减小通讯耦合,client每一个操做的所有信息都以http请求的形式被归纳成了精简的“做用量”( action)。
请求的url路径约定了用户的 操做意图(固然RESTful概念中,请求的method也能够反映操做意图),
request参数表征了该 “意图”的具体内容
正是基于这个action的抽象,client端的交互操做才能够被 集中转移到server端的controller中作统一响应。

从代码层面而言,flux无非就是一个常见的event dispatcher,

其目的是要将以往MVC中各个View组件内的controller代码片段提取出来放到更加恰当的地方进行集中化管理,并从开发体验上实现了温馨清爽、容易驾驭的“单向流”模式。 

  

  • 怎样实现从view到state的反馈流程

用户在view上的交互行为(好比点击提交按钮等)应当引发state改变的时候,这个流程该怎么处理?


首先,react框架为咱们理顺了 store --> view 的“单向”工做流store是state的容器);

而后,redux框架为咱们理顺了 view --> store 的**“单向”**工做流。

而且,react和redux都以组件化的形式能够将各自负责的功能进行灵活地组装或拆分,最大程度上确保咱们“一次只须要专一于一个局部问题”。具体来讲,分为如下步骤:

    1. 单例store的数据在react中能够经过view组件的属性(props)不断由父模块**“单向”**传递给子模块,造成一个树状分流结构。若是咱们把redux比做整个应用的“心肺” (redux的flux功能像心脏,reducer功能像肺部毛细血管),那么这个过程能够比做心脏(store)将氧分子(数据)经过动脉毛细血管(props)送到各个器官组织(view组件)
    2. 末端的view组件,又能够经过flux机制,将携带交互意图信息的action反馈给store。这个过程有点像将携带代谢产物的“红细胞”(action)经过静脉毛细血管又泵回心脏(store)
    3. action流回到store之后,action以参数的形式又被分流到各个具体的reducer组件中,这些reducer一样构成一个树状的hierarchy。这个过程像静脉血中的红细胞(action)被运输到肺部毛细血管(reducer组件)
    4. 接收到action后,各个child reducer以返回值的形式,将最新的state返回给parent reducer,最终确保整个单例store的全部数据是最新的。这个过程能够比做肺部毛细血管的血液充氧后,又被从新泵回了心脏
    5. 回到步骤1

  

  • 进一步熟悉React并理解Flux思想

从需求出发,看看使用React须要什么:

1. React有props和state:

    props 意味着:父级分发下来的属性;

    state 意味着:组件内部能够自行管理的状态;

    而且整个React没有数据向上回溯的能力,也就是说数据只能单向向下分发,或者自行内部消化。

    理解这个是理解React和Redux的前提


2. 通常构建的React组件内部多是一个完整的应用,它本身工做良好,你能够经过属性做为API控制它。可是更多的时候发现React根本没法让两个组件互相交流,使用对方的数据。

    而后这时候不经过DOM沟通(也就是React体制内)解决的惟一办法就是提高state,将state放到共有的父组件中来管理,再做为props分发回子组件。


3. 子组件改变父组件state的办法只能是经过onClick触发父组件声明好的回调,

    也就是父组件提早声明好函数或方法做为契约描述本身的state将如何变化,

    再将它一样做为属性交给子组件使用。

    这样就出现了一个模式:数据老是单向从顶层向下分发的,可是只有子组件回调在概念上能够回到state顶层影响数据。这样state必定程度上是响应式的。


4. 为了面临全部可能的扩展问题,最容易想到的办法就是全部state集中放到全部组件顶层,而后分发给全部组件。

5. 为了有更好的state管理,就须要一个库来做为更专业的顶层state分发给全部React应用,这就是Redux。

    让咱们回来看看重现上面结构的需求:

        a. 须要回调通知state (等同于回调参数) -> action【发起的通讯请求】
        b. 须要根据回调处理 (等同于父级方法) -> reducer 【对通讯请求刷选处理的过程】
        c. 须要state (等同于总状态) -> store

    对Redux来讲只有这三个要素:
        a. action是纯声明式的数据结构,只提供事件的全部要素,不提供逻辑。
        b. reducer是一个匹配函数,action的发送是全局的:全部的reducer均可以捕捉到并匹配与本身相关与否,相关就拿走action中的要素进行逻辑处理,修改store中的状态,不相关就不对state作处理原样返回。
        c. store负责存储状态并能够被react api回调,发布action.

    固然通常不会直接把两个库拿来用,还有一个binding叫react-redux, 提供一个Provider和connect。不少人其实看懂了redux卡在这里。

        a. Provider是一个普通组件,能够做为顶层app的分发点,它只须要store属性就能够了。它会将state分发给全部被connect的组件,无论它在哪里,被嵌套多少层。
        b. connect是真正的重点,它是一个科里化函数,意思是先接受两个参数(数据绑定mapStateToProps和事件绑定mapDispatchToProps),再接受一个参数(将要绑定的组件自己):

mapStateToProps:构建好Redux系统的时候,它会被自动初始化,可是你的React组件并不知道它的存在,所以你须要分拣出你须要的Redux状态,因此你须要绑定一个函数,它的参数是state,简单返回你关心的几个值。
mapDispatchToProps:声明好的action做为回调,也能够被注入到组件里,就是经过这个函数,它的参数是dispatch,经过redux的辅助方法bindActionCreator绑定全部action以及参数的dispatch,就能够做为属性在组件里面做为函数简单使用了,不须要手动dispatch。这个mapDispatchToProps是可选的,若是不传这个参数redux会简单把dispatch做为属性注入给组件,能够手动当作store.dispatch使用。这也是为何要科里化的缘由。

 
作好以上流程Redux和React就能够工做了。简单地说就是:
1. 顶层分发状态,让React组件被动地渲染。
2. 监听事件,事件有权利回到全部状态顶层影响状态。

 

--------------------------------------------------------------------------------------------------------

接下来看看Redux/React与这个故事的联系:
  • view(React) = 家具的摆放在视觉的效果上
  • store(state) = 每一个家具在空间内的坐标(如:电视的位置是x:10, y: 400)
  • action = 小明分配任务(谁应该干什么)
  • reducer = 具体任务都干些什么(把电视搬到沙发正对面而后靠墙的地方)

 

因此这个过程应该是这样的:

view ---> action ---> reducer ---> store(state) ---> view

若是放入一个web app中,
首先,store(state)决定了view, 【预期效果】
而后,用户与view的交互会产生action, 【根据效果作计划】
接着,这些action会触发reducer于是改变state, 【计划实施】
最后,state的改变又形成了view的变化。 【实施后的新效果】


 

都是函数式编程里的设计模式的东西,只是换了个马甲。

 

先了解下MVP模式[Android Module] 03 - Software Design and Architecture

Presenter的角色就是:从View收集loadData这样的请求,而后交由Model的一个具体对应的函数去执行。

 

 

React 组件 API


 

React 组件 API。咱们将讲解如下7个方法:

    • 设置状态:setState
    • 替换状态:replaceState
    • 设置属性:setProps
    • 替换属性:replaceProps
    • 强制更新:forceUpdate
    • 获取DOM节点:findDOMNode
    • 判断组件挂载状态:isMounted

 

例子:设置状态:setState

<body>
    <div id="message" align="center"></div>

    <script type="text/babel">
      var Counter = React.createClass({
getInitialState:
function () { return { clickCount: 0 }; },
handleClick:
function () { this.setState( function(state) { return {clickCount: state.clickCount + 1}; } ); },
render:
function () { return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>); }
});
ReactDOM.render(
<Counter />, document.getElementById('message') );
</script> </body>

  

 

React 组件生命周期


 

组件的生命周期可分红三个状态

    • Mounting:已插入真实 DOM
    • Updating:正在被从新渲染
    • Unmounting:已移出真实 DOM

 

生命周期的方法

    • componentWillMount 在渲染前调用,在客户端也在服务端。

    • componentDidMount : 在第一次渲染后调用,只在客户端。以后组件已经生成了对应的DOM结构,能够经过this.getDOMNode()来进行访问。 若是你想和其余JavaScript框架一块儿使用,能够在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操做(防止异部操做阻塞UI)。

    • componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

    • shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 能够在你确认不须要更新组件时使用。

    • componentWillUpdate在组件接收到新的props或者state但尚未render时被调用。在初始化时不会被调用。

    • componentDidUpdate 在组件完成更新后当即调用。在初始化时不会被调用。

    • componentWillUnmount在组件从 DOM 中移除的时候马上被调用。

 

 

React 表单与事件


 

1. 在输入框值发生变化时咱们能够更新 state。

var HelloMessage = React.createClass({
getInitialState:
function() { return {value: 'Hello Runoob!'}; },
------------------------------------------------------ handleChange:
function(event) { this.setState({value: event.target.value}); },
------------------------------------------------------ render:
function() { var value = this.state.value; return <div> <input type="text" value={value} onChange={this.handleChange} />   // 监听到value发生了变化,触发handleChange. <h4>{value}</h4> </div>; } });
ReactDOM.render(
<HelloMessage />, document.getElementById('example') );

2. 在子组件上使用表单。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>菜鸟教程 React 实例</title>
    <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script>
    <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script>
    <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script>
  </head>
  <body>
    <div id="example"></div>
<script type="text/babel">

var Content = React.createClass({ render: function() { return <div>   <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} />   <h4>{this.props.myDataProp}</h4> </div>; } });

-- 以上是子组件 --
======================================================================
-- 如下是父组件 --
var HelloMessage = React.createClass({
getInitialState:
function() { return {value: 'Hello Runoob!'};   },
---------------------------------------------------------------------- handleChange:
function(event) { this.setState({value: event.target.value}); },
---------------------------------------------------------------------- render:
function() { var value = this.state.value; return <div>   <Content myDataProp = {value} updateStateProp = {this.handleChange}>
</Content> </div>; } });
ReactDOM.render(
<HelloMessage />, document.getElementById('example') );
</script> </body> </html>

 

 

React 事件


 

1. 经过 onClick 事件来修改数据:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>菜鸟教程 React 实例</title>
    <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script>
    <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script>
    <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script>
  </head>
<body> <div id="example"></div> <script type="text/babel">
------------------------------------------------------------------- var HelloMessage = React.createClass({ getInitialState: function() { return {value: 'Hello Runoob!'}; },
------------------------------------------------------------------- handleChange:
function(event) { this.setState({value: '菜鸟教程'}) },
-------------------------------------------------------------------- render:
function() { var value = this.state.value; return <div> <button onClick={this.handleChange}>点我</button> <h4>{value}</h4> </div>; } }); ReactDOM.render( <HelloMessage />, document.getElementById('example') ); </script> </body>
</html>

2. 从子组件中更新父组件的 state 时,你须要在父组件经过建立事件句柄 (handleChange) ,并做为 prop (updateStateProp) 传递到你的子组件上。

  <body>
<div id="example"></div> <script type="text/babel">
var Content = React.createClass({ render: function() { return <div> <button onClick = {this.props.updateStateProp}>点我</button> <h4>{this.props.myDataProp}</h4> </div> } });
 -- 以上是子组件 --
=======================================================================================
-- 如下是父组件 --
var HelloMessage = React.createClass({
      getInitialState: function() {
        return {value: 'Hello Runoob!'};
      },
--------------------------------------------------------------------------------------- handleChange:
function(event) { this.setState({value: '菜鸟教程'}) },
--------------------------------------------------------------------------------------- render:
function() { var value = this.state.value; return <div> <Content myDataProp = {value} updateStateProp = {this.handleChange}></Content> </div>; } });
ReactDOM.render(
<HelloMessage />, document.getElementById('example') ); </script> </body>
相关文章
相关标签/搜索