初识React JSX

reactjs.png

今天给大家伙推荐一篇前端文章,以及作者谦谦君子,网名magicly,2010年毕业于北大,获信息管理学士学位和计算机软件学士学位,毕业之后先后在酷我音乐,人人游戏,腾讯等知名互联网公司工作,之后在成都一家创业公司担任CTO,带过一年多的团队。从前到后,网页设计、微信公众号、小程序、后端接口、数据库设计优化、运维脚本、数据统计分析等都做过,使用过Java、JS、Scala、Python、Bash、C++、Rust等语言,算不上精通,但也远非只会写Hello World。最近关注大前端(包括Node.js, React, React Native, Electron, Web Workers, WebAssembly, WebGL, Three.js, D3.js等)以及深度学习算法等。
 
JSX
 
JSX即Javascript Extension,是在js语言上做的扩展,是为了在js里面更方便地书写html而开发。
 
以下即是最简单的一段JSX。
 
const hi = <h1>Hello, JSX!</h1>
嵌入JS表达式
 
JSX里面可以很容易地嵌入JS表达式,比如:
 
const hi = <h1>Hello, JSX! 1 + 1 == {1 + 1}. today is {new Date()}</h1>
JSX也是表达式
 
而JSX本身也是一个表达式,会被Babel等编译成普通的JS对象,所以任何可以使用对象的地方都可以使用JSX,比如if判断,for循环里面,赋值给变量,作为函数参数传递或者返回值返回。比如:
 
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}

指定属性
 
JSX跟html很像(其实就是为了方便使用html才发明了JSX),所以支持指定属性。比如:
 
const img = <img src={user.photo} alt="this is a photo." />
注意,不要使用引号把{}包起来。
 
子元素
 
JSX可以包含子元素(就跟html标签一样),如果没有子元素可以直接用/>来结束标签。
 
const element = (
<div>
<h1 className="hi">Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);

注意JSX其实是JS,所以各种命名等都习惯用JS常用的camelCase风格,并且由于class是JS的关键字,所以标签里面要用className。
 
避免XSS
 
JSX会帮你做正确的escape,避免受到XSS攻击。
 
const title = response.potentiallyMaliciousInput;
// This is safe:
const element = <h1>{title}</h1>;

所以如果title里面有标签<>,则会被转义为<>,如果你确保要插入的html没有问题(比如就是你自己生成的),你可以使用dangerouslySetInnerHTML属性插入未被转义的html内容:
 
const element = <div dangerouslySetInnerHTML={{__html: '<h1>Dangerous!</h>'}} />

注意这里是两个{}包起来哦, 因为外面一层是JSX里面写JS表达式, 里面一层是JS对象本身。
 
JSX的内部表示
 
Babel会把JSX编译成React.createElement()调用,如下两者是完全等同的:
 
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);

React.createElement()会做一些检查(比如props类型等)来减少bug,然后生产如下的对象:
 
// Note: this structure is simplified
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};

这些对象叫“React elements”,可以把它看成是对想要展现在屏幕上的内容描述。React读取这些对象,然后用它们构造DOM节点,并保持状态一致。
 
如果没有子元素的话,可以直接使用闭标签, 比如:
 
<img src="x.jpg" alt="a pic" />
会被翻译成:
 
React.createElement(  'img',
{src: 'x.jpg', alt: 'a pic'}, null)

你可以使用在线Babel编译器来查看JSX具体编译成什么样的JS。
 
必须import React
 
因为JSX是编译成React.createElement,所以React必须在JSX文件中被import,否则会编译报错。比如下面的例子,虽然没有直接引用React,但是也需要import进来。这是初学者常犯的一个错误,经常出现在stateless component文件中, 因为container component里面需要显示地extends React.Component。
 
import React from 'React';
import CustomButton from './CustomButton';

function WarningButton() {
// return React.createElement(CustomButton, {color: 'red'}, null);
return <CustomButton color="red" />;
}

自定义的组件必须大写开头
 
小写字母开头的组件是built-in的组件,比如<div>或者<span>等。如果你定义了一个小写字母开头的组件,那也可以, 不过在用在JSX之前,必须赋值给一个大写字母开头的变量。感觉有点奇怪, 所以不建议这样用,记住定义组件的时候就大写字母开头好了。
 
import React from 'react';

// Wrong! This is a component and should have been capitalized:
function hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
// Wrong! React thinks <hello /> is an HTML tag because it's not capitalized:
return <hello toWhat="World" />;

// 可以这样修复
//const Hello = hello;
//return <Hello toWhat="World" />;
}

最好是定义的时候就定义成大写字母开头的:
 
import React from 'react';

// Correct! This is a component and should be capitalized:
function Hello(props) {
// Correct! This use of <div> is legitimate because div is a valid HTML tag:
return <div>Hello {props.toWhat}</div>;
}

function HelloWorld() {
// Correct! React knows <Hello /> is a component because it's capitalized.
return <Hello toWhat="World" />;
}

JSX Type用.表达式
 
如果你只有一个moduleexport了很多React components,那么可以用.表达式直接访问,比如:
 
import React from 'react';

