react学习系列之深入jsx

jasminecjc

JSX是一种javascript语法扩展(也就是语法糖),类似于XML,一般用babel编译成javascript

Why JSX?

React里可以使用原生的javascript,但是React官方推荐使用JSX,使用JSX,组件的结构和组件之间的关系看上去更加清晰。
前端界面的最基本功能在于展现数据,为此大多数框架都使用了模板引擎
模板可以直观的定义UI来展现Model中的数据,你不必手动的去拼出一个很长的HTML字符串,几乎每种框架都有自己的模板引擎。传统MVC框架强调界面展示逻辑和业务逻辑的分离,因此为了应对复杂的展示逻辑需求,这些模板引擎几乎都不可避免的需要发展成一门独立的语言,如上面代码所示,每个框架都有自己的模板语言语法。而这无疑增加了框架的门槛和复杂度。
React直接放弃了模板而发明了JSX。看上去很像模板语言,但其本质是通过代码来构建界面,这使得我们不再需要掌握一门新的语言就可以直观的去定义用户界面:掌握了javascript就已经掌握了JSX
JSX完美利用了javascript自带的语法和特性,我们只要记住HTML只是代码创建DOM的一种语法形式,就很容易理解JSX。而这种使用代码构建界面的方式,完全消除了业务逻辑和界面元素之间的隔阂,让代码更加直观和易于维护。

Transform

var Nav, Profile;
// Input (JSX):
var app = <Nav color="blue"><Profile>click</Profile></Nav>;
// Output (JS):
var app = React.createElement(
  Nav,
  {color:"blue"},
  React.createElement(Profile, null, "click")
);

也就是说,我们写一个XML标签,实质上就是在调用 React.createElement 这个方法,并返回一个 ReactElement 对象。
扩展: React.createElement用法

ReactElement createElement(
  string/ReactClass type,
  [object props],
  [children ...]
)

通过上面的代码可以看出JSX的优势,XML有着开闭标签,在构建复杂的树形结构时,比函数调用和对象字面量更易读

HTML Tags vs. React Components

React.render方法可以渲染HTML结构,也可以渲染React组件。
渲染HTML标签,声明变量采用首字母小写

var myDivElement = <div className="foo" />;
ReactDOM.render(myDivElement, document.getElementById('example'));

渲染React组件,声明变量采用首字母大写

var MyComponent = React.createClass({/*...*/});
var myElement = <MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example'));

语法

