React刚开始红的时候,由于对其不甚了解,觉得JSX的写法略非主流,故一直没打算将其应用在项目上,随着身边大神们的科普,才后知后觉是个好东西。

好在哪里呢?个人拙见,有俩点:

1. 虚拟DOM —— 在DOM树的状态需要发生变化时,虚拟DOM机制会将同一Event loop前后的DOM树进行对比(自然通过一系列高效的算法),如果俩个DOM树存在不一样的地方,那么React仅仅会针对这些不一样的区域(DOM diff)来进行响应的DOM修改,从而实现最高效的DOM操作和渲染。

如下图,我们修改了DOM树上一些节点(或UI组件)对应绑定的state(不知道state是什么没关系,后续我们会提及),React会即刻将其标记为“脏状态”,在一个Event loop结束时(即使过程中你对某个组件的state进行了多次修改),React会计算得出DOM树上需要修改的地方(“脏”了的地方,如下图红点)及其最终的状态,并仅仅针对这些地方进行一次性的重新渲染。

于是好处显而易见,并非每修改一次组件的state,就会重新渲染一次,而是在Event loop结束后做一次“秋后算账”,减少冗余的DOM操作。另外React只针对需要修改的地方来做新的渲染,而非重新渲染整个DOM树,自然效率很是不错。

2. 组件可嵌套,而且,可以模版化嘛 —— 其实在React里提及的“组件”,常规是一些可封装起来、复用的UI模块,说的接地气了可以理解为“带有细粒度UI功能的部分DOM区域”。然后我们可以把这些组件层层嵌套起来使用(当然这样组件间会存在依赖关系)。

至于模块化,类似于ejs那样可以作为独立的模块被引用到页面上来复用,不过咧,它可以直接把UI组件当作脚本模块那样来使用,咱完全可以配合CommonJS、AMD、CMD等规范来require需要的组件模块,并处理好它们的依赖关系(是不是碉堡了)。

基于上述的俩点,自然的也打算投入React怀抱了。不过在这之前得先理清俩点事情:

