前言
时下虽然接入 JSX 语法的框架(React、Vue)越来越多,但与之缘分最深的毫无疑问仍然是 React。2013 年,当 React 带着 JSX 横空出世时,社区曾对 JSX 有过不少的争议,但如今,越来越多的人面对 JSX 都要说上一句“真香”!典型的“真香”系列。
JSX 是什么?
按照 React 官方的解释,JSX 是一个 JavaScript 的语法扩展,类似于模板语法,或者说是一个类似于 XML 的 ECMAScript 语法扩展,并且具备 JavaScript 的全部功能。
这段解释可抽离两个关键点:
- 「JavaScript 语法扩展」
- 「具备JavaScript 的全部功能」
JSX 的定位是 JavaScript 的「语法扩展」,而不是“某个版本”,这就决定了浏览器并不会像天然支持 JavaScript 一样支持 JSX 。这就引出了一个问题 “JSX 是如何在 JavaScript 中生效的?”
JSX 语法是如何在 JavaScript 中生效的?
React
在 React 框架中,JSX 的语法是如何在 JavaScript 中生效的呢?React 官网给出的解释是,JSX 会被编译为 React.createElement(), React.createElement() 将返回一个叫作“React Element”的 JS 对象。
对于 JSX 的编译是由 Babel 来完成的。
Babel 是一个工具链,主要用于将采用 ECMAScript 2015 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
当然 Babel 也具备将 JSX 转换为 JS 的能力,看一个例子:左边是我们 React 开发中写到的语法,并且包含了一段 JSX 代码。经过 Babel 转换之后,就全部变成了 JS 代码。
其实如果仔细看,发现 JSX 更像是一种语法糖,通过类似模板语法的描述方式,描述函数对象。其实在 React 中并不会强制使用 JSX 语法,我们也可以使用 React.createElement 函数,例如使用 React.createElement 函数写这样一段代码。
代码语言:javascript复制class Test extends React.Component {
render() {
return React.createElement(
"div",
null,
React.createElement(
"div",
null,
"Hello, ",
this.props.test
),
React.createElement("div", null, "Today is a fine day.")
);
}
}
ReactDOM.render(
React.createElement(Test, {
test: "baixiaobai"
}),
document.getElementById("root")
);
在采用 JSX 之后,这段代码会这样写:
代码语言:javascript复制class Test extends React.Component {
render() {
return (
<div>
<div>Hello, {this.props.test}</div>
<div>Today is a fine day.</div>
</div>
);
}
}
ReactDOM.render(
<Test test="baixiaobai" />,
document.getElementById('root')
);
通过对比发现,在实际功能效果一致的前提下,JSX 代码层次分明、嵌套关系清晰;而 React.createElement 代码则给人一种非常混乱的“杂糅感”,这样的代码不仅读起来不友好,写起来也费劲。
JSX 语法写出来的代码更为的简洁,而且代码结构层次更加的清晰。
JSX 语法糖允许我们开发人员像写 HTML 一样来写我们的 JS 代码。在降低学习成本的同时还提升了我们的研发效率和研发体验。
Vue
当然在 Vue 框架中也不例外的可以使用 JSX 语法,虽然 Vue 默认推荐的还是模板。
为什么默认推荐的模板语法,引用一段 Vue 官网的原话如下:
任何合乎规范的 HTML 都是合法的 Vue 模板,这也带来了一些特有的优势:
- 对于很多习惯了 HTML 的开发者来说,模板比起 JSX 读写起来更自然。这里当然有主观偏好的成分,但如果这种区别会导致开发效率的提升,那么它就有客观的价值存在。
- 基于 HTML 的模板使得将已有的应用逐步迁移到 Vue 更为容易。
- 这也使得设计师和新人开发者更容易理解和参与到项目中。
- 你甚至可以使用其他模板预处理器,比如 Pug 来书写 Vue 的模板。
有些开发者认为模板意味着需要学习额外的 DSL (Domain-Specific Language 领域特定语言) 才能进行开发——我们认为这种区别是比较肤浅的。首先,JSX 并不是没有学习成本的——它是基于 JS 之上的一套额外语法。同时,正如同熟悉 JS 的人学习 JSX 会很容易一样,熟悉 HTML 的人学习 Vue 的模板语法也是很容易的。最后,DSL 的存在使得我们可以让开发者用更少的代码做更多的事,比如 v-on 的各种修饰符,在 JSX 中实现对应的功能会需要多得多的代码。
更抽象一点来看,我们可以把组件区分为两类:一类是偏视图表现的 (presentational),一类则是偏逻辑的 (logical)。我们推荐在前者中使用模板,在后者中使用 JSX 或渲染函数。这两类组件的比例会根据应用类型的不同有所变化,但整体来说我们发现表现类的组件远远多于逻辑类组件。
例如有这样一段模板语法。
代码语言:javascript复制<anchored-heading :level="1">
<span>Hello</span> world!
</anchored-heading>
使用 JSX 语法会写成这样。
代码语言:javascript复制render: function (h) {
return (
<AnchoredHeading level={1}>
<span>Hello</span> world!
</AnchoredHeading>
)
}
转换为 createElement 转换的 JS 就变成了这样。
代码语言:javascript复制createElement(
'anchored-heading', {
props: {
level: 1
}
}, [
createElement('span', 'Hello'),
' world!'
]
);
但是不管是模板语法还是 JSX 语法,都不会得到浏览器纯天然的支持,这些语法最后都会被编译成相应的 h 函数(createElement函数,不泛指所有版本,在不同版本有差异)最后变成 JS 对象,这里的编译也是和 React 一样使用的 Babel 插件来完成的。
不管是 React 推崇的 JSX 语法,还是 Vue 默认的模板语法,目的都是为了让我们写出来的代码更为的简洁,而且代码接口层次更加的清晰。在降低学习成本的同时还提升了我们的研发效率和研发体验。
读到这里,相信你已经充分理解了“JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,并且具备 JavaScript 的全部功能。 ”这一定义背后的深意。
不管是 React 还是 Vue 我们都提到了一个函数 createElement,这个函数就是将我们的 JSX 映射为 DOM的。