React 初窥:JSX 详解

React 初窥:JSX 详解 从属于笔者的 React 与前端工程化实践系列文章,本文引用借鉴的以及更多 React 相关资料参考 React 学习与实践资料索引html

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) 函数调用,譬如咱们定义了某个MyButtonreact

// 必需要在 JSX 声明文件中引入 React
import React from 'react';

<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

会被编译为:前端工程化

React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

而若是咱们直接声明某个DOM元素,一样会转化为createElement函数调用:api

React.createElement(
  'div',
  {className: 'sidebar'},
  null
)

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

/** @jsx JSXDOM */
 
var defaultValue = "Fill me ...";
 
document.body.appendChild(
  <div>
    <input type="text" value={defaultValue} />
    <button onclick="alert('clicked!');">Click Me!</button>
    <ul>
      {['un', 'deux', 'trois'].map(function(number) {
        return <li>{number}</li>;
      })}
    </ul>
  </div>
);

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

// JSX
var box =
  <Box>
    {
      shouldShowAnswer(user) ?
      <Answer value={false}>no</Answer> :
      <Box.Comment>
         Text Content
      </Box.Comment>
    }
  </Box>;

而使用模板字符串的方式以下:安全

// Template Literals
var box = jsx`
  <${Box}>
    ${
      shouldShowAnswer(user) ?
      jsx`<${Answer} value=${false}>no</${Answer}>` :
      jsx`
        <${Box.Comment}>
         Text Content
        </${Box.Comment}>
      `
    }
  </${Box}>
`;

其主要缺陷在于由于存在变量的嵌套,须要在做用域中进进出出,很容易形成语法错误,所以仍是 JSX 语法为佳。前端框架

JSX 语法

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

变量使用

  • 注释

在 HTML 中,咱们会使用 <!-- --> 进行注释,不过 JSX 中并不支持:

render() {
  return (
    <div>
      <!-- This doesn't work! -->
    </div>
  )
}

咱们须要以 JavaScript 中块注释的方式进行注释:

{/* A JSX comment */}

{/* 
  Multi
  line
  comment
*/}
  • 数组

JSX 容许使用任意的变量,所以若是咱们须要使用数组进行循环元素渲染时,直接使用 map、reduce、filter 等方法便可:

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
      )}
    </ul>
  );
}
  • 条件渲染

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

{condition && <span>为真时进行渲染</span> }

若是要进行非操做:

{condition || <span>为假时进行渲染</span> }

咱们也可使用常见的三元操做符进行判断:

{condition
  ? <span>为真时进行渲染</span>
  : <span>为假时进行渲染</span>
}

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

{condition ? (
  <span>
   为假时进行渲染
  </span>
) : (
  <span>
   为假时进行渲染
  </span>
)}

元素属性

  • style 属性

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

const divStyle = {
  color: 'blue',
  backgroundImage: 'url(' + imgUrl + ')',
};

function HelloWorldComponent() {
  return <div style={divStyle}>Hello World!</div>;
}

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

const divStyle = {
  WebkitTransition: 'all', // note the capital 'W' here
  msTransition: 'all' // 'ms' is the only lowercase vendor prefix
};

function ComponentWithTransition() {
  return <div style={divStyle}>This should work cross-browser</div>;
}
  • 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 时,必需要使用属性表达式。譬如 <input type='checkbox' checked={true}> 能够简写为<input type='checkbox' checked>,而 <input type='checkbox' checked={falsed}> 即不能够省略 checked 属性。

  • 自定义属性

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

<div customProperty='a' />

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

<div data-attr='attr' />

子元素

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

  • 字符串

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

<MyComponent>Hello World!</MyComponent>

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

<div>Hello World</div>

<div>
  Hello World
</div>

<div>
  Hello
  World
</div>

<div>

  Hello World
</div>
  • JSX 嵌套
    咱们能够嵌套地使用 JSX,即将某些 JSX 元素做为子元素,从而容许咱们方便地展现嵌套组件:

<MyContainer>
  <MyFirstComponent />
  <MySecondComponent />
</MyContainer>

咱们能够混合使用字符串与 JSX,这也是 JSX 很相似于 HTML 的地方:

<div>
  Here is a list:
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
  </ul>
</div>

某个 React 组件不能够返回多个 React 元素,不过单个 JSX 表达式是容许包含多个子元素的;所以若是咱们但愿某个组件返回多个并列的子元素,就须要将它们包裹在某个 div 中。

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

<MyComponent>foo</MyComponent>

<MyComponent>{'foo'}</MyComponent>

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

function Item(props) {
  return <li>{props.message}</li>;
}

function TodoList() {
  const todos = ['finish doc', 'submit pr', 'nag dan to review'];
  return (
    <ul>
      {todos.map((message) => <Item key={message} message={message} />)}
    </ul>
  );
}
  • 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 <div>{items}</div>;
}

function ListOfTenThings() {
  return (
    <Repeat numTimes={10}>
      {(index) => <div key={index}>This is item {index} in the list</div>}
    </Repeat>
  );
}
  • 布尔值与空值
    falsenullundefinedtrue 是有效的子元素,不过它们并不会被渲染,而是直接被忽略,以下的 JSX 表达式会被渲染为相同结果:

<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

避免 XSS 注入攻击

最后须要说起的是,React 中 JSX 可以帮咱们自动防御部分 XSS 攻击,譬如咱们常见的须要将用户输入的内容再呈现出来:

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

在标准的 HTML 中,若是咱们不对用户输入做任何的过滤,那么当用户输入 <script>alert(1)<script/> 这样的可执行代码以后,就存在被 XSS 攻击的危险。而 React 在实际渲染以前会帮咱们自动过滤掉嵌入在 JSX 中的危险代码,将全部的输入进行编码,保证其为纯字符串以后再进行渲染。不过这种安全过滤有时候也会对咱们形成不便,譬如若是咱们须要使用 &copy; 这样的实体字符时,React 会自动将其转移最后致使没法正确渲染,咱们能够寻找以下几种解决方法:

  • 直接使用 UTF-8 字符或者使用对应字符的 Unicode 编码

  • 使用数组封装

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

function createMarkup() {
  return {__html: 'First &middot; Second'};
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}
相关文章
相关标签/搜索