React & JSX 基本思想

React,是一个框架,更是一套思想,更准确的说是一套前端的思想,指引你如何运行先进的技术构造组件化的前端结构(当然更多可能是界面)。

在本章中你将会掌握以下几个重要的思想:

  1. 组件化思想(Component-Based) 与 组件的职责;
  2. JSX是一种语法糖;
  3. 虚拟DOM思想
  4. 组件与元素(Component and Element) ;

1 React 组件化思想(Component-Based)

其实只要你写过UI界面,你就接触过组件化的思想,只不过是有的系统称之为”控件”,有的系统称之为”View”,还有称为”UIControl”,其实都是一回事。一个按钮是一个组件,一个输入框是一个组件,组合起来的一个登录区域也可以是一个组件。

这里我想让大家对组件化思想有一个统一的更彻底的认识。

1.1 一个组件的职责

虽然我说一个按钮就是一个组件,已经很具像了,但是,你要明白的是你知道组件是什么还不够!本章是要让你拥有组件化的思想,所谓思想,就是要让你从内到外、学会用真正的组件思想去思考问题。

  • 组合模式

我们先看一个无关的粟子:大家玩过乐高吧。我来找个图:

这个乐高积木搭起来的房子,就是一个组件,在React中称之为 Component。

我们细看之,这个房子是由数个小积木搭出来的,其中这些小积木也不是个个都不相同,有几类相同的小积木。比如,红色的、蓝色的、黄色的。他们的大小不一样,形状也不一样。而这一个个的小积木,在React中也是 Component。

所以,你现在知道我们将之译为”组件”是多么贴切了!一个界面就是由多个小组件组成的大小组,而其中,”小组件”也可能是由数个更小的”小组件”组成(比如这个房子的窗子,就是由红、绿、蓝等几种原子组件组成)。整个就完全符合软件设计模式中的”组合模式”。 (注意,我们这里把不能再分的最小组件称之为”原子组件”)。

  • 组件的职责

我先把迷底解开吧,在React中(其实写其它系统的UI都是如此),组件一共有两个职责:

1
2
1.决定自己长什么样;
2.决定怎样与用户进行交互;

我们现在把目光聚焦在原子组件上。其实不看图大家也知道乐高的原子组件是怎样的,就是一个方块。然后上半部分会有突出的圆点,可以插入另一个其它组件与之进行”组合”;下半部分是凹进去的圆点,是被另一块积木插的。这一插一被插的部位,我们称之为”接口”。怎样?够形象了吧。在React中也一样,组件是需要有统一的接口的。这样它们才能组合。

我们现在借搭乐高模型来理一理组件的职责:

  1. 决定自己长什么样;一个积木做出来时,它就要知道自己长啥样,比如乐高的原子组件是一个个小方块,便于用户堆砌自己喜欢的形状:如一个窗户要像一个窗户一样,由窗口、窗台等组成;
  2. 决定怎样与用户进行交互;积木的突起和凹进去的槽就是交互的接口。比如一个窗户组件,要在上下四周提供可以插的口来嵌入到房子中一样。

在 React 中,一个组件首先也要知道自己长什么样,需要用哪些组件组合而成,要用什么布局、什么样式。 然后,再考虑要暴露哪些接口(属性或是方法)。

1
2
1.决定自己长什么样,就要用到组合的技术;
2.决定怎样与用户交互,要使用到 props & state 来提供接口。

上面提到的组合的技术与 props & state 技术,后面几节都会介绍到,大家请稍安勿躁~

那么,具体在 React 中怎么定义组件呢? —- 使用 JSX

2 JSX基础

JSX 不是一种新型的语言,只是一个语法糖。

语法糖的百度百科

语法糖,也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

你如果学过C的话,也可以把它理解成宏定义。编译器在编译前就会把事先定义的特殊语法,转换成语言里的标准语法。

2.1 什么是JSX ?

JSX 不是一种新型的语言,只是一个语法糖。从字面上看:JSX = JS + XML, 就是往JavaScript里插 XML 代码。

  • 为什么要这样做?

为什么要往JS里插 XML代码? 其实就是为了组件化。我们来想想硬编码写界面吧,你可能写过一大堆这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
LinearLayout layout = new LinewrLayout();
layout.direction = LinearLayout.horizon;

Button btn1 = new Button("测试");
btn1.marginTop = 10;
layout.add(btn1);

Button btn2 = new Button("测试2");
btn2.marginTop = 5;
layout.add(btn2);

btn1.marginTop = 7; // 后来有条友仔改Bug时想调低Btn1的位置,在这里改了发现调低了就提代码走人。下次你死活不知道为什么btn1的margin不是10!

上面的代码是一段类似 Java的伪代码(因为硬布局我有段时间没写了,只能写个大概的意思),主要意思就是先初始化一个布局的对象,然后新建两个Button加进去,从而让这两个button水平排列。

这样的代码即不好阅读,也不好维护。特别是我在最后加的一行代码,因为代码hardCode界面比较自由,可能会出现这样的代码,你只要定义好组件后可以在任何地方修改之,到时你要找问题起来头会爆掉(比如在写iOS时,很多时我们要手写布局,就是这么麻烦)。。

而使用XML布局,你的代码可能只要这样(下面的代码效果与上面那段伪Java代码是一样的):

1
2
3
4
<LinearLayout direction='horizon'>
<Button text="测试" maringTop=7/>
<Button text="测试2" maringTop=5/>
</LinearLayout>

所以,使用了多种布局方法之后,我觉得使用 XML布局是最直观,写起来也很爽。这段代码很清楚地表示了两个Button装在一个父的Layout容器中。

