购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.1 React简介

React框架最早孵化于Facebook内部,Jordan Walke是框架的创始人。React作为内部使用的框架,在2011年的时候用于Facebook的新闻流(newsfeed),并于2012年用在了Instagram项目上。在2013年5月美国的JSConf大会上,Facebook宣布React框架项目开源。

图1-1为GitHub上React的开源项目截图,地址为: https://github.com/facebook/react/

图1-1 GitHub上的React项目

React框架产生的缘由是在当时的技术背景下,前端MVC(Model-View-Controller)框架性能不能满足Facebook项目的性能需求以及扩展需求,所以Jordan Walke索性就自己着手开始写React框架,这种精神值得学习。

在当时Facebook内部极其复杂的项目中,面临的一个问题是,在MVC架构的项目中当Model和View有数据流动时,可能会出现双向的数据流动,那么项目的调试以及维护将变得异常复杂。

React官方也说自己不是一个MVC框架( https://reactjs.org/blog/2013/06/05/why-react.html ),或者说React只专注于MVC框架设计模式中的View层面的实现。

图1-2为React框架的基本结构,清晰明了地描述出了React底层与前端浏览器的沟通机制。

为了大大减少传统前端直接操作DOM的昂贵花费,React使用Virtual DOM(虚拟DOM)进行DOM的更新。

图1-2 React框架结构

React的组件是用户界面的最小元素,与外界的所有交互都通过state和props进行传递。通过这样的组件封装设计,使用声明式的编程方式,使得React的逻辑足够简化,并可以通过模块化开发逐步构建出项目的整体UI。

React框架中还有一个重要的概念是单向数据流,所有的数据流从父节点传递到子节点。假设父节点数据通过props传递到子节点,如果相对父节点(或者说相对顶层)传递的props值改变了,那么其所有的子节点(默认在没有使用shouldComponentUpdate进行优化的情况下)都会进行重新渲染,这样的设计使得组件足够扁平并且也便于维护。

以下的示例代码演示了React框架基本组件定义以及单向数据流的传递。

完整代码在本书配套源码的01-01-02文件夹。

我们在index.js文件中定义React项目的入口,render函数中使用了子组件BodyIndex,并通过props传递了两个参数,id和name,用于从父组件向子组件传递参数,这也是React框架中数据流的传递方式:

 1. /** 
 2.  * 章节: 01-01-02 
 3.  * index.js 定义了 React 项目的入口 
 4.  * FilePath: /01-01-02/index.js 
 5.  * @Parry 
 6.  */  
 7.
 8. var React = require('react');  
 9. var ReactDOM = require('react-dom');  
10. import BodyIndex from './components/bodyindex';  
11. class Index extends React.Component {  
12.
13.   //生命周期函数 componentWillMount,组件即将加载  
14.   componentWillMount(){
15.     console.log("Index - componentWillMount");
16.   }
17.   
18.   //生命周期函数 componentDidMount,组件加载完毕
19.   componentDidMount(){  
20.     console.log("Index - componentDidMount");
21.   }
22.   
23.   //页面表现组件渲染
24.   render() {
25.     return (
26.       <div>
27.         <BodyIndex id={1234567890} name={"IndexPage"}/>
28.       </div>
29.     );
30.   }
31. }
32. 
33. ReactDOM.render(<Index/>, document.getElementById('example'));  

在子组件BodyIndex中定义了state值,并通过setTimeout函数在页面加载5秒后进行state值的修改。页面表现层代码演示了如何读取自身的state值以及读取父组件传递过来的props值:

 1. /** 
 2.  * 章节: 01-01-02 
 3.  * bodyindex.js 定义了一个名为 BodyIndex 的子组件 
 4.  * FilePath: /01-01-02/bodyindex.js 
 5.  * @Parry 
 6.  */  
 7.
 8. import React from 'react';  
 9. export default class BodyIndex extends React.Component {  
10.   constructor() {  
11.     super();  
12.     this.state = {  
13.       username: "Parry"  
14.     };  
15.   }
16.  
17.   render() {  
18.     setTimeout(() => {  
19.       //5秒后更改一下 state  
20.       this.setState({username: "React"});  
21.     }, 5000);  
22.  
23.     return (  
24.       <div>  
25.    
26.         <h1>子组件页面</h1>  
27.  
28.         <h2>当前组件自身的 state</h2>  
29.         <p>username: {this.state.username}</p>  
30.  
31.         <h2>父组件传递过来的参数</h2>  
32.         <p>id: {this.props.id}</p>  
33.         <p>name: {this.props.name}</p>  
34.  
35.       </div>  
36.     )  
37.   }  
38. }  

项目的package.json文件配置和使用的相关框架版本如下所示:

 1.  {
 2.    "name": "01-01-02",  
 3.    "version": "1.0.0",  
 4.    "description": "",  
 5.    "main": "index.js",  
 6.    "scripts": {  
 7.      "test": "echo \"Error: no test specified\" && exit 1"  
 8.    },  
 9.    "author": "",  
10.   "license": "ISC",  
11.   "dependencies": {  
12.     "babel-preset-es2015": "^6.14.0",  
13.     "babel-preset-react": "^6.11.1",  
14.     "babelify": "^7.3.0",  
15.     "react": "^15.3.2",  
16.     "react-dom": "^15.3.2",  
17.     "webpack": "^1.13.2",  
18.     "webpack-dev-server": "^1.16.1"  
19.   }
20. }

在命令行执行webpack-dev-server命令后,浏览器中的运行结果如图1-3所示,并且在5秒后子组件的state定义的username值由Parry变成了React。具体的配置方法及其意义将在后续章节讲解。

你可以直接在本地编写代码运行测试或直接下载本书配套源码运行,运行后,注意此state页面值更新的部分,整个页面没有进行任何的重新刷新加载,而只是进行了局部的更新,其原理详见下一节。

图1-3 代码在浏览器中的执行结果

React框架底层的核心为Virtual DOM,也就是虚拟DOM。本节将介绍它的底层特性,只有理解了React框架底层的本质,才能更好地帮助你理解React框架的前端表现,并为后续章节讨论React Native框架的性能优化进行一定的知识储备。

传统的HTML页面需要更新页面元素,或者说需要更新页面时,都是将整个页面重新加载实现重绘,执行这样的操作不管是从服务器代价还是从用户体验上来说,“代价”都是非常昂贵的。后来,有了AJAX(Asynchronous JavaScript And XML)这样的局部更新技术,实现了页面局部组件的异步更新,不过AJAX在代码的编写、维护、性能以及更新粒度的控制上还是不太完美。

文档对象模型(Document Object Model,DOM),是W3C组织推荐的处理可扩展标志语言的标准编程接口。在HTML网页上,将构成页面(或文档)的对象元素组织在一个树形结构中,用来表示文档中对象的标准模型就称为DOM。

React在框架底层设计了一个虚拟DOM,此虚拟DOM与页面上的真实DOM相互映射,当业务逻辑修改了React组件中的state部分,如上例中,子组件的state值,username由Parry修改成了React,React框架底层的diff算法会通过比较虚拟DOM与真实DOM的差异,找出哪些部分被修改了最终只更新真实DOM与虚拟DOM差异的部分。此计算过程是在内存中进行的,所以React在前端中的高性能表现正是来自于其底层的优良设计。

图1-4展示了React中的虚拟DOM与页面真实DOM之间的关系,其间的差异通过React框架底层的diff算法获取。

图1-4 React虚拟DOM与页面真实DOM

要更加深入地了解React在源码级别的实现原理,可以参考我博客里从React的源码角度对其底层批量更新state策略的分析文章:

http://blog.parryqiu.com/2017/12/19/react_set_state_asynchronously/

http://blog.parryqiu.com/2017/12/29/react-state-in-sourcecode/

http://blog.parryqiu.com/2018/01/04/2018-01-04/

http://blog.parryqiu.com/2018/01/08/2018-01-08/

通过以上对React框架的简介、代码演示以及底层原理的剖析得知,React最大的优势在于更新页面DOM时,对比于之前的前端更新方案,效率会大大提高。其实React并不会在state更改的第一时间就去执行diff算法并立即更新页面DOM,而是将多次操作汇聚成一次批量操作,这样再次大大提升了页面更新重绘的效率。

使用React框架开发,我们不会通过JavaScript代码直接操作前端真实DOM,而是完全通过state以及props的变更引起页面DOM的变更,相对于jQuery等框架那样进行大量的DOM查找与操作要简单、高效得多。

React框架在开源生态下,已经有大量的相关开源框架与组件可供使用,非常适合项目的快速开发。 uEvjSg+mbd3jySjZV3Tn05xahdZ9VJZUMUP9p6pbN6UGZ5x5iZKMl8dc8Yjz1zE4

点击中间区域
呼出菜单
上一章
目录
下一章
×