欢迎使用CSDN-markdown编辑器

来源:互联网 发布:阿里云市值 编辑:程序博客网 时间:2024/05/24 07:06

写在前面

最近在学React, 看了React官方的文章https://facebook.github.io/react/docs。 Quick Start部分的内容其实蛮好理解,例子也不错,只要跟着一步一步的做,还是很容易上手的。当Hello World带给我的喜悦渐渐褪去之后,回过头来,还是要再近一步看看React的细节。所以尝试翻译Advanced Guides部分。一方面督促自己仔细的阅读这几篇文章,一方面也留下一点资料以便自己之后阅读。

深度理解JSX

从本质上讲,JSX不过是给React.createElement(component, props, ...children)方法提供了一个语法糖,如下的JSX代码:
<MyButton color="blue" shadowSize={2}>  Click Me</MyButton>

会被编译成下面这样:

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

如果标签没有子节点的话,也可以使用单标签(自关闭形式self-close)的形式,所以:

React.createElement(  <div className="sidebar" />)

会被编译成:

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

如果你想测试一些特殊的JSX是如何被转化成Javascript的,可以尝试读读这个:the online Babel compiler

个人理解

在web前端技术还远没有像今天这样发达的10到15年前,java程序员们如果想使用html标签之外的自定义标签,不得不把html写到servlet里面去,然后在服务器端编译,再用JSP把相关的lib引入到页面,再由servlet编译成浏览器可以解析的html。如今这一过程再度上演,演员换成了前端框架React!

指定React元素类型

JSX标签的第一部分决定了React元素的类型。

首字母大写的类型意味着这个JSX标签来自于一个React component。这些标签可以看作是被编译成可直接引用的命名变量,所以如果你想使用JSX <Foo /> 表达式,那Foo必须在当前js文件的作用域中。

个人理解

  1. JSX的自定义标签,必须以大写字母开头
  2. 如果自定义标签的代码写在了单独的一个js文件里,在使用的时候,要import进来。

React必须在作用域之内

既然JSX会通过一个React.createElement调用来进行’编译’,那React的lib必须总是在你JSX代码的作用域内生效的。

比如:下面的两个import是必须的,哪怕你的代码并没有直接的使用它们。

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

如果你不使用javascript bundler(应该就是npm),而是直接从<script>标签中引用React,那他应该就已经是一个全局的变量了。

使用Dot Notation来引用JSX的类型

你可以使用dot-notation的方式来引用这些JSX. 如果你有一个返回多个React component的单独模块,这会比较方便。比如: 如果MyComponents.DatePicker是一个component, 你就可以在JSX代码里直接这样引用:

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" />;}

所谓的Dot Notation,就是程序员常说的”点”一下。
有的时候,我们可能会把一组相关的React的component定义在一个文件中。但是对外提供统一的引用变量。所谓相关的React的component,意思是: 比如把所有的form相关的component放到一起,或者把相关的数据容器(table, tree)等放到一起。

用户自定义的Components必须是大写字母开头

当元素的类型以小写字母开头的时候,它会被当作像<div><span>那样的内置的标签,以string:'div''span'传递给React.createElement.像<Foo />这种以大写字母开头的类型,会以Object的方式传递给React.createElement(Foo),并且对应到已经定义或引入的React component上。

我们推荐以大写字母来命名component。但是如果你已经有了一个以小写字母开头的component, 你也可以在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" />;}

要解决这个问题,我们要把hello重新命名成Hello,并且用<Hello />来引用

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" />;}

在运行时选择React类型

对于React的类型,你不能使用一般表达式。如果你想要使用一般表达式来引用元素类型的话,你可以先将表达式赋值给一个变量。比较常用的做法是,根据props值的不同来渲染不同的component:

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} />;}

要解决这个问题,我们可以将‘类型’赋值给一个变量:

import React from 'react';import { PhotoStory, VideoStory } from './stories';const components = {  photo: PhotoStory,  video: VideoStory};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

用Javascript的表达式作为Props

你可以用大括号包裹任意Javascript表达式传递给props。比如,在JSX:

<MyComponent foo={1 + 2 + 3 + 4} />

对于MyComponent来说,props.foo的值将会是10,因为表达式1 + 2 + 3 + 4的值就是10.

if语句和for循环在javascript里面都不是表达式,所以它们不能被直接用于JSX。取而代之的是,你可以把他们放到外部的代码中。比如:

function NumberDescriber(props) {  let description;  if (props.number % 2 == 0) {    description = <strong>even</strong>;  } else {    description = <i>odd</i>;  }  return <div>{props.number} is an {description} number</div>;}