JSX的基本语法规则:遇到 HTML 标签(以 < 开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。JSX允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员:

var arr = [
  <h1>Hello world!</h1>,
  <h2>React is awesome</h2>,
];
React.render(
  <div>{arr}</div>,
  document.getElementById('example')
);

Attribute Expressions

// Input (JSX):
var person = <Person name={window.isLoggedIn ? window.name : ''} />;
// Output (JS):
var person = React.createElement(
  Person,
  {name: window.isLoggedIn ? window.name : ''}
);

Child Expressions

// Input (JSX):
var content = <Container>{window.isLoggedIn ? <Nav /> : <Login />}</Container>;
// Output (JS):
var content = React.createElement(
  Container,
  null,
  window.isLoggedIn ? React.createElement(Nav) : React.createElement(Login)
);

Boolean Attributes

// These two are equivalent in JSX for disabling a button
<input type="button" disabled />;
<input type="button" disabled={true} />;

// And these two are equivalent in JSX for not disabling a button
<input type="button" />;
<input type="button" disabled={false} />;

注释

JSX 里添加注释很容易;它们只是 JS 表达式而已。你只需要在一个标签的子节点内(非最外层)小心地用 {} 包围要注释的部分。

var content = (
  <Nav>
    {/* 一般注释, 用 {} 包围 */}
    <Person
      /* 多
         行
         注释 */
      name={window.isLoggedIn ? window.name : ''} // 行尾注释
    />
  </Nav>
);

由于JSX只是一种语法,因此JavaScript的关键字class, for等也不能出现在XML中,而要使用className, htmlFor代替,这和原生DOM在JavaScript中的创建也是一致的。

Spread Attributes

如果你事先知道组件需要的全部 Props(属性),JSX 很容易地这样写:

var component = <Component foo={x} bar={y} />;

如果你不知道要设置哪些 Props,那么现在最好不要设置它:

  var component = <Component />;
  component.props.foo = x; // bad
  component.props.bar = y; // also bad

这样是反模式,因为 React 不能帮你检查属性类型(propTypes)。这样即使你的 属性类型有错误也不能得到清晰的错误提示。

Props 应该被当作禁止修改的。修改 props 对象可能会导致预料之外的结果,所以最好不要去修改 props 对象。
可以用展开属性来写

  var props = {};
  props.foo = x;
  props.bar = y;
  var component = <Component {...props} />;

传入对象的属性会被复制到组件内。

它能被多次使用,也可以和其它属性一起用。注意顺序很重要,后面的会覆盖掉前面的。

  var props = { foo: 'default' };
  var component = <Component {...props} foo={'override'} />;
  console.log(component.props.foo); // 'override'

JSX 陷阱

JSX 与 HTML 非常相似,但是有些关键区别要注意。

内联样式

在React中写行内样式时,要这样写,不能采用引号的书写方式

React.render(
    <div style={{color:'red'}}>
        xxxxx
    </div>,
    document.body
);

HTML 实体

React默认会进行HTML的转义,避免XSS攻击
有多种绕过的方法。最简单的是直接用 Unicode 字符。这时要确保文件是 UTF-8 编码且网页也指定为 UTF-8 编码。

  <div>{'First · Second'}</div>

安全的做法是先找到 实体的 Unicode 编号 ,然后在 JavaScript 字符串里使用。

 <div>{'First \u00b7 Second'}</div>
 <div>{'First ' + String.fromCharCode(183) + ' Second'}</div>

还可以直接使用原生的HTML,但可以看出不推荐这种做法

 <div dangerouslySetInnerHTML={{'{{'}}__html: 'First &middot; Second'}} />

自定义 HTML 属性

如果往原生 HTML 元素里传入 HTML 规范里不存在的属性,React 不会显示它们。如果需要使用自定义属性,要加 data- 前缀。

 <div data-custom-attribute="foo" />

以 aria- 开头的 [网络无障碍] 属性可以正常使用。

 <div aria-hidden={true} />

TIPS

三元运算符代替IF...ELSE

在JSX中是不可以直接在{}中加入if-else的,可以使用下面这种三元表达式:

React.render(<div id={condition ? 'msg' : ''}>Hello World!</div>, mountNode);

在三元运算符满足不了的时候,React建议的方式是在JS代码中使用if表达式

var loginButton;
if (loggedIn) {
  loginButton = <LogoutButton />;
} else {
  loginButton = <LoginButton />;
}

return (
  <nav>
    <Home />
    {loginButton}
  </nav>
);

Switch-Case

return (
  <section>
    <h1>Color</h1>
    <h3>Name</h3>
    <p>{this.state.color || "white"}</p>
    <h3>Hex</h3>
    <p>
      {(() => {
        switch (this.state.color) {
          case "red":   return "#FF0000";
          case "green": return "#00FF00";
          case "blue":  return "#0000FF";
          default:      return "#FFFFFF";
        }
      })()}
    </p>
  </section>
);

False in JSX

ReactDOM.render(<div id={false} />, mountNode);

渲染出 id="false"

ReactDOM.render(<input value={false} />, mountNode);

渲染出字符串"false"作为input的value

ReactDOM.render(<div>{false}</div>, mountNode);

渲染出无子元素

命名空间式组件

用法如下
命名空间式组件允许你使用一个父组件,而其他的子组件是他的属性

var Form = MyFormComponent;
 
var App = (
  <Form>
    <Form.Row>
      <Form.Label />
      <Form.Input />
    </Form.Row>
  </Form>
);

var MyFormComponent = React.createClass({ ... });
MyFormComponent.Row = React.createClass({ ... });
MyFormComponent.Label = React.createClass({ ... });
MyFormComponent.Input = React.createClass({ ... });

注意点如下: React.js的命名空间的组件
只在0.11及以上版本可用

阅读 2.3k

jascheng的前端学习
前端学习路上的笔记和总结
952 声望
37 粉丝
0 条评论
952 声望
37 粉丝
宣传栏