文档章节

React学习(1)——JSX语法与React组件

随风溜达的向日葵
 随风溜达的向日葵
发布于 2016/11/29 22:24
字数 3043
阅读 360
收藏 9
点赞 2
评论 1

本文记录了在官网学习如何使用JSX+ES6开发React的过程。  

全文共分为3篇内容:

  1. JSX语法与React组件
  2. 状态、事件与动态渲染
  3. 列表、键值与表单

    扩展:webpack搭建React开发环境

JSX基础介绍

    先看看一个最简单的例子:

const element = <h1>Hello, world!</h1>;

    上面这段有趣的例子既不是标准的JavaScript也不是HTML,它就是我们接下来要介绍的JSX的语法,是一种JavaScript的扩展。在React中使用JSX描述一个UI是什么样子的,就好像HTML告诉浏览器我们看到的页面是什么样子。最开始接触JSX时会感觉它很像一种模板语言,但是除了提供模板能力之外,他拥有JavaScript所有的能力。

    JSX用于产生React的组件,JSX最大的特色就是就是在JavaScript中嵌入和HTML表达式。我们先看下面这个例子:

function formatName(user) {
  //将参数合并成一个srting
  return user.firstName + ' ' + user.lastName;
}

//创建user对象
const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

//创建element对象,并用JSX语法标识为一个html内容。
//h1标签中会包含方法计算之后的内容
const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);

测试代码

    这个例子将JSX语法分成了很多部分,element就是一个HTML的JSX表达式,HTML标签最好使用一组()括号包裹起来以避免分号导致的问题(分号可能会在编译时成为HTML内容的一部分)。ReactDOM是一个react工具,用于提供Dom渲染功能。ReactDOM.render 方法接受2个参数,一个是要渲染的JSX元素,另外一个是Dom对象,render会在这个Dom对象中添加由JSX定义的HTML。

    JSX是一种丰富的表达式,他可以随意嵌套JavaScript和HTML使用,例如if、for等等,比如:

function getGreeting(user) {
  // 使用if来判断输入参数,根据判断结果来输出HTML内容
  if (user) {
    return <h1>Hello, {formatName(user)}</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

    源生的HTML可以任意指定属性,同样在JSX中也有这个能力,例如:

const element = <div tabIndex="0"></div>;
//或
const element = <img src={user.avatarUrl}></img>;

    也可以直接用 />表示一个HTML标签的闭环:

const element = <img src={user.avatarUrl} />;

    当然也可以同时声明父元素和子元素:

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

    需要注意的是:由于JSX更像JavaScript,在使用JSX语法时建议使用驼峰规范来命名。例如将标签上的"class"命名为"className"。

    JSX天生具备防止注入攻击的能力。ReactDom在渲染之前会转义所有嵌入JSX中的值,所以他能确保没有任何特殊的内容被注入到最终的HTML代码中。在渲染之前,所有的东西都会转换成string类型,这将能有效的防止XSS攻击。

JSX对象

    首先需要强调的是,JSX对象就是一个JavaScript对象,所有的JSX表达式最终都会转义成JavaScript。有两种方法可以创建JSX对象:

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

    和

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

    上面2种创建JSX对象的方法结果都是一样的。使用React.createElement() 方法的好处是它会执行一些检查,以帮助我们编写无错误的代码。最终通过转义,他会输出这样一个结构:

const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world'
  }
};

    官方将这个对象的结构称为React元素。React通过这个对象来控制浏览器对dom的渲染,最终显示我们想要的内容。

渲染React元素

    前一小节提到的React元素是React的基本单元,React会由一个一个的基本单元组成,最终构建成一个有效的体系(组件化)。每一个元素用来描述想在屏幕上展示什么。

    和Dom结构不同的是, React元素是一个纯粹的对象并且比创建一个Dom花费的资源更少。React会全局维护所有的元素,并在合适的时候更新到浏览器的Dom,这就是虚拟Dom管理机制。

将一个元素渲染成为Dom

    从一个简单的div标签开始:

