你好!
假设你已经知道了什么是react,并了解它为你解决什么问题。你可能已经用react开发了中小型项目,但想提升自己的技能并得到所有未解决问题的答案。
你应该知道,React由Facebook的开发人员和JavaScript社区中的数百位贡献者维护。
React是最流行的UI开发库之一,因高性能而被人们熟知,这要归功于它巧妙地处理了DOM。
React使用JSX,一种 JavaScript 的语法扩展。 这需要你重新思考关注点分离原则。react有许多很酷的特性,例如服务端渲染,该特性让你可以开发通用应用。
关注点分离原则,也叫正交原则,HTML CSS JS 分离 互不影响
状态转移:js不去修改css 的样式(.style.color='red'),只修改css中的状态(addClass('active'))。
学习本书,你需要知道如何使用终端在你的node,js环境中安装和运行npm。
所有的示例都遵循es2015语法滚翻,以便易于阅读和理解。
在第一章,我们将讲解一些基础概念,这些有助于高效地使用react,对于初学者来说理解这些概念非常重要:
- 命令式编程与声明式编程的区别
- react组件与组件实例,react如何用元素来控制UI流。
- react如何改变构建web应用的方式,主张一种新的关注点分离的概念是什么,它选择不同的设计理念背后的原因是什么。
- 为什么人们对javaScript框架感到疲劳,使用react生态系统时,如何避免开发者常见的错误?
1.1 声明式编程
阅读react文档和博客,你肯定遇到过声明式这一词汇。
事实上,react如此强大的原因之一是它强调了声明式编程规范。
因此,要掌握react,需要理解声明式编程的概念以及它与命令式编程的区别。
最简单的理解方式是命令式编程描述代码如何工作,声明式编程则表明想要达到什么目的。
现实生活中一个有关命令式编程的例子,比如去酒吧喝啤酒,对服务员发出以下指令。
*从架子上取出一个杯子
- 把杯子放在酒瓶前
- 取下瓶塞,将被子倒满。
- 把杯子递给我
在声明式的世界里,你只需说”请给我一杯啤酒“。
按声明式的方法点一杯啤酒,需要假设服务员知道如何提供啤酒,这是声明式编程工作的一个重要方面。
来看一个JavaScript的示例,写一个简单的函数,给定包含大写字符串的数组时,该函数返回包含相同的小写字符串的数组。
toLowerCase(['FOO', 'BAR']) // ['foo', 'bar']
命令式函数按如下方式实现:
const toLowerCase = input => {
const output = []
for (let i = 0; i < input.length; i++) {
output.push(input[i].toLowerCase())
}
return output
}
首先创建一个空数组来保存结果,通过遍历传入的数组,将数组中的每个元素取小写值存入空数组中,返回需要输出的数组。
声明式实现如下所示:
const toLowerCase = input => input.map(
value => value.toLowerCase()
)
输入数组中的元素会传递到map函数,然后map函数会返回包含小写值的新数组。
有一些重要的差别需要注意:前者代码不够优雅,需要更多的努力去理解,后者更简洁,也易于理解,对于大型代码库来说,可维护性是至关重要的。
另一方面,声明式编程不需要使用变量,在执行过程中不持续更新变量的值。事实上,声明式变量更倾向于避免创建和修改状态。
最后一个示例,了解一下react声明式变量。
我们将解决web开发中一个常见的需求:展示带有标记的地图。
JavaScript实现(使用Google Maps SDK),如下示例。
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 4,
center: myLatLng,
})
const marker = new google.maps.Marker({
position: myLatLng,
title: 'Hello World!',
})
marker.setMap(map)
这显然是命令式,因为在代码中描述了创建地图,创建标记,将标记放入地图中的指令。
React组件在一个页面上展示地图的方式,如下示例:
<Gmaps zoom={4} center={myLatLng}>
<Marker position={myLatLng} title="Hello world!" />
</Gmaps>
在声明式编程中,开发者只需要描述想实现什么么不需要列出具体的步骤。
声明式编程方式使得React很容易使用,因此最终的代码也很简单,这样产生的bug也更少,可维护性也更强。
1.2 React元素
假设你已经熟悉组件及其实例,要想高效地使用react,你还需要了解另一个对象——元素。
无论调用createClass方法,还是继承Component,或者声明一个无状态函数,都是在创建组件。React管理着运行环境中的所有组件实例,在某一时刻,内存中可能存在一个组件的多个实例。
如前文所述,React遵循声明式规范,因此不需要告诉她如何与Dom交互。只需要声明希望在屏幕上看到什么,React就会为你完成工作。
你可能已经体会过,大部分UI库是按相反方式工作的:让开发人员负责更新页面,
手动管理DOM元素的创建和销毁。
React使用元素这种特殊类型的对象来控制UI流程。元素描述了屏幕需要显示的内容。这些不可变对象比组件和组建实力要简单的多,只包含了展示界面所必须的信息。
如下是一个元素的例子:
{
type: Title,
props: {
color: 'red',
children: 'Hello, Title!'
}
}
元素最重要的属性是type,和一些其他的属性。另一个比较特殊的属性是children,它是可选的,表示元素的直接后代。
type是重要的,因为它告诉React如何处理元素本身。如果type属性是一个字符串,则表示一个DOM节点,如果是一个函数,则表示一个组件。
DOM元素和组件可以相互嵌套,以表示一个渲染树。
{
type: Title,
props: {
color: 'red',
children: {
type: 'h1',
props: {
children: 'Hello, H1!'
}
}
}
}
当元素的type属性是一个函数时,React会调用它,传入props来取回底层元素。React对返回结果进行回调,直到得到一个完整的DOM节点树,渲染到页面。
这个过程称作一致性比较,React DOM和ReactNative 都用它在各自的平台创建UI。
1.3 舍弃所学
初次使用react需要一个开发的思想,因为它带来了设计web和app应用的新的方式。实际上,React试图打破我们熟知的大部分最佳实践,并革新UI的构建方式。
在过去的二十年,我们学到了关注点分离原则是重要的,我们习惯将其理解为从模板中分离出逻辑。我们的目标始终是将Javascript和HTML写入不同的文件。
不同的模板方案被创建出来帮助开发者实现这个目标。
问题在于,大多数情况下,这种分离仅仅是一种假象。真相是,无论KJavascript和HTML写在什么地方,它们都是紧密耦合的。
我们来看一个模板的示例:
{{#items}}
{{#first}}
<li><strong>{{name}}</strong></li>
{{/first}}
{{#link}}
<li><a href="{{url}}">{{name}}</a></li>
{{/link}}
{{/items}}
以上代码段摘自Mustache官网,它是最流行的模板系统之一。
第一行代码让Mustache循环遍历项目集合。循环内部包含了一些条件逻辑,以检查#first和#link属性是否存在,并根据它们的值渲染不同的HTML片段。变量位于双花括号中。
如果你的应用只显示一部分变量,模板库是一个好的选择,但是操作更复杂的数据结构时,情况就不一样了。
实际上,模板系统及其语言 Domain-Specific Language (DSL)提供了功能自己,他们试图提供一门真正编程语言的功能,而又不用像语言那样完备。
如示例所示,模板高度高度依赖于从逻辑层接收到的数据模型来显示信息。
另一方面,Javascript操作模板渲染出来的DOM元素来更新YUI,尽管他们是从不同的文件加载的。
同样的问题也存在在样式中,它们定义在不同的文件中,但模板引用了样式文件,而且CSS选择器也遵循了文档标记结构,因此,几乎不可能在不影响其他文件的前提下修改某个文件。这就是耦合的定义。
这就是为何传统的关注点分离更像是技术分离,这种做法当然说不上不好,但并没有真正解决问题。
React尝试更进一步,将模板放在其所所位置,即与逻辑在一起。React这么做的理由是,它建议你通过编写名为组件的小型代码快来组织应用。
框架不应该告诉你如何分离关注点,因为每个应用都有自己的方式,只有开发者才能决定如何划分应用的界限。
组件式开发彻底改变了Web应用的开发方式,这也是关注点分离这一经典概念逐渐被更现代化的架构所代替的原因。
React所主张的范式并不新奇,也不是由React的开发人员所发明的,但是React为这个概念的主流化做出了巨大贡献。最重要的是,不同专业水平的开发人员都能够轻松理解这个概念,它也因此流行起来。
React组件的渲染方法如下所示:
render() {
return (
<button style={{ color: 'red' }} onClick={this.handleClick}>
Click me!
</button>
)
}
一开始都会觉得这种语法有些奇怪,不过这只是因为我们还没有习惯而已。
一旦学会,我们就会意识到它的强大之处并理解其潜力。
同时使用JavaScript来编写逻辑和模板不但有助于更好地分离关注点,也赋予我们更强的能力和表达力,这正是构建复杂UI所必需的。
这也是为什么,即使混用Javascript和HTML的做法听起来有些奇怪,也请花5分钟试用React。
学习一门新技术最好的方式是尝试小型项目并观察具体的应用情况。一般来说,如果新技术能带来长远利益,那么正确的做法就是忘掉学过的一切并改变思维模式。
此外,开发React的工程师一直在社区中推广另一个概念:将样式的逻辑也放到组件中。这个概念颇具争议,而且很难被接受。
React的最终目标是将创建组件所用到的每项技术都封装起来,并根据它们的领域和功能进行关注点分离。
以下示例展示了React文档中的一个样式对象:
var divStyle = {
color: 'white',
backgroundImage: 'url(' + imgUrl + ')',
WebkitTransition: 'all', // 注意此处大写的'W'
msTransition: 'all' // 'ms'是唯一小写的厂商前缀
};
ReactDOM.render(
<div style={divStyle}>Hello World!</div>,
mountNode
);
开发者使用JavaScript编写样式的这套方案称作CSS in JavaScript。第7章将对其进行详细介绍。
1.4. 常见误解
有一种普遍的观点认为,React是一个庞大的技术和工具集,要想使用它,必须学会包管理器,转译器,模块打包器和各种不同的库。
这种观念相当普遍,人们口口相传,以至于它有了明确的定义,还被赋予了JavaScript疲劳这一名称。
理解这种观念背后的缘由并不难。实际上,React生态系统中的所有代码仓库和类库都源自炫酷的新技术、最新版的JavaScript以及最先进的技术和规范。
此外,GitHub上有大量的react模板,每一个都有数十个能够解决各种问题的依赖项。
这很容易让人理解成使用react就要使用这些工具,然后事实并非如此。
虽然人们普遍如此认为,但React其实是一个很小的轻量的库,它可以想使用jQuery或者Backnone一样,在关闭Body元素之前以脚本的方式引入到任何页面(甚至是JSFiddle)。
实际需要引用两个脚本,React分为两个包:核心包react,实现了库的核心特性,react-dom,包含了所有浏览器相关的特性。这样做的理由是,核心包可以用于支持不同的目标平台,如浏览器中的React DOM以及移动设备上的React Native。
只需下载发行包并托管到自己的服务器上(或者使用https://unpkg.com),你就可以在短短几分钟内开始使用React及其特性。
要想使用React,可以在HTML代码中添加以下URL:
- https://cdn.bootcss.com/react/15.3.2/react.min.js
- https://cdn.bootcss.com/react/15.3.2/react-dom.min.js
如果只引入核心包,则无法使用JSX语法,因为它不是浏览器支持的标准语言。不过,重点在于先从最小特性集入手,需要时再加入更多功能。
对于简单的UI,只需使用createElement方法。只有当开发更复杂的UI时,才需要引入转译器来启用JSX语法并将其转换成JavaScript。
一旦应用变得更大,就需要使用路由库来处理不同的页面和视图。与上同理,引入路由库即可。
有些情况下,我们需要从API路径加载数据。如果应用继续扩增,就需要利用外部依赖项对复杂操作进行抽象,此时才应该引入包管理器。
然后就需要将应用拆分成独立的模块并按正确的形式组织文件。此时应该开始考虑使用模块打包器。
使用这种非常简单的方法,就不会再感到疲劳了。
如果代码模板带有数百个依赖和数十个npm包,不熟悉它们必然会感到不知所措。
值得注意的是,与编程相关的每种工作(特别是前端工程)都需要持续的学习。Web的本质决定了它要根据用户与开发者的需求快速进化与改变。我们的生态环境从一开始就按这种方式发展,这也正是它如此令人激动的原因。
积累了一些Web开发经验后,我们了解到掌握一切知识是不可能的,但应该用正确的方法学习新知识,以避免疲劳感。只要能跟上所有的新趋势,就不需要为了掌握新类库而实际运用它,除非我们有时间做业余项目。
JavaScript领域很令人吃惊:只要发布或者起草了一个规范,社区中就会有人以转译器插件或polyfill的形式实现它,这使得在浏览器厂商赞成并开始支持该规范前,大家就可以使用它。
这些使得JavaScript和浏览器生态环境与任何其他语言或平台完全不同。
其弊端是一切变化得太快,不过问题也只是如何在押宝新技术和保持稳妥间找到平衡。
任何情况下,Facebook的开发人员都很注重开发体验,并且善于倾听社区反馈。因此,尽管使用React并非一定要学习数百种工具,但他们也意识到了这种令人疲劳的现象,于是发布了一个CLI工具,让构建和运行真正的React应用变得非常简单。
这个CLI工具只需要Node.js/npm环境,然后就可以全局安装:
npm install -g create-react-app
安装好这个可执行程序后,就可以通过文件夹名称来创建应用了。
create-react-app hello-world
最后,执行** cd hello-world **命令进入应用的文件夹,接着运行以下命令:
npm start
应用只靠一项依赖就可以运行了,但包括了用最先进的技术开发完整React应用所需的一切特性。以下截图展示了create-react-app创建的应用的默认页面。
本书将利用这个工具运行每章中的代码示例,你也可以在本书主页中获取它们:http://www.ituring.com.cn/book/2007。
1.5 小结
我们在本章中学习了一些基本概念,它们对于学习后续章节很重要,对于平时开发React应用也至关重要。
现在我们知道了如何编写声明式代码,并清晰地理解了自己开发的组件与React元素的区别,React元素的实例会显示在屏幕上。
我们了解了将逻辑和模板放在一起的原因,以及为什么这种不太流行的决策能为React带来巨大成功。
我们深入探讨了为什么JavaScript生态系统中的人们会普遍感到疲劳,也研究了如何通过迭代的方式来避免这些问题。
最后,我们探讨了新的CLI工具create-react-app。现在是时候动手编写一些真正的代码了。
摘自 图灵程序设计丛书
参考《React Design Patterns and Best Practices》,
网友评论