为啥使用React,给我个理由


过去

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

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

React

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

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

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

  • 只关心View

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

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

  • 工作状态

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

只要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>

[React] 02 - Intro: why react and its design pattern的更多相关文章

  1. [React] 01 - Intro: javaScript library for building user interfaces

    教学视频: http://www.php.cn/code/8217.html React 教程: http://www.runoob.com/react/react-tutorial.html 本篇是 ...

  2. [React] 03 - Intro: react.js in twelve demos

    Ref: React 入门实例教程 这算什么,react学习例子的十二门徒?哈哈 如何运行别人的react项目? Ref: [React全家桶入门之CODE]项目代码与使用方法 使用git克隆项目到本 ...

  3. react基础学习和react服务端渲染框架next.js踩坑

    说明 React作为Facebook 内部开发 Instagram 的项目中,是一个用来构建用户界面的优秀 JS 库,于 2013 年 5 月开源.作为前端的三大框架之一,React的应用可以说是非常 ...

  4. 【React】- 1、React介绍

    React的开发背景 构建数据不断变化的大型应用 大量DOM操作   <----   自动DOM操作 数据变化 逻辑及其复杂   <----   状态对应内容(自动变化) 特点: - 简单 ...

  5. 【react学习】关于react框架使用的一些细节要点的思考

    ( _(:3 」∠)_给园友们提个建议,无论是API文档还是书籍,一定要多看几遍!特别是隔一段时间后,会有意想不到的收获的)   这篇文章主要是写关于学习react中的一些自己的思考:   1.set ...

  6. React Native 系列(二) -- React入门知识

    前言 本系列是基于React Native版本号0.44.3写的,最初学习React Native的时候,完全没有接触过React和JS,本文的目的是为了给那些JS和React小白提供一个快速入门,让 ...

  7. React-Native(三):React Native是基于React设计的

    React Native是基于React js设计的. 参考:<React 入门实例教程> React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript ...

  8. 1. React介绍 React开发环境搭建 React第一个程序

    什么是 React         React 是 Facebook 发布的 JavaScript 库,以其高性能和独特的设计理念受到了广泛关注. React的开发背景         Faceboo ...

  9. 转载 React.createClass 对决 extends React.Component

    先给出结论,这其实是殊途同归的两种方式.过去我们一般都会使用 React.createClass 方法来创建组件,但基于 ES6 的小小语法糖,我们还可以通过 extends React.Compon ...

随机推荐

  1. javax.crypto.BadPaddingException: Given final block not properly padded解决方案

    解密的时候报错: javax.crypto.BadPaddingException:   Given   final   block   not   properly   padded 该异常是在解密 ...

  2. Java int转string 长度不足左补0

    最近项目中用到这个需求,我试了两个方法都可以实现 方法一:(推荐) String s=String.format("%010d", 123)//123为int类型,0代表前面要补的 ...

  3. StackExchange.Redis 管道 批量 高性能插入数据

    现在用redis来做数据缓存的越来越多了,很多项目都有初始化redis数据的过程,由于初始化的数据比较大,那么该过程越快越好.这里我们以HashSet方法为例, 这里我们推荐用HashEntry[] ...

  4. 从Encoder到Decoder实现Seq2Seq模型

    https://zhuanlan.zhihu.com/p/27608348 更新:感谢@Gang He指出的代码错误.get_batches函数中第15行与第19行,代码已经重新修改,GitHub已更 ...

  5. virtualbox centos安装增强工具和Centos与VirtualBox共享文件夹设置

    VirtualBox 大家都习惯性把它简称为 Vbox ,比 VM 的体积小.开源.速 度快.不过在使用 VirtualBox  在虚拟机中安装 CentOS 之后,不能直接运行安装好 Virtual ...

  6. 天猫魔盒1代TMB100E刷机, 以及右声道无声的问题

    这个是在小米盒子1代之后买的, 当时速度比小米盒子快, 除了遥控器比较软, 电池盖不太对得齐以外, 用起来还不错. 但是时间长了之后总是不停自己升级, 自己安装一些应用, 还删不了, 要知道这个盒子的 ...

  7. Spark初识

    一.简介 1.什么是Spark 官网地址:http://spark.apache.org/ Apache Spark™是用于大规模数据处理的统一分析引擎. 从右侧最后一条新闻看,Spark也用于AI人 ...

  8. GCD 之线程死锁

    GCD 确实好用 ,很强大,相比NSOpretion 无法提供 取消任务的功能. 如此强大的工具用不好可能会出现线程死锁. 如下代码: - (void)viewDidLoad { [super vie ...

  9. Jenkins Post Build网址

    Hudson Post build taskhttps://plugins.jenkins.io/postbuild-taskThis plugin allows the user to execut ...

  10. Ubuntu图形界面环境下启动应该程序:

    1.先说下Ubuntu14.04系统开机紫框的问题: Grub theme:黑色屏幕出现紫色边框 There's a minor typo on the grub theme which produc ...