记录工作中的点点滴滴

说说对JSX的认识

引子

最近几个月做的一个项目,使用了react技术体系,自然而然的用到了JSX。下面就总结一下自己对JSX的认识。

什么是JSX

  1. 即JavaScript XML,一种在React组建内部构建标签的类XML语法。(增强React程序组件的可读性)
  2. JSX可以看作JavaScript的拓展,看起来有点像XML。使用React,可以进行JSX语法到JavaScript的转换。

下面我们来看一下一个简单的例子。
考虑一下这个变量的声明:

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

这个标签语法既不是字符串也不是HTML,这就是JSX。它是JavaScript的一种扩展语法。

JSX小例子

我们先从官网的一个最简单的例子说起,为了让大家能够直接在本地运行,我贴出了完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.24/browser.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
const element = <h1>Hello, world!</h1>;
ReactDOM.render(
element,
document.getElementById('example')
);
</script>
</body>
</html>

大家可以直接粘贴上面代码,保存在本地的一个test.html文件里,双击打开后,在浏览器里输出:
Alt text

我们看到

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

element变量的声明就是用了JSX语法,HTML语言直接写在JavaScript语言之中,不加任何引号。

注意:

  1. script 标签的 type 属性为 text/babel,这是React 独有的 JSX 语法,跟 JavaScript 不兼容。凡是在页面中直接使用 JSX 的地方,都要加上 type="text/babel"
  2. 一共用了三个库: react.js 、react-dom.js 和 browser.min.js ,它们必须首先加载。其中,react.js 是 React 的核心库,react-dom.js 是提供与 DOM 相关的功能, browser.min.js的作用是将 JSX 语法转为 JavaScript 语法。

js构造dom

比如要创建一个dom超链接:

1
<a class="link" href="https://github.com/facebook/react">React<a>

我们在原生DOM中,用js构造dom的方式是这样的:

1
2
3
4
var a = document.createElement('a')
a.setAttribute('class', 'link')
a.setAttribute('href', 'https://github.com/facebook/react')
a.appendChild(document.createTextNode('React'))

这个代码应该是大家比较熟悉的。当你在写代码的时候会不会感觉很繁琐呢,我们可以封装一下:

1
2
3
4
5
6
7
8
//第一个参数为node名
//第二个参数为一个对象,dom属性与事件都以键值对的形式书写
//第三个到第n个为子node,它们将按参数顺序出现,
//在这个例子中只有一个子元素,而且也是文本元素,所以可以直接书写,否则还得React.createElement一下
var a = React.createElement('a', {
className: 'link',
href: 'https://github.com/facebook/react'
}, 'React')

看完这个代码,是不是感觉一下子要简洁的多。
现在有个编译工具,可以让你用html语法来写React.createElement,部署上线前编译回来。你愿意吗?
不管你的答案是什么,但这就是jsx的一半真相。

来看个直接的对比

前面已经回答过,在使用React的时候,可以不使用JSX,大概这样写:

1
2
3
var child1 = React.createElement('li', null, 'First Text Content');
var child2 = React.createElement('li', null, 'Second Text Content');
var root = React.createElement('ul', { className: 'my-list' }, child1, child2);

使用这样的机制,我们完全可以用JavaScript构建完整的界面DOM树,正如我们可以用JavaScript创建真实DOM。但这样的代码可读性并不好,于是React发明了JSX利用我们熟悉的HTML语法来创建虚拟DOM:

1
2
3
4
5
6
var root =(
<ul className="my-list">
<li>First Text Content</li>
<li>Second Text Content</li>
</ul>
);

总结

  1. 这两段代码是完全等价的,后者将XML语法直接加入到JavaScript代码中,让你能够高效的通过代码而不是模板来定义界面。
  2. 之后JSX通过翻译器转换到纯JavaScript再由浏览器执行。

注意

在实际开发中,JSX在产品打包阶段都已经编译成纯JavaScript,JSX的语法不会带来任何性能影响。
另外,由于JSX只是一种语法,因此JavaScript的关键字class, for等也不能出现在XML中,而要如例子中所示,使用className, htmlFor代替,这和原生DOM在JavaScript中的创建也是一致的。

相信大家在看完了上面的这些举例后,心中的疑问自然而然就迎刃而解了。

因此,JSX本身并不是什么高深的技术,可以说只是一个比较高级但很直观的语法糖。它非常有用,却不是一个必需品,没有JSX的React也可以正常工作:只要你乐意用JavaScript代码去创建这些虚拟DOM元素。

为什么使用JSX

抛出疑问

看了上面的这些简单的demo,大家肯定会抛出这样的疑问:

  1. 为什么React官方推荐使用JSX呢?
  2. 等等。。。

使用React,不一定非要使用JSX语法,可以使用原生的JS进行开发。
但是React作者强烈建议我们使用JSX,因为:

  1. JSX在定义类似HTML这种树形结构时,十分的简单明了。
  2. 简明的代码结构更利于开发和维护。
  3. XML有着开闭标签,在构建复杂的树形结构时,比函数调用和对象字面量更易读。

