题注:阿里巴巴南京研发中心成立一年来业务与团队迅速扩展,诚求前端/后端等各路英才。欢迎关注 [阿里南京技术专刊] (https://zhuanlan.zhihu.com/ali-nanjing),也欢迎投递简历,发送简历到 zixiong.zzx@alibaba-inc.com, 诚邀各路大佬前来指教。

JSX

我们在上文中已经很多次的提及了 JSX,大家也对于基本的基于 JSX 编写 React 组件所有了解。实际上在 JSX 推出之初饱受非议,很多人觉得其很怪异。的确虽然与正统的 HTML 相比其都是类 XML语法的声明式标签语言,但是其对于类名强制使用 className、强制要求标签闭合等特点会让不少的传统前端开发者不太适应。JSX 的引入对笔者之前的工作流的冲击在于不能够直接使用 UI 部门提供的页面模板,并且因为组件化的分割与预编译,UI 比较麻烦地直接在浏览器开发工具中调整CSS样式然后保存到源代码中。JSX 本质上还是属于 JavaScript,这就避免了我们重复地学习不同框架或库中的指令约定,而可以直接使用 JavaScript 来描述模板渲染逻辑;而在前端框架的工作流中,往往将 JSX 的转化工作交托于 Babel 等转化工具,我们可以通过如下方式指定 JSX 使用的构建函数:

/** @jsx h */

JSX 的前世今生

JSX 语言的名字最早出现在游戏厂商 DeNA,不过其偏重于加入增强语法使得JavaScript 变得更快、更安全、更简单。而 React 则是依赖于 ECMAScript 语法本身,并没有添加扩充语义。React 引入 JSX 主要是为了方便 View 层组件化,承载了构建 HTML 结构化页面的职责。这一点与其他很多的 JavaScript 模板语言异曲同工,不过React 将 JSX 映射为虚拟元素,并且通过创建与更新虚拟元素来管理整个 Virtual DOM 系统。譬如我们 JSX 语法声明某个虚拟组件时,会被转化为React.createElement(component,props,...children) 函数调用,譬如我们定义了某个MyButton:

// 必须要在 JSX 声明文件中引入 React

import React from 'react';

Click Me

会被编译为:

React.createElement(

MyButton,

{color: 'blue', shadowSize: 2},

'Click Me'

)

而如果我们直接声明某个DOM元素,同样会转化为createElement函数调用:

React.createElement(

'div',

{className: 'sidebar'},

null

)

实际上除了最著名的 Babel JSX 转换器之外,我们还可以使用 JSXDOM 与 Mercury JSX 这两个同样的可以将 JSX 语法转化为 DOM 或者 Virtual DOM。在 JSXDOM 中,只支持使用 DOM 元素,允许在 DOM 标签中直接使用 JavaScript 变量,譬如当我们需要声明某个列表时,可以使用如下语法:

/** @jsx JSXDOM */

var defaultValue = "Fill me ...";

document.body.appendChild(

Click Me!

{['un', 'deux', 'trois'].map(function(number) {

return

{number};

})}

);

这里我们还想讨论另一个问题,为什么需要引入 JSX。在 ECAMScript 6 的 ECMA-262 标准中引入了所谓的模板字符串(Template Literals),即可以在 ECMAScript 中使用内嵌的 DSL 来引入 JavaScript 变量,不过虽然模板字符串对于较长的嵌入式 DSL 作用极佳,但是对于需要引入大量作用域中的 ECMAScript 表达式会造成大量的噪音副作用,譬如如果我们要声明某个评论框布局,使用 JSX 的方式如下:

// JSX

var box =

{

shouldShowAnswer(user) ?

no :

Text Content

}

;

而使用模板字符串的方式如下:

// Template Literals

var box = jsx`

${

shouldShowAnswer(user) ?

jsx`no${Answer}>` :

jsx`

Text Content

${Box.Comment}>

`

}

${Box}>

`;

其主要缺陷在于因为存在变量的嵌套,需要在作用域中进进出出,很容易造成语法错误,因此还是 JSX 语法为佳。

JSX 语法

JSX 的官方定义是类 XML 语法的 ECMAscript 扩展,完美地利用了 JavaScript 自带的语法和特性,并使用大家熟悉的 HTML 语法来创建虚拟元素。JSX 基本语法基本被 XML 囊括了,但也有很多的不同之处。React 在定义标签时,标签一定要闭合,否则无法编译通过。这一点与标准的 HTML 差别很大,HTML 在浏览器渲染时会自动进行补全,而强大的 JSX 报错机制则直接在编译阶段就以报错的方式指明出来。HTML 中自闭合的标签(如 )在 JSX 中也遵循同样规则,自定义标签可以根据是否有子组件或文本来决定闭合方式。另外 DOCTYPE 头也是一个非常特殊的标志,一般会在使用 React 作为服务端渲染时用到。在 HTML 中,DOCTYPE 是没有闭合的,也就是说我们无法直接渲染它。常见的做法是构造一个保存 HTML 的变量,将 DOCTYPE 与整个 HTML 标签渲染后的结果串联起来。使用JSX声明组件时,最外层的组件根元素只允许使用单一根元素。这一点我们在上文中也陈述过,因为 JSX 语法会被转化为 React.createElement(component,props,...children) 调用,而该函数的第一个参数只允许传入单元素,而不允许传入多元素。

变量使用注释

在 HTML 中,我们会使用 进行注释,不过 JSX 中并不支持:

render() {

return (

)

}

我们需要以 JavaScript 中块注释的方式进行注释:

{/* A JSX comment */}

{/*

Multi

line

comment

*/}数组

JSX 允许使用任意的变量,因此如果我们需要使用数组进行循环元素渲染时,直接使用 map、reduce、filter 等方法即可:

function NumberList(props) {

const numbers = props.numbers;

return (

{numbers.map((number) =>

value={number} />

)}

);

}条件渲染

在JSX中我们不能再使用传统的if/else条件判断语法,但是可以使用更为简洁明了的Conditional Operator运算符,譬如我们要进行if操作:

{condition && 为真时进行渲染 }

如果要进行非操作:

{condition || 为假时进行渲染 }

我们也可以使用常见的三元操作符进行判断:

{condition

? 为真时进行渲染

: 为假时进行渲染

}

如果对于较大的代码块,建议是进行换行以提升代码可读性:

{condition ? (

为假时进行渲染

) : (

为假时进行渲染

)}

元素属性style 属性

JSX 中的 style 并没有跟 HTML 一样接收某个 CSS 字符串,而是接收某个使用 camelCase 风格属性的 JavaScript 对象,这一点倒是和DOM 对象的 style 属性一致。譬如:

const divStyle = {

color: 'blue',

backgroundImage: 'url(' + imgUrl + ')',

};

function HelloWorldComponent() {

return

Hello World!

;

}

注意,内联样式并不能自动添加前缀,这也是笔者不太喜欢使用 CSS-in-JS 这种形式设置样式的的原因。为了支持旧版本浏览器,需要提供相关的前缀:

const divStyle = {

WebkitTransition: 'all', // note the capital 'W' here

msTransition: 'all' // 'ms' is the only lowercase vendor prefix

};

function ComponentWithTransition() {

return

This should work cross-browser

;

}className

React 中是使用 className 来声明 CSS 类名,这一点对于所有的 DOM 与 SVG 元素都起作用。不过如果你是将 React 与 Web Components 结合使用,也是可以使用 class 属性的。htmlFor

因为 for 是JavaScript中的保留关键字,因此 React 元素是使用 htmlFor 作为替代。Boolean 系列属性

HTML 表单元素中我们经常会使用 disabled、required、checked 与 readOnly 等 Boolean 值性质的书,缺省的属性值会导致 JSX 认为 bool 值设为 true。当我们需要传入 false 时,必须要使用属性表达式。譬如 可以简写为,而 即不可以省略 checked 属性。自定义属性

如果在 JSX 中向 DOM 元素中传入自定义属性,React 是会自动忽略的:

不过如果要使用HTML标准的自定义属性,即以 data-* 或者 aria-* 形式的属性是支持的。

子元素

JSX 表达式中允许在一对开放标签或者闭合标签之间包含内容,这即是所谓的子元素,本部分介绍 JSX 支持的不同类别的子元素使用方式。字符串

我们可以将字符串放置在一对开放与闭合的标签之间,此时所谓的 props.children 即就是字符串类型;譬如:

Hello World!

就是合法的 JSX 声明,此时 MyComponent 中的 props.children 值就是字符串 Hello World!;另外需要注意的是,JSX 会自动移除行首与行末的空格,并且移除空行,因此下面的三种声明方式渲染的结果是一致的:

Hello World

Hello World

Hello

World

Hello World

JSX 嵌套我们可以嵌套地使用 JSX,即将某些 JSX 元素作为子元素,从而允许我们方便地展示嵌套组件:

我们可以混合使用字符串与 JSX,这也是 JSX 很类似于 HTML 的地方:

Here is a list:

  • Item 1
  • Item 2

某个 React 组件不可以返回多个 React 元素,不过单个 JSX 表达式是允许包含多个子元素的;因此如果我们希望某个组件返回多个并列的子元素,就需要将它们包裹在某个 div 中。JavaScript 表达式

我们可以传入包裹在 {} 内的任意 JavaScript 表达式作为子元素,譬如下述声明方式渲染的结果是相同的:

foo

{'foo'}

这种模式常用于渲染 HTML 列表:

function Item(props) {

return

{props.message};

}

function TodoList() {

const todos = ['finish doc', 'submit pr', 'nag dan to review'];

return (

{todos.map((message) => )}

);

}JavaScript 函数

正常情况下 JSX 中包含的 JavaScript 表达式会被解析为字符串、React 元素或者列表;不过 props.children 是允许我们传入任意值的,譬如我们可以传入某个函数并且在自定义组件中调用:

// Calls the children callback numTimes to produce a repeated component

function Repeat(props) {

let items = [];

for (let i = 0; i < props.numTimes; i++) {

items.push(props.children(i));

}

return

{items}

;

}

function ListOfTenThings() {

return (

{(index) =>

This is item {index} in the list

}

);

}布尔值与空值

false,null,undefined 与 true 是有效的子元素,不过它们并不会被渲染,而是直接被忽略,如下的 JSX 表达式会被渲染为相同结果:

{false}
{null}
{undefined}
{true}

避免 XSS 注入攻击

最后需要提及的是,React 中 JSX 能够帮我们自动防护部分 XSS 攻击,譬如我们常见的需要将用户输入的内容再呈现出来:

const title = response.potentiallyMaliciousInput;

// This is safe:

const element =

{title}

;

在标准的 HTML 中,如果我们不对用户输入作任何的过滤,那么当用户输入

使用数组封装

直接插入原始的 HTML,React 为我们提供了 dangerouslySetInnerHTML 属性,其类似于 DOM 的 innerHTML 属性,允许我们声明强制直接插入 HTML 代码:

function createMarkup() {

return {__html: 'First · Second'};

}

function MyComponent() {

return

;

}

浏览器解析jsx_React 初窥:JSX 详解相关推荐

  1. python3 urlopen_扣丁学堂解析Python3中urlopen()使用详解

    扣丁学堂解析Python3中urlopen()使用详解 2018-07-23 13:31:14 1318浏览 在现如今,2018年Python一直属于IT行业中比较热门技术,那么今天扣丁学堂Pytho ...

  2. dicom多帧转换_Python解析多帧dicom数据详解

    概述 pydicom是一个常用python DICOM parser.但是,没有提供解析多帧图的示例.本文结合相关函数和DICOM知识做一个简单说明. DICOM多帧数据存储 DICOM标准中关于多帧 ...

  3. 360浏览器怎么导入html,360浏览器收藏夹导入/导出方法详解

    360浏览器收藏夹导入/导出方法详解 我们在使用360浏览器的时候,如果需要将收藏夹导出备份或者将已有导出的收藏夹导入,该如何操作呢?今天一起来看看360浏览器收藏夹导入/导出方法. 360浏览器收藏 ...

  4. 阿里聚安全Android应用漏洞扫描器解析:本地拒绝服务检测详解

    阿里聚安全Android应用漏洞扫描器解析:本地拒绝服务检测详解 阿里聚安全的Android应用漏洞扫描器有一个检测项是本地拒绝服务漏洞的检测,采用的是静态分析加动态模糊测试的方法来检测,检测结果准确 ...

  5. 浏览器解析jsx_React——JSX语法

    JSX(JavaScript XML)是 js 内定义的一套类 XML 语法,可以解析出目标 js 代码,颠覆传统 js 写法.HTML 由浏览器解析,而 JSX 是由 js 解析.当让也可以通过构建 ...

  6. 【THREE源码解析篇】THREE.Sprite详解

    THREE.Sprite详解 问题背景 1.sprite_vert.glsl解析 问题背景 因为之前想修改sprite使得可以不面向屏幕,正常立起来和3D文字一样,对此研究了下THREE对这块的实现原 ...

  7. 顶象App加固技术解析:DEX文件格式的详解

    Android应用的源代码主要分为java与C/C++两部分,其中java编译后的文件是DEX文件,也是Android Dalvik虚拟机运行的程序,因此也是基于Android平台的虚机应用很难绕过的 ...

  8. easyui js解析字符串_js相关:详解Jquery Easyui的验证扩展

    js相关:详解Jquery Easyui的验证扩展 发布于 2020-7-2| 复制链接 本文主要介绍了Jquery Easyui验证扩展,Easyui验证,Easyui校验,js正则表达式.具有一定 ...

  9. DNS 缓存、CDN 缓存、浏览器缓存,JVM DNS 缓存详解

    最近测试遇到修改host文件后,清除浏览器缓存后,重启浏览器后,发现浏览器还是访问老DNS服务,于是网上查查原因,豁然开朗. 这里对互联网上一些文章进行整理,原文可查看参. 1.DNS 缓存 1.1 ...

最新文章

  1. 蚂蚁金服“刷脸”支付技术解读:错误率低于百万分之一
  2. Linux for windows
  3. 大世界游戏制作:《幽灵行动·荒野》程序化技术介绍
  4. maven2-snapshot快照库和release发布库的应用
  5. 《兔兔公司的历史》那些年,百度的荣耀和沉沦
  6. lin通信ldf文件解析_lin ldf
  7. 机器学习之监督学习(二)——神经网络
  8. JZ5-用两个栈实现队列
  9. iOS-SEL的用法和讲解
  10. 湖北为何迷失在中国互联网版图 应“引凤归巢”
  11. coq 函数式编程--Basics.v
  12. 项目管理: Alpha,Beta,RC,GA,Release
  13. dede php低版本出现500错误,织梦后台生成文章提示500错误怎么办
  14. Excel删除文本中任意位置所有空格的3种方法比较
  15. 解决谷歌浏览器打开后是毒霸浏览器的问题
  16. oracle grant
  17. Python快速统计文件中高频词
  18. python macd quantization trade_9纠正MACD的变量错误
  19. jsch.addIdentity invalid key报错问题
  20. 基于java的理发店管理系统_理发店会员管理系统

热门文章

  1. 使用rest_framework的routers模块添加路由
  2. 前端下载本地excel模板
  3. 页眉怎么添加【节】,设置不同章节不同页眉
  4. java反编译工具gd gson,浅谈Android中static修饰符,及Gson转String实例
  5. EXCEL列乱序后内容重新对应
  6. kettle 邮件服务器,kettle 实用功能之三 ---- 使用 kettle 群发动态内容的邮件。
  7. 51单片机c语言试题及答案,单片机C语言期末考试题..
  8. matlab多重积分如何使用方法,MATLAB在《高等数学》中的应用_多重积分
  9. Rime常用配置||输入默认英文
  10. 工业4.0细谈MES制造执行系统