zoukankan      html  css  js  c++  java
  • React学习手册笔记

    第1章 初识React

    第2章 JavaScript新特性

    第3章 JavaScript函数式编程

    第4章 React进阶

    第5章 React与JSX

    第6章 Props、State和组件树

    第7章 组件扩展

    第8章 Redux

    第9章 React Redux

    第10章 测试

    第11章 React Router

    第12章 React服务器端应用

    《React学习手册实践练习》:https://blog.csdn.net/nmj2008/article/details/115356376

    第1章 初识React

    React是--款用于创建用户界面的流行库。它是Facebook为了辅助数据驱动的大型网站解决某些问题而研发的。2013年React刚发布时,该项目受到了外界的一-些质疑,因为React的技术规范非常独特。

    为了不让新用户望而却步,React核心团队编写了名为“Why React” 的文章建议读者“给它(即React) 5分钟”。他们鼓励人们摒弃先入为主的观念,先了解一下React再对它评判也不迟。

    的确,React是一款小型脚本库,并不能满足用户即插即用构建自己应用程序的所有需求。那么请给它5分钟。

    的确,在React中, 开发工作就像在JavaScript中编写HTML代码。当然,这些标签需要经过预处理才能在浏览器中工作。用户可能还需要类似webpack这类构建工具处理这些事情。那么请给它5分钟。

    如果你像我们一样读到了这篇文章,你也许会被层出不穷的JavaScript库允诺的功能弄得眼花缭乱,即一个能够解决所有和DOM相关问题的脚本库:该库可以大幅度减轻我们的工作量,并且还不会让我们陷入沮丧。

    然后问题接踵而至:如何转换JSX格式?如何载入数据?在哪里存放CSS文件?什么是声明式编程?每种问题又会引出更多问题,比如用户如何将该库集成到日常的工作中。每种规范又引入新术语、新技术和更多问题。

    花几分钟时间学习了React组件的相关知识之后,读者应该会了解到与以往不同的一种Web开发方式。不过在用户开始使用React编写程序之前还有不少障碍需要克服。

    首先,React是一款小型脚本库,并且只能完成部分工作。它并没有像一般的JavaScript框架那样附带用户需要的所有工具。开发生态环境中所需的大部分工具需要用户自主选择。此外,新工具的不断涌现,旧的工具就会被取而代之。随着越来越多的库名称出现在团队讨论中,真有点让人应接不暇。

    React相对于JavaScript历史来说,正处于一个非常重要而又混乱的时代。ECMA过去发布相关规范的频率非常低。有时需要长达10年才发布-版新规范。这意味着开发者不需要花太多时间学习新的语法。

    自2015年以来,每年都会有新的语言特性和语法发布。最近一年多(ECMAScript2016,ECMAScript 2017)甚至更新了数个版本系统(ECMAScript3, ECMAScript5)。随着该语言的发展,React社区的早期使用者倾向于使用新的语法。这通常意味着开发文档会假定用户是了解最新的ECMAScript语法的。如果用户不熟悉最新的语法规范,那么浏览React代码时就会晕头转向。

    JavaScript函数式编程的兴起

    除了语言层面上出现的变化之外,JavaScript函数式编程也后劲十足。JavaScript不一定必须是一门函数式编程语言,不过可以在JavaScript代码中使用函数式编程范式。React鼓励用户使用函数式编程胜过面向对象编程。这种思维转变可以在程序的可测试性和执行性能方面获得更多好处。不过大量的React材料采用了上述编程范式时,学习曲线也变得异常陡峭。

    JavaScript工具审美疲劳

    JavaScript工具审美疲劳(http://bit.ly/2pSiuE4) 已经是老生常谈了,不过归根结底这个问题是由构建过程产生的。在过去,用户只需将JavaScript文件添加到自己的页面即可。而现在JavaScript文件通常都必须经过编译,通常还伴随着一个自动化持续分发过程。新兴的语法必须经过转换以便可以在大部分浏览器上运行。JSX格式的文件必须转换成JavaScript文件。SCss文件有可能还需要进行预处理。这些组件也需要通过测试。用户也许会爱上React,但是现在必须先成为- - 名使用webpack的专家,处理代码分割、压缩和测试等工作。

    React易于上手的原因

    本书的目标是通过将相关材料有机地组织起来,避免读者在学习过程中产生混乱,同时也为后续的学习打下坚实的基础。我们将从学习新的语法开始,让读者了解JavaScript的新特性,特别是React中经常用到的。然后我们将会简要介绍一下JavaScript的函数式编程,使得用户能够马上应用这些技术,并理解上述范式对React产生的重要影响。

    然后我们将通过向读者介绍第--个React组件来讲解相关的基础知识,同时还会阐述如何转换代码和必须这么做的原因。在了解相关基础知识之后,我们将会向读者介绍一个可以保存和调整颜色的新应用示例。该应用将会使用React构建,并使用若千高级React技术进行优化,采用Redux作为客户端数据容器,通过Jest测试 和React路由完成该应用的构建。最后一章,我们将会介绍通用的同构代码,并通过在服务器端渲染来提高颜色管理器应用的性能。

    我们希望以这种方式加速读者对React生态系统的深入了解,而非找尝辄止。同时可以让读者掌握在实际工作中构建React应用必备的技能和工具。

    React技术展望

    React仍然很年轻。它的核心功能已经非常稳定,不过还是有改善空间的。React的未来版本将会加入线程,以及旨在提高渲染速度的核心算法的重新实现。这些特性对于开发者会产生重大影响的观点还为时过早,不过这将大幅度提高应用App渲染和更新的效率是毋庸置疑的。

    许多必要的改进都和目标设备有关。本书介绍的技术主要是通过React开发单页Web应用的,不过我们不应该假定Web浏览就是React应用App可以运行的唯一场景。2015年诞生的React Native项目允许用户将React应用转换成原生的iOS和Android应用App。React VR是一个构建交互式虚拟现实应用的框架,开发者可以使用React和JavaScript为用户提供360°的全景体验。React库的--条命令就可以帮助开发者构建跨屏幕和设备的快速开发环境。

    我们希望能够为用户提供一个坚实的平台,这个平台能够适应不断变化的开发生态环境,并且构建的应用可运行的平台不仅仅局限于Web浏览器。

    拥抱变化

    React和相关工具的发展可谓日新月异,有时甚至是一些革命性的变化。事实上,这些工具的某些未来版本和本书目前介绍的内容也可能大相径庭。用户仍然可以放心地运行本书的代码示例。我们将会在package.json文件中提供精确的软件版本信息,因此读者在安装相关版本时大可放心。

    除了本书之外,读者仍然可以通过React官方博客(https://facebook. github. io/react/blog/)了解该项目的最新动态。当React有新版本发布时,核心团队将会撰写博文介绍该版本的细节和新的功能特性。

    还有不少流行的React技术峰会可供读者追踪它的最新动态。如果用户不能亲临会场,React技术峰会还会将会议内容录制成视频放在YouTube视频网站上供用户浏览。这些峰会主要包括:

    React Conf (http://conf.reactjs.com/)
    Facebook赞助的湾区会议。
    React Rally (http://www.reactrally.com/)
    盐湖城的社区会议。
    ReactiveConf (https://reactiveconf.com/)
    斯洛伐克首都布拉迪斯拉发的社区会议。
    React Amsterdam (http://react. amsterdam/)
    阿姆斯特丹的社区会议。

    与本书有关的GitHub版本库(https://github.com/moonhighwayllearning-react) 提供的代码文件都是以章节为单位进行组织的。该版本库混合了代码文件和JSBin示例程序。如果你以前从没有用过JSBin,那么它其实是类似Code Pen和JSFiddle的在线代码编辑器。JSBin的主要特性之一就是用户只需单击相关链接,马上就可以修复该文件。当用户创建或者开始编辑一个JSBin时,它会为用户的示例代码创建一个唯一的URL地址,如图1-1所示。

    第2章 JavaScript新特性

    本章将会向读者介绍本书用到的所有Javascript新特性。如果你还没有选择使用最新的语法,那么现在应该是一个良好的契机。如果你已经非常熟悉ES.Next语言规范中的特性,那么完全可以略过本章,直接阅读下一章的内容。

    ES6中的变量声明

    在ES6之前,声明变量的唯一方式就是通过var关键字。现在我们有了一些不同的选项对这一功能进行改进。

    关键字const

    常量是一个不能被修改的变量。和之前其他语言类似,JavaScript在ES6中引入了常量的概念。

    在常量之前,我们使用的都是变量,所有变量都可以被重写:

    var pizza = true
    pizza = false
    console.1og(pizza) // false

    我们无法重置一个常量的值,并且当我们尝试重写常量时,系统会生成一个控制台错误信息(见图2-1) :

    const pizza = true
    pizza = false

    关键字let

    JavaScript目前已经拥有文法层面的变量作用域了。在JavaScript中, 我们使用一对花括号表示代码块,这些花括号限定了变量的作用域。换句话说,类似if/else语句这样的情况。如果你以前学习过其他编程语言,可能也会假定这些代码块限定了变量作用域,不过并非如此。

    如果变量是在if/else语句块中创建的,那么变量的作用域并不会受到该代码块的限制。

    var topic = "JavaScript"
    if (topic) {
      var topic = "React"
      console.log('block', topic) // block React
    }

    console.1og(' global', topic)//global React

    if语句块中的变量topic重置了全局变量topic的值。

    通过使用let关键字,我们可以将一个变量的作用域限定在任意代码块中。使用let关键字可以确保全局变量的值不受干扰:

    var topic = "JavaScript"
    if (topic) {
    let topic = "React"
    console.1og('block', topic) // React
    }
    console.1og('global', topic)
    // JavaScript

    topic的值没有重置语句块之外的变量。
    花括号不能限制变量作用域范围的另外一个地方是在循环体中:

    var div,
    container = document.getElementById('container')
    for (var i=0; i<5; i++) {
    div = document.createElement('div')
    div.onclick = function() {
    alert('This is box #' + i)
    }
    }
    container.appendChild(div)

    在这个循环中,我们在容器中创建了5个div元素。每个div都关联了一个单击事件来弹出一个警示对话框显示索引。在for循环中声明i时,创建一一个全局变量i,然后进行循环直到它的值变成5。当用户单击任意一个div时,警示对话框显示i的值都会是5,因为当前全局变量i的值就是5 (见图2-2) 。

    使用关键字let替代var定义循环计数器i,从而限定它的作用域。现在单击任意一个box后,将会显示i的作用域被限定在循环过程内部的值(见图2-3):

    var div, container = document.getElementById('container')
    for (let i=0; i<5; i++) { 
    div = document. createElement('div')
    div. onclick = function() {
    alert('This is box #: ' + i)
    container.appendChild(div)
    }

    模板字符串

    模板字符串为用户提供了一种连接字符串的替代性方案。它还允许用户在字符串中插入变量。

    普通的字符串拼接是通过加号(+)和逗号(, )将变量和若干字符串拼接在一起后得到一个新字符串的:

    console.1og(lastName +","+ firstName +","+ middleName )

    通过模板,我们可以创建-一个字符串并使用${ }将变量值插入其中:

    console.log('${lastName}, ${firstName} ${middleName}')

    JavaScript的任意返回值都可以被添加到模板字符串的${ }内部。

    模板字符串还支持空格,这使得起草email模板、代码片段,以及其他任何包含空格的内容更容易了。现在用户可以在不破坏代码片段的情况下构造一个跨越多行的字符串。示例2-1演示了Tab制表符、换行符、空格和变量名在email模板中的具体用法。

    以前在JavaScript代码中直接使用HTML字符串并不是很方便,这是因为我们需要将它们挤在一行才能运行。现在空格可以被当文本处理,用户就可以将格式化的HTML插入其中,并且也更易于理解:

    document.body.innerHTML =
    <section>
    <header>
    <h1>The HTML5 Blog</h1>
    </header>
    <article>
    <h2>${article.title}</h2>
    ${article. body}
    </article>
    <footer>
    <p>copyright ${new Date().getYear()} | The HTML5 Blog</p>
    </footer>
    </section>

    注意,我们还可以将页面的标题和其中的内容文本作为变量插入模板字符串中。

    默认参数

    包括C++和Python这样的编程语言都允许开发者为函数参数声明默认值。ES6中也添加了对默认参数的支持,因此如果事件调用过程没有提供参数,那么系统将会使用默认参数值。

    比如我们可以构造一个默认字符串:

    function logActivity(name=" Shane McConkey", activity="skiing") {
    console.1og(' ${name} loves ${activity}')
    }

    如果在调用函数favoriteActivity时没有给它提供参数,那么它可以使用默认参数值正确地执行。默认参数并不局限于字符串,可以是任意类型:

    var defaultPerson = {
      name:{
        first: "Shane" ,
        last: "McConkey"
      },
      favActivity: "skiing"
    }
    function logActivity(p=defaultPerson) {
      console.1og( ${p.name.first} loves ${p. favActivity}")
    }

    箭头函数

    ES6中的箭头函数是一个非常有用的特性,用户可以在不使用function关键字的情况下创建一个函数,并且用户通常还不需要使用return关键字。示例2-2是一个使用普通函数语法的例子。

    示例2-2:一个普通函数

    var lordify = function(firstname) {
      return '${frstname} of Canterbury'
    }
    console.log( lordify("Dale") ) / Dale of Canterbury
    console.log( lordify("Daryle") ) // Daryle of Canterbury

    使用箭头函数,我们可以大幅度简化函数语法声明,如示例2-3所示。

    示例2-3:一个箭头函数

    var lordify = firstname =>'${firstname} of Canterbury'

    通过使用箭头,我们现在只需一行代码就获得了一个函数实体。关键字function被移除了。我们还可以移除关键字return,因为箭头指向的内容将会自动返回。另外一个优点是如果函数只包含一个参数,我们还可以移除参数两边的括号。

    包含一个以上的参数时,函数两边的圆括号是必不可少的。

    //旧方案
    var lordify = function(irstName, land) {
    return '${firstName} of ${land}'
    }
    //新方案
    var lordify = (firstName, land) =>'${firstName} of ${land}'
    console.1og( lordify("Dale", "Maryland") ) // Dale of Maryland
    console.1og( lordify("Daryle","Culpeper") ) // Daryle of Culpeper

    我们可以将它当作单行函数使用,因为只有一行语句需要返回。

    出现多行语句声明时需要使用一对花括号将它们括起来:

    //旧方案
    var lordify = function(firstName, land) {
      if (!firstName) {
        throw new Error('A firstName is required to lordify')
      }
      if (!land) {
        throw new Error('A lord must have a land' )
      return '${firstName} of ${land}'
    } //新方案 var _lordify = (firstName, land) => { if (!firstName) {

       throw new Error('A frstName is required to lordify')
      }
      if (lland) {
        throw new Error('A lord must have a land')
      }
      }
      return '${frstName} of ${land}'

    }
    console.log( lordify("Kelly", "Sonoma") )// Kelly of Sonoma
    console.1og( lordify("Dave") )// ! JAVASCRIPT ERROR

    这些if/else语句中虽然使用了花括号,不过它们仍然可以利用箭头函数简短语法的特性。

    箭头函数并不会拘泥于此。比如在setTimeout回调函数中的关键字this可以表示tahoe对象之外的其他对象:

    var tahoe ={
      resorts: ["Kirkwood" ,"Squaw" , "Alpine" , "Heavenly" , "Northstar"],
      print: function(delay=1000) {
        setTimeout(function() {
        console.1og(this. resorts. join(","))
      },delay)
    }
    }
    tahoe. print() // Cannot read property 'join' of undefined

    上述代码抛出异常信息是因为它尝试在this指代的对象上调用.join方法。在这种情况下,this指代是window对象。此外,我们可以使用箭头函数的语法限定this的作用域:

    var tahoe = {
    resorts:
    ["Kirkwood" ,”Squaw","Alpine" ,"Heavenly" ,"Northstar"],
    print: function(delay=1000) {
    setTimeout(() = {
    console.1og(this.resorts .join(","))
    }, delay) 
    }
    tahoe.print() / Kirkwood, Squaw, Alpine, Heavenly, Northstar

    这一次代码能够正常工作,并且我们可以调用.join方法对resorts中的元素用逗号分隔后打印输出。不过需要注意的是,用户心里一定要时刻保持对作用域的关注。箭头函数不会限定下列代码中关键字this的作用域:

    var tahoe = {
      resorts: ["Kirkwood" , "Squaw","Alpine" , "Heavenly" , "Northstar"],
      print: (delay=1000) = {
      setTimeout(() => {
      console. log(this.resorts . join(","))
      }, delay)
    }
    tahoe.print() / Cannot read property resorts of undefined

    将print函数声明改为一个箭头函数的形式意味着其中关键字this指代的对象就是window。

    为此,我们可以修改控制台的信息输出,来验证一下其中的关键字this指代的是否就是window :

    var tahoe ={
    resorts: ["Kirkwood" ,"Squaw" ,"AIpine" , "Heavenly" , "Northstar"],
    print: (delay=1000)=> {
    setTimeout(() => {
    console.log(this == window)
    }, delay) 
    }
    
    }
    tahoe.print()

    上述代码的输出结果是true。为了解决这个问题,我们可以使用普通的函数声明方式:

    var tahoe = {
    resorts: ["Kirkwood" , "Squaw" , "Alpine","Heavenly" , "Northstar"],
    print: function(delay=1000) {
    setTimeout(() => {
    console.log(this === window)
    }, delay)
    }
    tahoe.print() //false

    ES6转译

    并不是所有浏览器都支持ES6,其中有些甚至根本不支持该规范。确保用户根据ES6规范编写的代码能够正常工作的唯一办法是, 在浏览器中运行这些代码之前将它们转换成符合ES5规范的代码。这个过程被称为转译。Babel(http://ww.babeljs.io/)是当前最流行的转译工具之一。

    在过去,使用JavaScript最新特性的唯一- 办法是等待数周、数月,甚至数年,直到大部分主流的浏览器都支持它们。现在,转译机制使得马上使用JavaScript最新特性的想法变成了现实。转译的步骤和其他语言类似。转译不是编译:我们的代码并没有被编译成二进制形式。相反,它们被转译成能够被绝大多数浏览器识别的语法。当然,JavaScript目前还有自己的源代码,这意味着属于用户项目的某些源代码文件并不是直接在浏览器中运行的。

    示例2-4展示了一些ES6代码。其中包括一个箭头函数,并且其中已经包含了某些参数名为x和y的默认参数。

    示例2-4: Babel转译之前的ES6代码

    const add=(x=5,y=10)=>console.log(x+y);

    对上述代码进行转译之后,结果如下列代码所示:

    "use strict";
    var add = function add() {
    var x = arguments.length <= 0| arguments[o] === undefined ?
    5 : arguments[0];
    var y = arguments.length <= 1 I | arguments[1] === undefined ?
    10 : arguments[1];
    return console.1og(x + y);
    };

    用户还可以使用内联式Babel转译器在浏览器中直接对JavaScript代码进行转译。用户只需添加browser.js文件,并将任意脚本的类型指定为type="text/babel"即可(虽然当前Babel的版本是Babel 6,不过调用Babel 5版本的CDN服务仍然可以正常工作)。

    <script
    src="https://cdnjs.cloudflare.com/ajax/1ibs/babel-core/5.8.23/browser.js">
    </script>
    <script src="script.js" type=" text/babel">
    </script>

    浏览器中的转译
    这种方法意味着浏览器会在运行时执行转译工作。这对于上线的产品来说并非良策,因为它会大大拖慢页面应用的响应速度。第5章将会向读者详细介绍如何在已经上线的产品中解决这个问题。现在,CDN服务将会允许客户端发现和使用ES6的特性。

    你心里也许会这么想:“太棒了!当所有浏览器都支持ES6时,我们就不需要再使用Babel了!”不过,就目前来看,如果我们希望使用下一版本的特性还离不开Babel。除非发生革命性的变化,在可预见的将来我们可能还需要使用Babel转译代码。

    ES6的对象和数组

    ES6规范为我们提供了一种使用对象和数组的新方法,并且可以在这些数据集中限定变量作用域的范围。这些特性包括解构,对象语义增强和扩展运算符。

    解构赋值

    解构赋值允许用户将某个对象内的字段的作用域本地化,并且可以声明哪些值是将要使用的。

    以对象sandwich为例。它包含四个字段,但是我们只想使用其中两个字段的值。那么我们可以将其中的bread和meat属性的作用域本地化:

    var sandwich = {
    bread: "dutch crunch",
    meat: "tuna" ,
    cheese: "swiss", 
    toppings: ["lettuce", "tomato", ” mustard" ]
    }
    var {bread, meat} = sandwich
    console.log(bread, meat) // dutch crunch tuna

    上述代码将brea d和meat从对象中提取出来,然后为它们创建了相应的局部变量。当然,变量bread和meat的值还可以被修改:

    var {bread, meat} = sandwich
    bread = "garlic"
    meat =”turkey"
    console.log(bread) /1 garlic
    console.1og(meat)“turkey
    console.1og(sandwich. bread, sandwich.meat) // dutch crunch tuna

    我们还可以解构传入的函数参数。比如有一个主要用于记录人名的函数:

    var lordify = regularPerson => {
      console.1og('${regularPerson.firstname} of Canterbury')
    }
    var regularPerson = {
      firstname: "Bill",
      lastname: "Wilson"
    } 
    lordify(regularPerson) /
    / Bill of Canterbury

    除了使用点符号语法访问对象内部属性之外,我们还可以在regularPerson对象之外解构我们需要使用的值:

    var lordify = ({firstname}) => {
      console.1og( ${firstname} of Canterbury )
    } lordify(regularPerson)
    // Bill of Canterbury

    解构还可以更具声明性,这意味着我们尝试完成的代码内容可以更具描述性。通过解构firstname,声明将会只使用变量firstname。关于声明式编程的细节将会在下一章深入展开。

    数组中的值也可以被解构。假定我们希望使用数组中的第一-个值作为变量名:

    var [firstResort] = ["Kirkwood", "Squaw", "Alpine" ]
    console.log(firstResort)“Kirkwood

    我们还可以使用逗号进行列表匹配,继而跳过不必要的值。当用逗号取代应该被跳过的元素时会触发列表匹配操作。对于同一个数组,我们可以通过逗号替换其中的前两个值来达到访问最后一个值的目的:

    var [,,thirdResort] = ["Kirkwood", "Squaw", "Alpine"]
    console.1og(thirdResort) // Alpine

    本章后续的内容中,将会结合数组解构和扩展运算符进一步拓展这个示例。

    对象语义增强

    对象语义增强和解构恰恰相反。它是重组或者回炉再造的过程。通过对象语义增强,我们可以从全局作用域中获得变量并将它们转换成一个对象:

    var name = "Tallac" 
    var elevation = 9738
    var funHike = {name ,elevation}
    console. log( funHike) 11 {name: "Tallac", elevation: 9738}
  • 相关阅读:
    处理emacs-org模式TODO的一个脚本
    MYSQL 数据类型
    Redis命令学习-Transaction(事务)
    成都青羊考场科目二考试分享
    地图入门_坐标系统
    microsoft SQL server,错误2
    搭建个人博客 方式2 使用jekyll
    WIN10 10招
    java正則表達式总结
    图解hdu5301Buildings
  • 原文地址:https://www.cnblogs.com/2008nmj/p/14602732.html
Copyright © 2011-2022 走看看