而与 Android不同的是,React并没有把代码与XML布局分开,而是放在了一起 ,这就变成了 JSX 语法。这样做的好处就是,你的代码更紧凑了。

2.2 JSX 是语法糖

我们说过,JSX把布局与逻辑代码放在一起了。所以,就像我们之前的Demo一样,JSX编写 component 的代码像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Demo extends React.Component {

render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
欢迎来到加油宝!
</Text>
<Text style={styles.welcome}>
你可以在加油宝app里享受更多的加油优惠,以及回报率可观的理财服务 ^_^
</Text>
</View>
);
}
}

上面就是一个 JSX 的基本语法,看起来也是很好理解的。

  1. 定义了一个 Demo组件(继承自 React.Component)。
  2. render 就像是我们以前写MFC或是Android界面时的 reDraw或rePaint函数一样,在这个方法中渲染界面。
  3. 在 render 方法中,我们看到的之前介绍过的 XML 布局写法。

其实很简单也很好理解 ,其实我们只是在需要渲染UI时使用 JSX的语法糖插入了 XML 布局而已,就这样,一个界面就写完了。

我们看看这个界面,就是一个 View当作布局对象,里面放置了两个文本控件,显示了两段话。

我们下面就来分析下JSX作为语法糖,他究竟做了些什么?

3 组件 与 元素(Component and Element)

我们说过,JSX是一门语法糖,那么他究竟做了哪些语法转换的工作呢?其实也就是上面提到的,JavaScript原生是不支持插入 XML的,JSX很明显就是增加了这一部分,在它的编译器工作时,将插入的XML语法转换成为了 javaScript语法。我们下面就来看看它是怎么做到的。我们在了解转化之前,先了解下”虚拟DOM”技术,这个技术告诉你为什么要将组件转化成UI元素。

3.1 虚拟DOM

React有一个概念叫作”虚拟DOM”,这个如果不熟浏览器DOM编程也不会影响你理解大概 。

其实在React里,"组件"只是一个概念、是一个在内存中的数据结构而已,并不是一个具体的可直接展现的本地UI控件。比如上面用到的展示文字的

组件,只是一个数据结构,包含了这类显示文字的UI控件应该有的数据结构。

举个粟子:比如按钮,在React中只是一个抽象的按钮概念,其实一个按钮的定义很简单,无非就是一块矩形区域、区域的颜色、点下去的颜色、文字颜色、点击触发的事件(当然还有很多细节React会帮你考虑)。你把这些都定义好了,就有了一个抽象的按钮结构,这样,无论你把它用在什么系统里(浏览器也好,Android系统也好,iOS也好),它都可以给你画出一个按钮 。 这样的好处之一就是他是UI层的最高抽象,所以React声称自己可以跨平台另外还有个好处层层的虚拟DOM组成一棵树,你不必在每次有组件变化时都重新渲染整棵树,你可以通过计算,只渲染必要的组件就行,当然,这此具体怎么实现这里先不分析,追究太深也会影响我们入门,后面有缘再做些高级课程的话,我们再来细聊。

总之,虚拟DOM技术(在RN中你也可称之为虚拟UI技术)就是,你只要按他的架构写好UI结构,然后渲染时它会自动转化成平台支持的UI组件输出。

现在我们再来解释组件与元素,你就明白了。

  • 组件,就是一个抽象的控件结构,比如按钮Button、文本框Text。
  • 元素,则是一个具像的组件。

比如下面这段就定义了一个组件

1
2
3
4
5
class BigButton extends React.Component {
render(){
.....
}
}

比如下面这段代码就定义了一个Text元素。

1
2
3
<Text style={styles.welcome}>
欢迎来到加油宝!
</Text>

我们发现,定义组件,一段直接用 JS 代码继承 React的 Component类就可以了。但是定义一个元素我们通常使用 JSX语法。

其实正如上面所言,JSX在编译时会将之转化成 js代码。

比如React官网上的这个例子

1
const element = <h1 className="greeting"> Hello, world! </h1>;

标签框着的语句会被转化成

1
2
3
4
5
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);

其中 React.createElement这个方法签名:

1
2
3
4
5
React.createElement(
组件名,
传给组件的Props(还记得我们上面说过的决定组件交互的接口吗),
Children(这个对象一般用于放子组件)
);

createElement 内有一系列工厂方法,帮你做了很多检查、补充细节之类的活,其实就是为了输出一个 element的组件元素的对象,它就是一个抽象的高层UI数据结构了:

1
2
3
4
5
6
7
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};

比如这个 h1 组件,表示一个标题组件元素,它的文本是 “Hello, world”,你只要抽象出这个,在什么系统上只要适配好都可以有一套统一的输出啦~

4 小结

对,本章没有给出一个实际可操作的事例,让爱实践的朋友很不爽~但是,这些基础的思想都是非常重要的,可以说是贯穿你的整个工作阶段,建议好好掌握。

我们来一起回顾一下:

首先,我们了解了什么是组件化的思想。在 React 中可以说一切都是由组件形成的。我们又弄明白了组件与元素的概念,了解了虚拟DOM技术,知道React的UI层其实是一个对UI层的高度抽象(组件)。我们通过一些例子知道了抽象出来的高级UI抽象是什么样的一个数据结构(元素)。然后我们了解了在 React中,使用一个叫 JSX语法糖来构建这套组件系统,那是一种XML的布局方式,而且JSX是采用了直接将XML布局插入到JavaScript这种紧凑的方式。

好了,本章到这里已经差不多了,下章我们可以终于可以动动手了,将本章的理论知识一次性释放个够!