<div id="root"></div>

    这是一个“根元素”,我们将通过ReactDom来管理他的所有子元素。一个RreactDom.render方法只能用来渲染一个Dom元素。如果想同时对多个元素进行渲染,可以使用互不关联的RreactDom.render方法来对不同的Dom元素进行操作。

    下面的例子将一个JSX元素渲染到Dom中,完成后,会在页面中显示Hello world:

const element = <h1>Hello, world</h1>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

测试代码

更新已被渲染的元素

    React元素是不可变对象,一旦创建,将不再能够修改,包括其属性和子元素。更新UI的方法就是创建一个新的元素并用ReactDom.render()再次渲染他。如下面的例子:

//创建一个tick方法,用于执行重复方法
function tick() {
  //创建一个JSX对象
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

测试代码

    上面代码中创建了一个tick方法,并使用setInterval让这个方法每1秒执行一次。tick中创建了一个用于显示时间的JSX对象,然后将其渲染到#root节点中。运行代码可以看到例子实现了一个时钟功能,每秒都会调用ReactDom.render动态修改时钟的数字。

需要强调的是:重复使用ReactDom.render方法来多次渲染Dom并不是React推崇的方法。后续的内容中会介绍更合理的方法。

React只执行必要的更新

    ReactDom会将当前的元素与之前的元素进行比对,并且只会更新被改动部分的Dom以避免全局渲染和多次重复渲染。我们可以通过浏览器工具来验证最后一个例子——我们使用render方法创建了整个Dom结构,但是仅仅只有表示时间的文字部分发生了变动。

组件与属性

    组件是React的重要概念,组件能让我们将整个页面的UI分解成独立、可复用、可继续分割的对象。从概念上来说,组件很像JavaScript的一个方法,他可以接受任意的参数输入(React中将这些参数称呼为属性——Props)并返回一个用于UI展示的React元素。

使用函数或类声明组件

     在React中既可以使用function来声明一个组件,也可以使用ES6规范的class关键字来声明一个组件。下面的例子是使用function创建一个组件:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

    例子中使用function声明了一个名为Welcome的组件,他只能接收一个参数用于描述组件的元素。在React中,我们将通过function创建的组件命名为“functional”,因为从字面上看它实际上就是一个JavaScript的函数。

    下面的例子是使用ES6 class方式声明一个组件:

//ES6
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

//JavaScript 非官方内容
var Welcome = React.createClass({
   render: function () {
     return <h1>Hello, {this.props.name}</h1>;
   }
});
//

    上面两种创建组件的方式,从React的角度来说是一样的。

    与使用方法创建组件相比,使用ES6 class的方式创建组件有更多特性,后续篇幅会说明。

渲染一个组件

    为了便于说明,我们先用<div>标签创建一个最简单的组件:

const element = <div />;

    此时,element即可认为是一个组件,组件中只有一个div元素。根据这个定义,我们可以使用用户自定义的组件,比如使用上面的Welcome:

const element = <Welcome name="Sara" />;

    当React发现element中有用户自定义的组件,它会使用JSX语法解析element并将标签上的属性转换成一个JSX对象,这个对象被称为“props”。

    例如下面这个例子,我们经使用组件在屏幕上输出"Hello, Sara":

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

测试代码

看看发生了什么:

  1. 使用ReactDOM.render()方法渲染<Welcome name="Sara" />。
  2. React调用Welcome方法,并传递了一个参数:{name: 'Sara'}。
  3. 在Welcome组件中合并了参数,并返回一个<h1>Hello, Sara</h1>。
  4. ReactDom将<h1>Hello, Sara</h1>更新到浏览器的Dom树中。

需要注意的是,使用React组件时一定要将组件名称首字母大写。例如在html标签中<div>是一个标准的Dom,但是<Welcome>并不是一个标准的html标签,而是一个React组件。React通过判断组件名称的首字母加以区分。

组件组合

    一个组件能够被其他的组件引用,就像使用普通的html标签一样。我们可以把组件抽象成各种抽象功能在任何地方使用,例如一个按钮、一个弹出框、一个表单。下面的例子展示了React组件的组合使用:

function Welcome(props) {//创建Welcome组件
  return <h1>Hello, {props.name}</h1>;
}

function App() {//创建App组件
  return (
    <div>
      <Welcome name="Sara" /> //使用Welcome组件
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render( //向Dom渲染App组件
  <App />,
  document.getElementById('root')
);

测试代码    

    在例子中,首先创建了一个Welcome组件,然后在App组件中重复使用它,最后向浏览器渲染App。App组件中整合使用了Welcome组件。基于组件可以层层封装,建议在使用React开始新项目时先从封装一些小的组件开始,比如按钮、弹出框等,这会对后面开发高层次的业务逻辑时有巨大的帮助。

    一个组件只能返回一个根元素,不能同时包含2个根元素。因此上面的例子中App组件中增加了一个<div>元素将Welcome组件包裹起来。

抽象提取组件

    不必担心组件的分割粒度太小,开发组件时我们最好是通过多个层次的组件组合实现一个更高层次的组件。

    看下面这个例子——封装一个Comment组件:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name} 
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

尝试代码

    Comment组件很难维护,因为它内部的代码都前台和交织在一起,也无法实现代码复用。现在我们稍微修改组件中的Avator,将其提取成一个组件:

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

    Avator组件不需知道他是在哪被使用,它只关心输入的参数,并使用参数生成一个Dom结构。现在可以像下面这样声明一个Comment组件:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />//使用Avatar组件
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

    我们再将UserInfo也提取成一个组件:

function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} /> //使用Avatar组件
        {props.user.name}
      </div>
    </div>
  );
}

    此时的Comment:

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} /> //使用UserInfo组件
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