可能说这些你会感觉比较模糊,下面来举几个看得见的例子。

前端界面的最基本功能在于展现数据,为此大多数框架都使用了模板引擎,

在AngularJS中:

1
2
3
4
5
6
<div ng-if="person != null">
Welcome back, <b>{{person.firstName}} {{person.lastName}}</b>!
</div>
<div ng-if="person == null">
Please log in.
</div>

在EmberJS中:

1
2
3
4
5
{{#if person}}
Welcome back, <b>{{person.firstName}} {{person.lastName}}</b>!
{{else}}
Please log in.
{{/if}}

总结

  1. 模板可以直观的定义UI来展现Model中的数据,你不必手动的去拼出一个很长的HTML字符串,几乎每种框架都有自己的模板引擎。
  2. 传统MVC框架强调界面展示逻辑和业务逻辑的分离,因此为了应对复杂的展示逻辑需求,这些模板引擎几乎都不可避免的需要发展成一门独立的语言。
  3. 如上面代码所示,每个框架都有自己的模板语言语法。而这无疑增加了框架的门槛和复杂度。

使用JSX
正因为如此,React直接放弃了模板而发明了JSX。看上去很像模板语言,但其本质是通过代码来构建界面,这使得我们不再需要掌握一门新的语言就可以直观的去定义用户界面:掌握了JavaScript就已经掌握了JSX。

这里不妨再引用之前文章举过的例子,在展示一个列表时,模板语言通常提供名为Repeat的语法,例如在Angular中:

1
2
3
4
5
6
<ul class="unstyled">
<li ng-repeat="todo in todoList.todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>

而使用JSX,则代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var lis = this.todoList.todos.map(function (todo) {
return (
<li>
<input type="checkbox" checked={todo.done}>
<span className={'done-' + todo.done}>{todo.text}</span>
</li>
);
});
var ul = (
<ul class="unstyled">
{lis}
</ul>
);

可以看到,JSX完美利用了JavaScript自带的语法和特性,我们只要记住HTML只是代码创建DOM的一种语法形式,就很容易理解JSX。
而这种使用代码构建界面的方式,完全消除了业务逻辑和界面元素之间的隔阂,让代码更加直观和易于维护。

JSX的语法

你可以用 花括号 把任意的 JavaScript 表达式 嵌入到 JSX 中。
例如,2 + 2, user.firstName, 和 formatName(user),这些都是可用的表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
<h1>
Hello, {formatName(user)}!
</h1>
);
ReactDOM.render(
element,
document.getElementById('root')
);

JSX 也是一个表达式

编译之后,JSX 表达式就变成了常规的 JavaScript 对象。

这意味着你可以在 if 语句或者是 for 循环中使用 JSX,用它给变量赋值,当做参数接收,或者作为函数的返回值。

1
2
3
4
5
6
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}

用 JSX 指定属性值

您可以使用双引号来指定字符串字面量作为属性值:

1
const element = <div tabIndex="0"></div>;

您也可以用花括号嵌入一个 JavaScript 表达式作为属性值:

1
const element = <img src={user.avatarUrl}></img>;

注意

在属性中嵌入 JavaScript 表达式时,不要使用引号来包裹大括号。否则,JSX 将该属性视为字符串字面量而不是表达式。
对于字符串值你应该使用引号,对于表达式你应该使用大括号,但两者不能同时用于同一属性。

用 JSX 指定子元素

如果是空标签,您应该像 XML 一样,使用 />立即闭合它:

1
const element = <img src={user.avatarUrl} />;

JSX 标签可能包含子元素:

1
2
3
4
5
6
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);

JSX 防止注入攻击

在JSX中嵌入用户输入是安全的:

1
2
3
const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;

默认情况下, 在渲染之前, React DOM 会格式化(escapes) JSX中的所有值。
从而保证用户无法注入任何应用之外的代码。
在被渲染之前,所有的数据都被转义成为了字符串处理。 以避免 XSS(跨站脚本) 攻击。

JSX 表示对象

Babel 将JSX编译成 React.createElement() 调用。

下面的两个例子是是完全相同的:

1
2
3
4
5
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);

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

React.createElement() 会执行一些检查来帮助你编写没有bug的代码,但基本上它会创建一个如下所示的对象:

1
2
3
4
5
6
7
8
// 注意: 这是简化的结构
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};

结尾

关于JSX的介绍大概讲到这里,看完这篇文章后,希望大家能能够了解什么是JSX,React为什么推荐使用JSX等问题。
在下一节中来探索如何将 React 元素渲染到 DOM 上。

参考链接

  1. https://facebook.github.io/react/docs/introducing-jsx.html
  2. http://www.css88.com/react/docs/introducing-jsx.html
  3. http://www.infoq.com/cn/articles/react-jsx-and-component