你可以在相应的条件渲染和循环中学到更多的知识。

字符串字面量

你可以像props传递字符串字面量。下面两个JSX表达式是等价的:

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

当你传递字符串字面量的时候,如果的它的值含有HTML保留字的时候,下面的两个JSX的表达式是等价的:

<MyComponent message="&lt;3" /><MyComponent message={'<3'} />

这个行为通常来讲没有什么相关性,只是为了便于理解,在这里提及。

Props默认为TRUE

如果你不传递任何值给props,那它默认为true. 下面两个JSX表达式是等价的:

<MyTextBox autocomplete /><MyTextBox autocomplete={true} />

一般的,我们不推荐使用这个方式。因为它会和ES6 Object shorthand混淆。在ES6中,{foo}意思是{foo:foo},而不是{foo:true}。这个特性和HTML的特性是吻合的。

属性蔓延

如果你已经有了一个以对象形式存在的props,并且你希望在JSX中使用它。那你可以使用…作为操作符来传递整个props对象。下面两个components是等价的:

function App1() {  return <Greeting firstName="Ben" lastName="Hector" />;}function App2() {  const props = {firstName: 'Ben', lastName: 'Hector'};  return <Greeting {...props} />;}

当你想建立一个一般性的容器的时候,属性蔓延还是蛮有用的。但是,因为他们传递了许多无关的props给component,这会使你的代码显得笨重。我们建议保守的使用这个特性。

JSX中的子节点

包含开始标签和结束标签的JSX表达式,两个标签中的内容就是自标签。子标签作为一个特殊的props,以props.children的形式在传递到标签中。这里有几种不同的方式来传递子标签:

字符串字面量

可以在开始和结束标签之间放入一个字符串,props.children会讲被这个字符串赋值。这对于内置的HTML标签很有用,比如:

<MyComponent>Hello world!</MyComponent>

这是一个合法的JSX的表达式,在MyComponent中props.children就是简单的字符串”Hello world!”。HTML包含保留字,所以你可以像写HTML那样的写JSX:

<div>This is valid HTML &amp; JSX at the same time.</div>

JSX会自动删除一行中开始与结束的空格。当然,也会删除空行以及末尾的新行。在字面量中间出现的新行,会被浓缩成一行。所以,如下的方式,会被渲染成一样的效果:

<div>Hello World</div><div>  Hello World</div><div>  Hello  World</div><div>  Hello World</div>

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的组件不可以返回多个标签元素,但是一个单一个JSX表达式可以有多个子标签。所以如果你想让一个component渲染多个部分,可以用一个div将这些部分包裹起来。

Javascript表达式做子标签

你可以将任意Javascript表达式作为子标签,用大括号包裹起来就可以。例如:

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

这种方式通常用来渲染一个任意长度的JSX表达式的列表。例如:

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可以和其他类型的子标签混合使用。例如:

function Hello(props) {  return <div>Hello {props.addressee}!</div>;}

用Function作为子标签

正常来讲,插入到JSX中的Javascript表达式将会被执行成个字符串,或一个React元素,或者一个列表。但是props.children的表现就像其他的props一样可以是任何有序的数据,而不仅仅是React渲染过的数据。比如,如果你有一个自定义个component, 可以将props.children作为一个回调:

// Calls the children callback numTimes to produce a repeated componentfunction 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>  );}

传递到自定义component中的子标签可以是任意数据和形式,只要这个component在渲染之前把它转化为React可理解的东西就可以了。

Booleans, Null和Undefined会被忽略

false, null, undefined, 和true都是合法的子标签。他们不会被渲染。所有这些JSX表达式会被统一的渲染成一个东西:

<div /><div></div><div>{false}</div><div>{null}</div><div>{undefined}</div><div>{true}</div>

这有助于有条件的渲染React component。JSX只在showHeadertrue的情况下渲染<Header />:

<div>  {showHeader && <Header />}  <Content /></div>

但是对于有些falsy value,就是一个例外,比如0。当判断条件是这些falsy value时,React component同样会被渲染。比如下面这段代码不会按照预期来执行,因为空字符串也会被打印:

<div>  {props.messages.length &&    <MessageList messages={props.messages} />  }</div>

要解决这个问题,就要保证&&之前的代码始终得到的是boolean:

<div>  <div>  {props.messages.length > 0 &&    <MessageList messages={props.messages} />  }</div></div>

相反的,如果你像让false, true, null或者是undefined出现在页面上,那要先把他们转化成字符串:

<div>  My JavaScript variable is {String(myVariable)}.</div>

原文转自:JSX In Depth

原创粉丝点击