1. React是一个纯View层,不擅长于和动态数据打交道(哎哟咱也不谈Flux了,Flux的概念其实也不完善,因此它不同于,也替代不了常规的MV*框架;

2. React很擅长于处理组件化的页面,在页面上搭组件的形式有点像搭乐高一样,因此用上React的项目需求常规为界面组件化。另外React只支持到IE8+,就天朝的情况,是否使用React还是得稍微斟酌一番。

唠嗑了这么多,下面开始进入React的入门课程。本章提及的代码都可以在我的Github上下载到。

JSX

JSX是React编写组件的一种语法规范,可以看为是js的扩展,它支持将HTML和JS混写在一起,最终会通过React提供的 JSXTransformer.js 来将其编译为常规的js,方便浏览器解析。我们来个最简单的例子:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>最简单的jsx</title>
  6. <script src="react.js"></script>
  7. <script src="JSXTransformer.js"></script>
  8. </head>
  9. <body>
  10. <div id="a">123</div>
  11. <script type="text/jsx">
  12. var name = 'VaJoy';
  13. React.render(<h1>{name}</h1>, document.getElementById('a'));
  14. </script>
  15. </body>
  16. </html>

页面上我们引入了 react.js 和 JSXTransformer.js 俩个重要的脚本,前者自然是react的核心,后者则是将JSX语法转为js语法。

实际上使用 JSXTransformer.js 在客户端来解析JSX语法是一件冗余、耗性能的事情,我们可以在上线项目之前,事先使用诸如 gulp-react 的工具先把所有的JSX均转换为js文件再引入到页面中。

当然如果你掌握了react的JS写法(当然相比JSX的学习成本要高不少),你可以直接在页面上书写相应的 原生js 即可,来个对比:

  1. //使用JSX
  2. React.render(
  3. <div>
  4. <div>
  5. <div>content</div>
  6. </div>
  7. </div>,
  8. document.getElementById('example')
  9. );
  10.  
  11. //不使用JSX
  12. React.render(
  13. React.createElement('div', null,
  14. React.createElement('div', null,
  15. React.createElement('div', null, 'content')
  16. )
  17. ),
  18. document.getElementById('example')

回到开头第一段代码段,我们将页面的JSX直接写在 <script type="text/jsx"> 中,注意 type 类型要标明为 "text/jsx" 。

然后我们定义了个变量name,并使用了React最基础和最常用的一个方法 React.render() 来渲染DOM:

  1. var name = 'VaJoy';
  2. React.render(<h1>{name}</h1>, document.getElementById('a'));

React.render() 支持两个参数(其实还有第三个可选的参数,作为渲染后的回调),第一个参数为模板的渲染内容(HTML形式),第二个参数表示要插入这段模板的DOM节点(DOM node)

这里要提及一个知识点 —— 在JSX中,遇到 HTML 标签(以 < 开头),将用 HTML 规则解析;遇到代码块(以 { 开头),则用 JavaScript 规则解析。所以我们在<h1>中嵌入变量 name 时是以花括号的形式 {name} 来实现的。

至于执行结果,相信大家很容易猜出来:

即往div里插入了(其实应该说是彻底替换为)JSX中的模板内容(<h1>元素)

鉴于JSX支持HTML跟JS的混写,故其灵活性很高,我们试着把变量换为数组:

  1. var arr = ['HELLO', "here is", 123, "VaJoy`s blog"];
  2. React.render(
  3. <ul>{
  4. arr.filter(function(v){
  5. return typeof v === 'string'
  6. }).map(function(v){
  7. return <li> {v} </li>
  8. })
  9. }</ul>,
  10. document.getElementById('a')
  11. );

结果如下:

组件

开头已经提及React是用于组件化开发的,组件可看为是其最小组成单位,那么什么是组件(component)呢?我们看看下方这张图:

这是一个移动端上的UI界面,用于查询员工列表和某员工的具体信息。那么我们按页面上各功能来细分,以左侧的搜索主页界面而言,可以把它细分为SearchBar、EmployeeList、EmployeeListItem等UI组件,其中EmployeeList组件嵌套了多个EmployeeList组件,而这众多组件的搭配组成了整个Hompage界面。

常规我们用React开发的页面可以看为一个大组件,它由多个小组件搭建而成。

在JSX中,我们可以通过 React.createClass() 方法来创建一个组件(注意组件的名称必须为大写字母开头),并以命名标签的形式(<MyClassName />)来引用:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>组件</title>
  6. <style>
  7. .nameStyle{
  8. color: dodgerblue;
  9. }
  10. </style>
  11. <script src="react.js"></script>
  12. <script src="JSXTransformer.js"></script>
  13. </head>
  14. <body>
  15. <div id="a"></div>
  16. <script type="text/jsx">
  17. var name = 'VaJoy';
  18. var Demo = React.createClass({ //随便建了个叫Demo的组件
  19. render: function() { //每个组件都需要有自己的render方法
  20. var a = "Hello,";
  21. return (
  22. <p className="nameStyle" >{a,name}</p> //当然你可以写为{a}{name}
  23. );
  24. }
  25. });
  26. React.render(
  27. <Demo />, //引入组件Demo
  28. document.getElementById('a')
  29. );
  30. </script>
  31. </body>
  32. </html>

注意每个组件都需要有一个render方法,用于输出组件内容。另外组件DOM元素上的 class 属性需要写成 className ,for 属性需要写成 htmlFor ,这是因为 class 和 for 是 JavaScript 的保留字。

执行效果如下:

可以看到,如果在一个容器中引用了多个变量,React会对应每个文本节点来自动生成对应span标签(React防止XSS的机制,因此要注意规避span的样式污染!)

再来看个组件嵌套的示例,其实都很简单:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>组件嵌套</title>
  6. <style>
  7. .nameStyle{
  8. color: dodgerblue;
  9. }
  10. </style>
  11. <script src="react.js"></script>
  12. <script src="JSXTransformer.js"></script>
  13. </head>
  14. <body>
  15. <div id="a"></div>
  16. <script type="text/jsx">
  17. var name = 'VaJoy',
  18. Demo = React.createClass({
  19. render: function() {
  20. var a = "Hello,";
  21. return <span className="nameStyle" >{a,name}</span>;
  22. }
  23. }),
  24. DemoWrap = React.createClass({
  25. render: function() {
  26. return <ul>{
  27. [1,2,3].map(function(v){
  28. return <li style={{fontSize:'20px'}}>{v} <Demo /></li> //嵌套组件Demo
  29. })
  30. }</ul>;
  31. }
  32. });
  33.  
  34. React.render(
  35. <DemoWrap />, //引入组件DemoWrap
  36. document.getElementById('a')
  37. );
  38. </script>
  39. </body>
  40. </html>

注意在JSX里,给DOM设置 style 必须写成 {{ marginRight : '10px', fontSize : '18px' }} 的映射形式(预防XSS),否则会报错

执行结果(注意连空格都生成了一个span)

组件的“数据”

我们在前头提及了,React只是一个纯view层,并不涉及model。不过但对于React组件而言,它有两种特殊的data —— Props 和 States。其中的 States 有点类似于各MV*框架中的model部分,可以称为React的状态机制,用于与用户进行交互。

1. Props

props表示组件自身的属性,也可以用于在嵌套的内外层组件中传递信息(常规是由父层组件传递给子层组件)。要注意它是不变的、也不应该尝试去改变的。我们来个简单的示例:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>组件Props</title>
  6. <script src="react.js"></script>
  7. <script src="JSXTransformer.js"></script>
  8. </head>
  9. <body>
  10. <div id="a"></div>
  11. <script type="text/jsx">
  12. var Component1 = React.createClass({
  13. render: function() {
  14. return <p> {this.props.abc, this.props.name} </p>;
  15. }
  16. });
  17. var Component2 = React.createClass({
  18. render: function() {
  19. return (
  20. <div className="commentList" onClick={this.handleClick}>
  21. <Component1 abc="你好!" name="张三" />
  22. <Component1 abc="Hi!" name="李四" />
  23. </div>
  24. );
  25. },
  26. handleClick: function (e) {
  27. console.log(this.props.name, e.target);
  28. }
  29. });
  30. React.render(
  31. <Component2 name="我是Component2的name哦!" />,
  32. document.getElementById('a')
  33. );
  34. </script>
  35. </body>
  36. </html>

这里我们注册了俩个组件类 Component1 和 Component2 ,其中 Component2 内嵌了 Component1 。

我们先看 Component1 的定义:

  1. var Component1 = React.createClass({
  2. render: function() {
  3. return <p> {this.props.abc, this.props.name} </p>;
  4. }
  5. });

它返回了一个p标签,其中的内容是 this.props.abc 和 this.props.name,这里的 this 指的是组件 Component1 本身,因此比如 this.props.name 获取到的便是组件 Component1 自身的属性 name 的值(abc 和 name 的值我们都在 Component2 中进行了定义)。

我们接着看 Component2:

  1. var Component2 = React.createClass({
  2. render: function() {
  3. return (
  4. <div className="commentList" onClick={this.handleClick}>
  5. <Component1 abc="你好!" name="张三" />
  6. <Component1 abc="Hi!" name="李四" />
  7. </div>
  8. );
  9. },
  10. handleClick: function (e) {
  11. console.log(this.props.name, e.target);
  12. }
  13. });

在这里我们除了嵌入组件 Component1 并定义了它们的属性值 abc 和 name ,也使用了React的事件机制——我们用 onClick 方法来触发点击事件,其对应的 this.handleClick 是我们自定义的一个方法,用于输出 Component2 的 name 属性和当前被点击的目标元素。

最后我们用 React.render 方法渲染组件 Component2 ,并定义了它的 name 属性:

  1. React.render(
  2. <Component2 name="我是Component2的name哦!" />,
  3. document.getElementById('a')
  4. );

执行结果如下:

这里有点要留意的,无论是 props 组件属性,或者是 onClick 事件,都是 React 内部的东西,我们在HTML上是看不到它们的:

顺便提个Props的语法糖 —— 我们可以通过 “...this.props” 来将父层组件绑定的全部属性都直接写到子组件中:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>test</title>
  6. <style>
  7. .a{margin-right:30px;}
  8. </style>
  9. <script src="react.js"></script>
  10. <script src="JSXTransformer.js"></script>
  11. </head>
  12. <body>
  13.  
  14. <script type="text/jsx">
  15. var CheckLink = React.createClass({
  16. render: function() {
  17. // 这样会把 CheckList 所有的 props 复制到 <a>
  18. return <a {...this.props}>{this.props.children}</a>;
  19. }
  20. });
  21.  
  22. var arr = ['第一个连接', '第二个连接'];
  23. React.render(
  24. <div>
  25. <CheckLink href="/a.html" name="abc" className="a">{arr[0]}</CheckLink>
  26. <CheckLink href="/b.html" name="efg" className="a">{arr[1]}</CheckLink>
  27. </div>
  28. ,document.body
  29. );
  30. </script>
  31. </body>
  32. </html>

执行结果:

Props也并非都是直接写在组件标签上的属性,有一个例外 —— props.children,它表示当前组件的所有子节点(它们常规是在外部的组件赋予的)

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>props.children</title>
  6. <style>
  7. p:active{ color: deeppink; }
  8. </style>
  9. <script src="react.js"></script>
  10. <script src="JSXTransformer.js"></script>
  11. </head>
  12. <body>
  13. <div id="a"></div>
  14. <script type="text/jsx">
  15. var Component1 = React.createClass({
  16. render: function() {
  17. return <li>{this.props.children}</li>
  18. }
  19. });
  20. var Component2 = React.createClass({
  21. list: ['张三', '李四', '王五'],
  22. render: function() {
  23. return (
  24. <ul>
  25. {
  26. this.list.map(function(item){
  27. return <Component1>{item}</Component1>
  28. })
  29. }
  30. </ul>
  31. );
  32. }
  33. });
  34. React.render(
  35. <Component2 />,
  36. document.getElementById('a')
  37. );
  38. </script>
  39. </body>
  40. </html>

留意第27行的代码 return <Component1>{item}</Component1> ,其中的{item}会作为组件Component1的子节点(即 props.children)来处理。

执行如下:

2. State

State 是组件的状态,它是可变的,因此常用于跟用户的交互。

不同于Props,我们需要在组件中使用 getInitialState() 方法来初始化组件的State,并使用 this.setState() 来修改State的值:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4. <meta charset="UTF-8">
  5. <title>state</title>
  6. <style>
  7. p:active{ color: deeppink; }
  8. </style>
  9. <script src="react.js"></script>
  10. <script src="JSXTransformer.js"></script>
  11. </head>
  12. <body>
  13. <div id="a"></div>
  14. <script type="text/jsx">
  15. var Component = React.createClass({
  16. getInitialState: function() {
  17. return {
  18. isClick: !1
  19. }
  20. },
  21. render: function() {
  22. var text = this.state.isClick ? 'clicked!' : 'none click';
  23. return <div>
  24. <input type="checkbox" onChange={this.clickCb} />{ text }
  25. </div>;
  26. },
  27. clickCb: function(){
  28. this.setState({
  29. isClick : !this.state.isClick //点击checkbox时改变state.isClick
  30. })
  31. }
  32. });
  33.  
  34. React.render(
  35. <Component />,
  36. document.getElementById('a')
  37. );
  38. </script>
  39. </body>
  40. </html>

我们先通过 getInitialState() 方法初始化了组件Component的State对象,里面有个 isClick 的属性,默认值为 false。

接着在组件的 render 方法里定义了一个 text 变量,其值会根据 state.isClick 的变化而变化:

var text = this.state.isClick ? 'clicked!' : 'none click';

另外给checkbox控件绑定了React的 onChange 事件,checkbox的勾选状态改变时触发组件的 clickCb 自定义回调事件,它是这样的:

  1. clickCb: function(){
  2. this.setState({
  3. isClick : !this.state.isClick //点击checkbox时改变state.isClick
  4. })
  5. }

如注释,它会通过 this.setState 方法来改变 state.isClick 的值,进而依赖于 state.isClick 的变量 text 的值也会间接变化:

本系列的第一章就暂时讲到这里,也算是一个最基础的入门吧。

共勉~

ReactJS入门(一)—— 初步认识React的更多相关文章

  1. 一看就懂的ReactJs入门教程-精华版

    现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领 ...

  2. ReactJS入门指南

    ReactJS入门指南 本文旨在介绍ReactJS的基本知识,并一步步详细介绍React的基本概念和使用方法等,以及相应的Demo.本文在很大程度上参考了React官方文档和官方指南.如果你英语还不错 ...

  3. ReactJs入门教程

    现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和组件化的开发深深的吸引了我,下面来跟我一起领 ...

  4. ReactJS入门学习二

    ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...

  5. ReactJS入门学习一

    ReactJS入门学习一 阅读目录 React是什么? React如何制作组件? 理解组件属性props 理解页面中如何渲染数据的 理解从服务器端获取数据及理解state的 回到顶部 React是什么 ...

  6. 一看就懂的ReactJs入门教程(精华版)

    一看就懂的ReactJs入门教程(精华版) 现在最热门的前端框架有AngularJS.React.Bootstrap等.自从接触了ReactJS,ReactJs的虚拟DOM(Virtual DOM)和 ...

  7. [转]ReactJS入门教程

    Refference From:http://www.cocoachina.com/webapp/20150721/12692.html 现在最热门的前端框架有AngularJS.React.Boot ...

  8. ReactJS入门二

    ReactJS入门学习二 ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事 ...

  9. ReactJs入门思路

    ReactJs入门思路小指南 原文  http://segmentfault.com/blog/fakefish/1190000002449277 React是怎么搞的? React中,把一切东西都看 ...

  10. ReactJs入门教程-精华版

    原文地址:https://www.cnblogs.com/Leo_wl/p/4489197.html阅读目录 ReactJs入门教程-精华版 回到目录 ReactJs入门教程-精华版 现在最热门的前端 ...

随机推荐

  1. git使用入门

    添加文件到git仓库 git add readme.txt git commit -m "write a readme file" 查询工作区状态 git status 查询修改内 ...

  2. RSA算法学习

    package com.test.rsa; /* * 为了选择公钥和私钥,Bob必须执行如下步骤: * 1)选择两个大素数p和q.那么p和q应该多大呢?该值越大,RSA越难于破解,但是执行加密和解密所 ...

  3. C语言的一些小知识

    注:本文讨论的"C语言"为GNU C,而非ANSI C 标准库 语法 switch语句中的case关键词可以放在任何地方 switch (a) { case 1:; if (b== ...

  4. 创建Unity3D的MacOS Plugin的正确姿势

    http://www.tedlindstrom.se/how-to-link-dylibs-into-unity3d/ I was roaming around the net looking for ...

  5. postgresql数据库

    新建一个虚拟机,在本地跑程序,连虚拟机数据库报错: no pg_hba.conf entry for host "192.168.1.4" Google下,发现是要修改postgr ...

  6. 图解HTTP

    1.返回结果的HTTP状态码 a. 2xx 成功: 200 ok 204 No Content  206 Partial Content b. 3XX重定向:301 Moved Permanently ...

  7. Operation not allowed after ResultSet closed--操作mysql数据库

    一个stmt多个rs进行操作.那么从stmt得到的rs1,必须马上操作此rs1后,才能去得到另外的rs2,再对rs2操作.不能互相交替使用,会引起rs已经关闭错误——Operation not all ...

  8. php正则逆向引用与子模式分析

    先看一个例子: <?php $string = 'April 15, 2003'; $pattern = '/(\w+) (\d+), (\d+)/i'; $replacement = '${1 ...

  9. bzoj 4330: JSOI2012 爱之项链

    听说这题不公开.. 那就不贴题意了 首先要用burnside引理求出戒指的种数,那么对于一个顺时针旋转$k$个位置的置换就相当于连上一条$(i,(i+k)%R)$的边,每个环颜色必须相同 环的个数为$ ...

  10. webpack 使用教程--实时刷新测试

    学习webpack,基本始终是围绕: 1.如何安装webpack 2.如何使用webpack 3.如何使用loader 4.如何使用开发服务器 可能我们会在如何使用开发服务器的时候,遇到诸如调试的相关 ...