针对react和redux和mobx-react的概念梳理
主题:
本次培训主要侧重于思想,具体的实现方式各自参照官网,都有详尽的描述
设计思想上的理解可能相对更需要讲解一下,所以本次培训与之前的vue不同,侧重于概念引导
目标:
- 理解spa设计理念
- 理解react设计理念
- 理解redux设计理念
- 理解mobx设计理念
目标人群:
- 有一定的MVVM知识基础
- 上手过MVVM的项目,理解各种情况的下的通信方式
技术文档:
1. SPA(Single Page APP)
介绍react之前,先要介绍一下SPA。
- 什么是SPA?
顾名思义,单页应用。
概念上区别于传统的jsp,页面的刷新根据服务器的响应内容构造,本质上是不同的html文件的交替展示。
单页应用的渲染只会在一个html中不断更新DOM树。 - 为什么使用SPA?
- 前后端分离的最优解。
以RESTFUL API和AJAX为桥梁,将前后端的关注点彻底剥离开
后端工程师不需要再操心数据如何嵌套入页面,专心投入到业务逻辑中去。
前段工程师不需要再操心数据要如何取得以及保存,专心投入到用户交互中去。
技术栈的剥离也轻松完成
2. 服务器压力减小。
> 服务器不需要再操心页面的构建,返回内容中不会再有大量的html文本,极大增加吞吐能力。
3. 提高可复用率。
> 不再需要像过往一样,为PC,手机,平板单独进行开发,一套API甚至一套前段代码可以胜任各种场景。
- 前后端分离的最优解。
2. JSX
-
关注点分离:
区别于angular的html、css、ts的语法分离解耦方式
jsx以组件为单位,将构成组件所关注的内容揉在一个jsx文件内。
此举有利有弊
利在于可以更直观方便得清楚此组件的工作方式
弊在于大量代码存在时结构不太分明helloWorld.jsx:
// 常量定义(ts) const name = 'Josh Perez'; // render函数内定义标签内容(html) public render(): JSX.Element { return ( <h1>Hello, {name}</h1>; ); }
3. REACT:
-
组件
- 页面的最小组成单位,可以将其理解为一块积木。
一个完整的页面是由无数个积木组成,而根据需求可以将积木随意搭配组合,进而构建出不同的页面。
+ 低耦合的可复用化单位
> 在SPA应用编程中,组件化的概念必须深入人心,这样才能做出结构清晰,逻辑干净的工程
- 页面的最小组成单位,可以将其理解为一块积木。
-
props
组件接收参数的单位。概念等同于angular的@Input
// Clock组件 class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date}.</h2> </div> ); } } // App组件 class App extends React.Component { render() { return ( <Clock date="2019-01-01" /> ); } }
-
state:
区别于props,state是组件的私有状态,不与父组件产生交互
-
渲染表达式:
react的风格与angular、vue不同,渲染标签的逻辑更像传统的jsp。
class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> // 类比*ngIf if (this.props.showDate) { <h2>It is {this.props.date}.</h2> } <ul> // 类比*ngFor {props.list.map((post) => <li key={post.id}> {post.title} </li> )} </ul> </div> ); } }
-
关于状态提升以及单向数据流:
-
在react的世界里,数据是自上而下流动的,即单向数据流
-
设计理念
这里先来看一下react组件的构造思想,每一个组件可以认为是一个函数,由更大的函数对其进行调用,传参,操作其返回值
还是这个例子// Clock组件 将其理解为函数定义 function clock(date) class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date}.</h2> </div> ); } } // App组件 class App extends React.Component { render() { return ( // 此处理解为函数调用 clock(date),返回值为一个div <Clock date="2019-01-01" /> ); } }
为什么要讲函数设计呢?
纯函数与非纯函数的区别,就是非纯函数可能会产生副作用,即对传入的参数进行了修改。
而react的设计理念中,一个组件就是一个纯函数,他接收参数,但不会对其进行修改。// 纯函数 // 对于相同的参数总是返回相同的结果 function getSquare(x) { return x * x; } // 非纯函数 function getSquare(items) { var len = items.length; for (var i = 0; i < len; i++) { items[i] = items[i] * items[i]; } return items; }
-
实现方式
假如需求变为一个组件可以显示时间,并且可以修改
此处相当于将angular的双向绑定语法糖拆散// App组件 class App extends React.Component { handleChange(e) { this.setState({date: e.target.value}); } render() { return ( <Clock date="2019-01-01" // 将数据及数据变化后应该做什么告知子组件 onDateChange={this.handleChange} /> <Time date={this.state.date}> ); } } class Clock extends React.Component { // 外部数据变化时,调用外部指示的方法 handleChange(e) { this.props.onDateChange(e.target.value); } render() { return ( <div> <h1>Hello, world!</h1> <input value={this.props.date} onChange={this.handleChange} /> </div> ); } }
-
设计优势
- 所有状态的变化可追溯
父组件维护了一个状态,假设子组件可随意更改父组件甚至祖宗组件的状态
那各组件的状态改变就会变得难以追溯,父组件的状态也可能被子组件意外修改而不可察觉。
而单向数据流的设计会严格掌控状态的变化,所有的状态变化都是由编码者所控制的。
这样的程序在工程壮大后可维护性会显著增强实现方式很多,比如:
- 调用父组件传进来的方法
- 在子组件中dispatch一个action来对全局状态修改
- 全局状态在通过props分发给子组件
- 子组件推送一个事件,订阅方捕获变化
-
-
状态提升
当APP组件中存在以下两个组件:
【1】可以修改并展示时间的组件
【2】【1】修改后的时间format处理后展示
这时【1】组件的状态变化后,【2】组件必须也能捕捉到,进而进行format展示
所以状态需要保存在APP组件内,由APP控制业务流的流转。
详细介绍可以参照官网:状态提升
-
-
关于双向绑定:
angular的双向绑定本质是value + onChange的语法糖。
react没有这样的语法糖,严格将状态变化的控制交到程序员手中
参照5-2(单向数据流-实现方式) -
进阶:
设计理念理解之后,就可以参照官网进行进阶学习了。
react官方文档
4.REDUX
-
先上一个思考题,有如下APP:
E需要实时渲染D中变化的值
// A <div> <B/> <E/> </div> // B <C/> // C <D/> // D <input value="name"/> // E <p>name</p>
根据单向数据流以及状态提升的规则,需要在A组件中定义“name”,以及“onNameChange”回调
然后一路传给B、C、D。
D中发生变化时,再将状态变化事件一路回传给A
A接收到变化后,再将变化后的值交给E去渲染。很显然,这个数据流的传递又臭又长,可能会让你怀疑自己的工作是不是太弱智了些
于是,REDUX诞生了 -
REDUX干了些什么?
redux创造了APP的单一数据源概念,以依赖注入概念的实现方式,将组件的耦合性彻底打散
redux相对于react来说,可以理解为全局状态,独立于组件树之外。
使用方式参照官网REDUX-
redux的解决方案
假设在redux中定义一个变量name,D需要表示并修改name
所以redux将name作为prop传递给D,将onNameChange作为action分发给D
当name发生变化时,D来触发action,告知redux更新nameE需要表示name,所以redux将name作为prop传递给E,当全局状态发生变化时,自动更新传递给E的name
-
redux的优势
经过redux的洗礼,与name状态无关的A、B、C组件完全没有意识到name的存在
他们可以更为专注得去完成自己的任务,而不用操心其他的数据传递任务
从敲出来的代码,到设计上的思想,都彻底得完成了去耦 -
redux的劣势
redux的设计为纯函数式,每一个小状态的变化会重新返回一个新的,完整的全局状态。
当状态无比庞大时,一次更新的消耗是巨大且负担不起的
所以当你的工程会有庞大数据量的时候,选择redux请三思
那么当数据量无比庞大时,就没有办法了吗?
-
5.MOBX-REACT
- mobx的解决方案
当数据量很大时,redux的全局状态深度拷贝工作变得非常臃肿,内存及CPU资源会被压榨得喘不过气
于是mobx做出了面向对象的设计,将状态的变化锁定至对象中的某个属性,
一次变化只会针对全局状态中的某个小属性进行修正,资源问题得以解决。
具体使用方式,参照官网mobx-react