测试代码

属性(props)只读

    无论是使用函数(function)还是类(class)声明组件,它都不能通过修改props参数来改变值。例如下面这个sum方法:

function sum(a, b) {
  return a + b;
}

var param1 = 1,
    param2 = 2,
    result = sum(1, 2);

    在第一次计算得到结果之后,无论怎么修改param1、param2的值,result都不会改变。

    React相当的灵活自由,但是它有一条必须遵守的规则:

    所有的React组件必须像上面的sum方法这样保证传入的属性(props)参数只读。

原文地址:http://www.chkui.com/article/react/react_jsx_syntax_and_components

© 著作权归作者所有

共有 人打赏支持
随风溜达的向日葵
粉丝 165
博文 51
码字总数 115593
作品 0
广州
其他
加载中

评论(1)

小星星_cjx
小星星_cjx
:clap:
ReactJS学习笔记——npm、JSX、webpack

ReactJS学习笔记——npm、JSX、webpack [toc] React是一个JavaScript库文件,使用它的目的在于能够解决构建大的应用和数据的实时变更。该设计使用JSX允许你在构建标签结构时充分利用JavaScr...

小米墨客 ⋅ 2016/03/18 ⋅ 11

React组件(推荐,差代码)

课程地址:https://www.imooc.com/learn/944 认识React JSX—一种语法结构 一、环境安装: 1.HTTP服务器 安装python3.5.2 建立项目文件夹react_py 打开teminal(windows上我安装的cmder) 进...

xiaoge2016 ⋅ 04/12 ⋅ 0

鹅厂优文 | ReactJS一点通

欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 作者:卢文喆 腾讯云 UI工程师 导语 | 当React 刚开始红的时候,一直觉得 JSX 的设计思想极其独特,属于革命性的创新,它性能出众...

腾讯云加社区 ⋅ 06/06 ⋅ 0

React系列——React主要内容简介

React主要有四个主要内容构成,下面分别来介绍一下: 第1章 1、Virtual DOM 1.1、虚拟DOM是React的基石。 之所以引入虚拟DOM,一方面是性能的考虑。Web应用和网站不同,一个Web应用 中通常会...

龙马行空 ⋅ 2016/03/16 ⋅ 0

VUE 与其他常见前端框架对比

对比其他框架 这个页面无疑是最难编写的,但我们认为它也是非常重要的。或许你曾遇到了一些问题并且已经用其他的框架解决了。你来这里的目的是看看 Vue 是否有更好的解决方案。这也是我们在此...

⋅ 2017/12/09 ⋅ 0

React入门第一弹——React的起源

一、ReactJS简介 React起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用...

我家有宝 ⋅ 2016/01/14 ⋅ 0

React快速入门

简介 React是Facebook开源的一个用于构建用户界面的Javascript库,已经 应用于Facebook及旗下Instagram。 和庞大的AngularJS不同,React专注于MVC架构中的V,即视图。 这使得React很容易和开...