const MyComponents = {
DatePicker: function DatePicker(props) {
return <div>Imagine a {props.color} datepicker here.</div>;
}
}

function BlueDatePicker() {
return <MyComponents.DatePicker color="blue" />;
}

运行时候动态选择组件类型
 
React组件的类型不能是一个表达式。如果需要根据运行时的状态(比如props的某个值)来渲染不同的组件类型, 可以先把表达式赋值给某个大写字母开头的变量。
 
import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
photo: PhotoStory,
video: VideoStory
};

function Story(props) {
// Wrong! JSX type can't be an expression.
return <components[props.storyType] story={props.story} />;
}

这样才可以:
 
function Story(props) {
// Correct! JSX type can be a capitalized variable.
const SpecificStory = components[props.storyType];
return <SpecificStory story={props.story} />;
}

JSX的props
 
JSX中props的值可以是很多种类型。
 
JS表达式
 
<MyComponent foo={1 + 2 + 3 + 4} />
字符串
 
下面两种写法是一样的, 当然我们一般用第一种,因为简单啊!
 
<MyComponent message="hello world" />

<MyComponent message={'hello world'} />

不指定值默认是true
 
下面两者等价,不过官方说不推荐第一种,因为容易与ES6 object shorthand的写法{autocomplete}表达的是{autocomplete: autocomplete}混淆,官方提供这种写法只是为了跟HTML保持一致。
 
<MyTextBox autocomplete />

<MyTextBox autocomplete={true} />

展开object
 
如果父组件传递的props你想全部传递给子组件,用...展开object操作符会很方便,这个也是ES6的新特性Spread syntax。
 
function App1(props) {
return <Greeting firstName={props.firstName} lastName={props.lastName} />;
}

function App2(props) {
return <Greeting {...props} />;
}

<App1 props={firstName: 'Ben', lastName: 'Hector'} />
<App2 props={firstName: 'Ben', lastName: 'Hector'} />

不过这个功能要慎用,我见过很多人不管父组件传递过来的props包含多少个属性,统统都直接{...props}传递给子组件,而其实父组件传递过来的props可能有10个属性,而子组件只需要1个。而且这样写也看不出来子组件具体需要那几个参数,所以如果不是特别多的话,最好还是显示地写出来传递的props值。记住:
 
Explicit is better than implicit.。
 
子组件
 
JSX中在开标签和闭标签之间的内容会通过props.children传递给组件,值的类型有很多种。
 
字符串
 
<ComponentA>Hello, React!</ComponentA>
上述写法ComponentA中可以通过props.children拿到字符串Hello, React!。字符串是unescaped的,所以<>需要转义之后传递<>。
JSX会把下面这些空白去掉:
 
每行开始和结尾的空白
空行会去掉
挨着标签的换行会去掉
字符串中间的很多换行会合并成一个空格
字符串中间的空格不会去除,不过HTML显示的时候本身多个空格只会显示一个
所以下面这几种都是一样的, 大家可以合理利用空白来增强代码的阅读性。
 
 
   <div>Hello World</div>

<div>
Hello World
</div>

<div>
Hello

World
</div>

<div>

Hello World
</div>

JSX组件
 
跟HTML一样,JSX组件可以嵌套,不同种类的子组件也可以混用,比如:
 
<MyContainer>
Here is a list:
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<ComponentA>sth...</ComponentA>
<ComponentB />
</MyContainer>

JS表达式
 
比如:
 
<MyComponent>foo</MyComponent>

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

<MyComponent>{1 + 2 * 3}</MyComponent>

这种方式用来循环渲染列表的时候很适合,如:
 
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>
);
}

函数!!!
 
JS里面函数是第一等公民,也是可以像数字、字符串一样传递的。当然传递一个函数拿来显示会很奇怪(试一下,发现不会渲染,除非调用toString()转化为String),所以一般传递过去的函数会被调用进行一些转换,转化为React可以渲染的东西。
 
// 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>
);
}

Booleans, Null, Undefined会被忽略
 
false, null, undefined, 以及true都是合法的子元素,但是不会被渲染出来。下面这些JSX渲染出来对的都是一样的: 空元素!
 
<div />

<div></div>

<div>{false}</div>

<div>{null}</div>

<div>{undefined}</div>

<div>{true}</div>

这样做条件渲染就比较方便了,下面这样写的话只有showHeader为true的时候<Header />才会被渲染:
 
<div>
{showHeader && <Header />}
<Content />
</div>

注意有些所谓的falsy value,比如0是会被React渲染的。所以下面的例子中,如果messages是空的话,是会显示0的。
 
<div>
{props.messages.length &&
<MessageList messages={props.messages} />
}
</div>

可以把&&之前的表达式变成boolean来修复:
 
<div>
{props.messages.length > 0 &&
<MessageList messages={props.messages} />
}
</div>

如果确实需要显示false, null, undefined以及true的话,需要转化为String。
 
value is {String(true)}
value is {'' + null}
value is {`${undefined}`}

Rerfers
 
https://facebook.github.io/rea ... .html
https://facebook.github.io/rea ... .html

0 个评论

要回复文章请先登录注册