react學習系列之深入jsx

jasminecjc發表於2016-10-01

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及以上版本可用

相關文章