笔阁 ⋅ 2015/04/23 ⋅ 0

React Native第3天——常用组件和常见Q&A

我(web+android开发经验)学习React Native过程中接触的知识点和学习的线路图。 React Native第1天——环境配置及知识体系(http://my.oschina.net/addcn/blog/647290) 掌握环境配置及运行h...

addcn ⋅ 2016/03/30 ⋅ 0

react快速入门

简介 React是Facebook开源的一个用于构建用户界面的Javascript库,已经 应用于Facebook及旗下Instagram。 和庞大的AngularJS不同,React专注于MVC架构中的V,即视图。 这使得React很容易和开...

笔阁 ⋅ 2015/09/29 ⋅ 0

React Native第1天——环境配置及知识体系

我(web+android开发经验)学习React Native过程中接触的知识点和学习的线路图。 React Native第1天——环境配置及知识体系(http://my.oschina.net/addcn/blog/647290) 掌握环境配置及运行h...

addcn ⋅ 2016/03/25 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

windows profesional 2017 build problem

.net framework .... https://stackoverflow.com/questions/43330915/could-not-load-file-or-assembly-microsoft-build-frameworkvs-2017...

机油战士 ⋅ 30分钟前 ⋅ 0

python3中报错的解决方法(长期更新)

1、ImportError: No module named ‘DjangoUeditor’ 出错原因:安装DjangoUeditor库适用于python2,需要下载适用python3的 下载地址:https://github.com/twz915/DjangoUeditor3 2、python3......

xiaoge2016 ⋅ 35分钟前 ⋅ 0

数据结构与算法之双向链表

一、双向链表 1.双向链表的结点结构 typedef struct DualNode{ ElemType data; struct DualNode *prior; // 前驱结点 struct DualNode *next; // 后继结点}DualNode, *DuL...

aibinxiao ⋅ 54分钟前 ⋅ 0

五大最核心的大数据技术

大数据技术有5个核心部分,数据采集、数据存储、数据清洗、数据挖掘、数据可视化。关于这5个部分,有哪些核心技术?这些技术有哪些潜在价值?看完今天的文章就知道了。 大数据学习群:7165810...

董黎明 ⋅ 56分钟前 ⋅ 0

PhpStorm 头部注释、类注释和函数注释的设置

首先,PhpStorm中文件、类、函数等注释的设置在:setting-》Editor-》FIle and Code Template-》Includes下设置即可,其中方法的默认是这样的: /**${PARAM_DOC}#if (${TYPE_HINT} != "v...

nsns ⋅ 56分钟前 ⋅ 0

spring.net AOP

http://www.springframework.net/doc-latest/reference/html/aop-quickstart.html https://www.cnblogs.com/wujy/archive/2013/04/06/3003120.html...

whoisliang ⋅ 今天 ⋅ 0

【HAVENT原创】创建 Dockerfile 生成新的镜像,并发布到 DockerHub

注意:Win7 与 Win10 的版本存在差异,Win7 版本使用 Docker Quickstart Terminal 进入控制台,Win10下面直接用管理员权限打开控制台或者 PowerShell 即可;另外 Win7 下面只能访问 C盘,/ap...

HAVENT ⋅ 今天 ⋅ 0

pom.xml出现web.xml is missing ...解决方案

提示信息应该能看懂。也就是缺少了web.xml文件,<failOnMissingWebXml>被设置成true了。 搜索了一下,Stack Overflow上的答案解决了问题,分享一下。 目前被顶次数最多的回答原文如下: This...

源哥L ⋅ 今天 ⋅ 0

js时间戳与日期格式之间相互转换

1. 将时间戳转换成日期格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 // 简单的一句代码 var date = new Date(时间戳); //获取一个时间对象 /** 1. 下面是获取时间日期的方法,需要什么样的格式自己...

Jack088 ⋅ 今天 ⋅ 0

web添加log4j

添加xml配置log4j.properties # Global logging configuration---root日志设置#log4j.rootLogger=info,dailyRollingFile,stdoutlog4j.rootLogger=debug,stdout,dailyRollingFile---......

黄柳淞 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部