pdf react 进阶之路

2018-05-12 08:42:47 IMbRl71u7pt5X29rlEu7 阅读数 3200

我认识的一个伙计徐超,出了本书——《React进阶之路》,我申请了 2 本,作为福利,送给大家。参与方式在文末。


徐超特别说到:如果您发现这本书是一本烂书,请一定要告诉身边的工程师,避免他们再次掉坑。


这态度,我还没见过第2个。


以下为徐超自荐哦。


我的新书《React进阶之路》在今年四月份正式上市了。

>> 初衷

原本并没有写书的念头,写这本书是一个机缘巧合的事情。

去年(17年)下半年,我开始在一些技术社区写文章,写了几篇文章后,有出版社的编辑找到我,问我要不要写本关于React的书。一开始,我并没有答应,因为在我的印象里,市面上的技术书籍已经很多了,如果写出的书雷同性很高,对读者和作者都没有很大价值。后来,我调研了一番国内关于React图书的情况,发现实际情况并非我开始所想。目前,国内关于React图书的数量是非常少的,而且大都知识点相对陈旧(当然也是源于这些书写作时间较早)。鉴于此,我觉得写一本关于React 的书,对于前端开发人员,还是有比较大的价值的,而且写书也可以对我现有的React知识体系作一个总结和梳理,于我本身也是一次提高的过程。就这样,17年下半年的业余时间,全奉献给了《React进阶之路》这本书。

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1

>> 自荐

讲完了写作的初衷,我想从以下 4 点,为本书作自荐:

  1. 我和我的团队是React技术栈的重度用户。我从15年开始使用React,至今已有3年时间,不敢说精通React,但确实积累了不少React的使用经验。另外,我在团队内还负责团队成员技术的培养,所以我很了解React初学者和初级使用者(初级使用者的定义见后文),经常遇到的困惑和错误。所以,我在写书时,针对这些困惑和错误,会着重讲解,让React初学者和初级使用者可以少走些弯路。

  2. 本书不仅仅是对React这个库本身的讲解,而是对React技术栈的讲解。本书几乎涵盖了React技术栈中的所有主流技术:React、React Router、Redux、MobX,而且都是基于每个技术的最新版本进行讲解,例如,React部分包含React 16新特性的介绍,React Router是基于React Router v4介绍,相较于React Router v2、v3,v4在实现思想上和使用方式上,都有一个巨大的变化。对于很多陈旧/废弃知识点,本书也不再介绍,尽量保证读者所学知识与时俱进。

  3. 本书的写作方式,采用知识点和项目实战相互贯穿的方式。很多React初学者都有这样的感受:看过了很多讲解,但一到项目中就不知道如何下手。本书使用一个简易的BBS项目作为实战案例,并贯穿全书,每介绍一个知识点,就会立即将这个知识点应用到实战案例上,确保读者能从实践中加强对知识点的理解和应用能力。随着学习的进行,读者会看到实战案例越来越完善,最终成为一个”麻雀虽小,五脏俱全“的简易BBS。

  4. 本书非常注重从真实项目中总结实践经验。例如,Redux和MobX 是React 技术栈中最常用的两个状态管理方案,但这两个技术都是从比较高的层面上进行抽象,尤其是Redux,这样就导致很多开发者在真实项目中使用时,遇到了很多困难。本书结合作者自身的项目实践经验,用了大量篇幅指导读者如何在真实项目中使用这两个库,并给出了作者的最佳实践,以期读者能达到独立完成中小型前端项目的整体架构的能力。

>> 面向的读者

本书面向的主要读者:React初学者和初级使用者。一般情况下,1年以内React使用经验的同学可视为初级使用者,但请勿钻牛角尖,具体的初级使用者阶段因人而异。当然,也欢迎各位资深React使用者阅读本书,提供指导和建议。

>> 福利来啦

我向徐超要了2本书送给大家,咱们采用小程序抽奖的方式,识别下面的小程序即可参与:

640?wx_fmt=jpeg

如果想要购买,直接戳阅读原文,到京东。

2019-05-14 12:35:00 weixin_34195142 阅读数 145

1. 箭头函数内的 this 指向函数定义时所在的上下文对象,而不是函数执行时所在的上下文对象。

2. 引入 rest 参数(形式为 ...变量名)用于获取函数的多余参数,来代替 arguments 对象的使用。rest 参数是一个数组,数组织中的元素是多余的参数。注意,rest 之后不能有其他参数。

function languages(lang, ...types){
    console.log(types);
}
languages('JavaScript', 'Java', 'Python');  //["Java","Python"]

3. 扩展运算符:是三个点(...),它讲一个数组转化成用逗号分隔的参数序列,类似于 rest 参数的逆运算。

function sum(a, b, c){
    return a + b + c;
}
let numbers = [1, 2, 3];
sum(...numbers); //6

4. 组件的生命周期:组件从创建到被销毁的过程称为组件的生命周期。React 为不同的生命周期阶段提供了不同的生命周期方法,让开发者更好的控制组建的行为。组建的生命周期被分为三个阶段:挂载阶段,更新阶段和卸载阶段。

4.1  挂载阶段:组件被创建,执行初始化,并被挂载到 DOM 中,完成组建的第一次渲染,用到的生命周期方法有:constructor,componentWillMount,render, componenDidMount。

constructor:这是ES 6 class 的构造方法,组件被创建时,会首先调用组件的构造方法。这个构造方法接收一个 props 参数,props 是从父组件中传入的属性对象,如果父组件中没有传入属性而组件自身定义了默认属性,那么这个 props 指向的就是组件的默认属性。你必须在这个方法中首先调用 super(props) 才能保证props被传入组件中。constructor 通常用于初始化组件的state以及绑定事件处理方法等工作。

componentWillMount:这个方法在组件被挂载到DOM前调用,且只会被调用一次。这个方法在实际项目中很少会用到,因为可以在该方法中执行的工作都可以提前到 constructor 中。在这个方法中调用 this.setState 不会引起组件的重新渲染。

render:这是定义组件时唯一必要的方法(组件的其他生命周期方法都可以省略)。在这个方法中,根据组件的props和state返回一个React元素,用于描述组件的 UI,通常 React 元素使用JSX语法定义。需要注意的是,render 并不负责组件的实际渲染工作,它只是返回一个UI的描述,真正的渲染出页面DOM的工作由React自身负责。render 是一个纯函数,在这个方法中不能执行任何有副作用的操作,所以不能在 render 中调用 this.setState,这会改变组件的状态。

componentDidMount:在组件被挂载到 DOM 后调用,且只会被调用一次。这时候已经可以获取到 DOM 结构,因此依赖 DOM 节点的操作可以放到这个方法中。这个方法通常还会用于向服务器端请求数据。在这个方法中调用 this.setState 会引起组件的重新渲染。

4.2  更新阶段:组件被挂载到 DOM 后,组件的 props 或 state 可以引起组件更新。props 引起的组件更新,本质上是由渲染该组件的父组件引起的,也就是当父组件的 render 方法被调用时,组件会发生更新过程,这个时候,组件 props 的值可能发生改变,也可能没有改变,因为父组件可以使用相同的对象或值为组件的 props 赋值。但是,无论 props 是否改变,父组件 render 方法每一次调用,都会导致组件更新。State 引起的组件更新,是通过调用 this.setState 修改组件 state 来触发的。组件更新阶段,依次调用的生命周期方法有:componnentWIllReceiveProps,shouldComponentUpdate,componentWillUpdate,render, componentDidUpdate

componentWillReceiveProps:组件初始化时不调用,组件接收新的props (不管这个 props 是不是和原来的相同,都会接收)时调用。目前不推荐使用。

shouldComponentUpdate:这个方法决定组件是否继续执行更新过程。当方法返回 true 时(true也是这个方法的默认返回值),组件会继续更新过程;当方法返回 false 时,组件的更新过程停止,后续的 componentWillUpdate、render、componentDidUpdate 也不会再被调用。一般通过比较 nextProps、nextState 和组件当前的 props、state 决定这个方法的返回结果。这个方法可以用来减少组件不必要的渲染,从而优化组件的性能。

componentWillUpdate:这个方法在组件 render 调用前执行,可以作为组件更新发生前执行某些工作的地方,一般也很少用到。

注意:shouldComponentUpdate 和 componentWillUpdate 中都不能调用 setState,否则会引起循环调用问题,render 永远无法被调用,组件也无法正常渲染。

componentDidUpdate(prevProps,prevState):组件更新后被调用,可以作为操作更新后的 DOM 的地方。这个方法的两个参数 prevProps、prevState 代表组件更新前的 props 和 state 。

4.3 卸载阶段:组件卸载阶段只有一个生命周期的方法被调用:componentWillUnmount,这个方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清除组件中使用的定时器,清除componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。

最后注意:只有类组件有生命周期方法,函数组件没有生命周期方法,因此,永远不要在函数组件中使用生命周期方法。

5. 组件 state:组件state必须能代表一个组件 UI 呈现的完整状态集,即组件的任何 UI 改变都可以从state的变化中反映出来;同时,state 还必须代表一个组件UI呈现的最小状态集,即 state 中的所有状态都用于反映组件 UI 的变化,没有任何多余的状态,也不应该存在通过其他状态计算而来的中间状态。state和props又有什么区别呢?state和props都直接和组件的UI渲染有关,它们的变化都会触发组件重新渲染,但props对于使用它的组件来说是只读的,是通过父组件传递过来的,要想修改props,只能在父组件中修改;而state是组件内部自己维护的状态,是可变的。总结一下,组件中用到的一个变量是不是应该作为state可以通过下面的4条依据进行判断:
(1)这个变量是否通过props从父组件中获取?如果是,那么它不是一个状态。
(2)这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
(3)这个变量是否可以通过其他状态(state)或者属性(props)计算得到?如果是,那么它不是一个状态。
(4)这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性。

5.1 state 的更新是异步的:调用setState时,组件的state并不会立即改变,setState只是把要修改的状态放入一个队列中,React会优化真正的执行时机,并且出于性能
原因,可能会将多次setState的状态修改合并成一次状态修改。所以不要依赖当前的state,计算下一个state。当真正执行状态修改时,依赖的this.state并不能保证是最新的state,因为React会把多次state的修改合并成一次,这时this.state还是这几次state修改前的state。另外,需要注意的是,同样不能依赖当前的props计算下一个状态,因为props的更新也是异步的。

6. 组件通信:

6.1 组件挂载阶段通行:componentDidMount是执行组件与服务器通信的最佳地方,原因主
要有两个:
(1)在componentDidMount中执行服务器通信可以保证获取到数据时,组件已经处于挂载状态,这时即使要直接操作DOM也是安全的,而componentWillMount无法保证这一点。
(2)当组件在服务器端渲染时(本书不涉及服务器渲染内容),componentWillMount会被调用两次,一次是在服务器端,另一次是在浏览器端,而componentDidMount能保证在任何情况下只会被调用一次,从而不会发送多余的数据请求。

 7. 路由:一般在项目中建立两个文件夹,非路由组件文件夹(components)和路由组件文件夹(pages 或者 views)。

7.1 嵌套路由的写法:a. 编写路由组件;b. 在父路由中指定路由链接 <NavLink> 或 <Link> 和 路由 <Route>。注意在导入组件的时候,注意区分默认组件和普通组件的导入方法的不同,一旦混淆两者的导入方法,就会报错。

7.2 路由通信(路由组件之间传递数据):子路由接收的是一个 params 参数。该参数在 path 中,冒号后面的内容就是参数。Route 中的 path 会和 link 里面的 to 比较得出参数 parmas。那么子路由的内容,可以通过读取不同的 params 值,来显示,不同的内容。

8. redux:

转载于:https://www.cnblogs.com/rougan/p/10861571.html

2020-05-19 23:18:02 sunleiz 阅读数 136

写在前面

  • 书籍介绍:《React进阶之路》详细介绍了React技术栈涉及的主要技术。本书分为基础篇、进阶篇和实战篇三部分。基础篇主要介绍React的基本用法,包括React 16的新特性;进阶篇深入讲解组件state、虚拟DOM、高阶组件等React中的重要概念,同时对初学者容易困惑的知识点做了介绍;实战篇介绍React Router、Redux和MobX 3个React技术栈的重要成员,并通过实战项目讲解这些技术如何和React结合使用。
  • 我的简评:这本书适合初级的React开发者,书名虽是进阶,实际上就是一本入门的书。书中对一些React用法总结的还挺不错,实践性比较强。
  • !!文末有pdf书籍、笔记思维导图、随书代码打包下载地址,需要请自取!阅读[书籍精读系列]所有文章,请移步:推荐收藏-JavaScript书籍精读笔记系列导航

1.初识React

1.1.简介

  • 一句话:通过引入虚拟DOM、状态、单项数据流等设计理念,形成以组件为核心,用组件搭建UI的开发模式,理顺了UI的开发过程,完美的将数据、组件状态和UI映射到一起,极大地提高了开发大型Web应用的效率

1.2.特点

  • 声明式的视图层、简单的更新流程、灵活的渲染实现、高效的DOM操作

2.React基础

2.1.Jsx

  • 简介:一种用于描述UI的JavaScript扩展语法
  • Jsx语法:基本语法、标签类型、JavaScript表达式、标签属性、注释
  • Jsx不是必需的

2.2.组件

  • 定义:组件将应用的UI拆分成独立的、可复用的模块
  • props:用于把父组件中的数据或方法传递给子组件,供子组件使用
  • state:组件内部的状态,state的变化最终将反映到组件UI的变化上
  • props和state比较:props是组件对外的接口,组件通过props接收外部传入的数据(包括方法);state是组件对内的接口,组件内部状态的变化通过state来反映。另外,props是只读的,state是可变的
  • 有状态组件和无状态组件:用不到state就称为无状态组件
  • 属性校验和默认属性:PropTypes和defaultProps
  • 组件样式:外部和内联
  • 组件和元素:React元素是一个普通的JavaScript对象,React组件是一个class或函数

2.3.组件的生命周期

  • 挂载阶段:constructor、componentWillMount、render、componentDidMount
  • 更新阶段:componentWillReceiveProps(nextProps)、shouldComponentUpdate(nextProps, nextState)、componentWillUpdate(nextProps, nextState)、render、componentDidUpdate(prevProps, prevState)
  • 卸载阶段:componentWillUnmount

2.4.列表和Keys

  • 使用key属性来标记列表中的每个元素,通过key知道哪些元素发生了变化,只渲染发生变化的元素,提高渲染效率

2.5.事件处理

  • 合成事件,并不是原生的DOM事件
  • 1.使用箭头函数
  • 2.使用组件方法
  • 3.属性初始化语法

2.6.表单

  • 受控组件:表单元素的值是由React来管理的(input文本框、select列表、checkbox复选框和radiobox单选框)
  • 非受控组件:表单元素的状态依然由表单元素自己管理

3.React16新特性

  • 1.render新的返回类型:数组和字符串
  • 2.错误处理:错误边界componentDidCatch
  • 3.Portals:任意组件都可以将弹窗组件渲染到根节点上,以方便弹窗的显示
  • 4.自定义DOM属性:之前会忽略不识别的HTML和SVG属性,现在React会把不识别的属性传递给DOM元素

4.深入理解组件

4.1.组件state

  • 设计合适的state
  • 正确修改state:不能直接修改state;state的更新是异步的;state的更新是一个合并的过程;
  • state与不可变对象:状态的类型是不可变类型;状态的类型是数组;状态的类型是普通对象(不包含字符串、数组);

4.2.组件与服务器通信

  • 挂载阶段通信:componentDidMount是执行组件与服务器通信的最佳地方
  • 更新阶段通信

4.3.组件间通信

  • 父子组件通信:父向子:通过父组件向子组件的props传递数据完成;子向父:通过父组件向子组件传递的回调方法;
  • 兄弟组件通信:通过状态提升的方式实现;即把组件之间需要共享的状态保存到距离他们最近的共同父组件内,任意一个兄弟组件都可以通过父组件传递的回调函数来修改共享状态,父组件中共享状态的变化也会通过props向下传递给所有兄弟组件,从而完成兄弟组件之间的通信;
  • 组件层级太深时使用Context:创建context的方式是:在提供context的组件内新增一个getChildContext方法,返回context对象,然后在组件的childContextTypes属性上定义context对象的属性的类型信息
  • 延伸:使用消息队列实现组件通信:改变数据的组件发起一个消息,使用数据的组件监听这个消息,并在响应函数中触发setState来改变组件状态

4.4.特殊的ref

  • 在DOM元素上使用ref:ref接收一个回调函数作为值,在组件被挂载或卸载时,回调函数会被调用。在组件被挂载时,回调函数会接收当前dom元素作为参数,在组件被卸载时,回调函数会接收null作为参数
  • 在组件上使用ref:此时ref的回调函数接收的参数是当前组件的实例,提供在组件外部操作组件的方式
  • 父组件访问子组件的DOM节点:需要在父组件获取子组件的某个DOM元素

5.虚拟DOM和性能优化

5.1.虚拟DOM

  • 虚拟DOM使用普通的JavaScript对象描述DOM元素

5.2.Diff算法

  • 原理:通过比较两次虚拟DOM结构的变化找出差异部分,更新到真实DOM上,从而减少对真实DOM上执行的操作,提高程序执行效率
  • 几种情况:1.当根节点是不同类型时;2.当根节点是相同的DOM元素类型时;3.当根节点是相同的组件类型时;

5.3.性能优化

  • 1.使用生产环境版本的库
  • 2.避免不必要的组件渲染
  • 3.使用key

5.4.性能检测工具

  • React Developer Tools for Chrome
  • Chrome Performance Tab

6.高阶组件

6.1.基本概念

  • 接收React组件作为参数,并且返回一个新的React组件。实现方式本质上是装饰者模式

6.2.使用场景

  • 1.操作props
  • 2.通过ref访问组件实例
  • 3.组件状态提升
  • 4.用其他元素包装组件

6.4.继承实现高阶组件

  • 属性代理实现高阶组件:由高阶组件处理通用逻辑,然后将相关属性传递给被包装组件
  • 还可以通过继承方式实现高阶组件,通过继承被包装组件实现逻辑的复用

6.5.注意事项

  • 1.为更好地区别包装了不同组件的高阶组件,需要对高阶组件的显示名称做自定义处理
  • 2.不要在组件的render方法中使用高阶组件,尽量也不要在组件的其他生命周期方法中使用高阶组件
  • 3.如果需要使用被包装组件的静态方法,必须手动复制这些静态方法
  • 4.refs不会被传递给被包装组件
  • 5.与父组件的区别

7.路由:用React Router开发单页面应用

  • 7.1.前端路由的实现方式
  • 7.2.Router对象
  • 7.3.Route是React Router中用于配置路由信息的组件
  • 7.4.Link链接组件

8.Redux:可预测的状态管理机

8.1.三大原则

  • 1.唯一数据源
  • 2.保持应用状态只读
  • 3.应用状态的改变通过纯函数完成

8.2.主要组成

  • 1.action:信息的载体
  • 2.reducer:描述应用发生了什么操作
  • 3.store:是action和reducer之间的桥梁

8.3.store负责的工作

  • 1.保存应用状态
  • 2.通过方法getState()访问应用状态
  • 3.通过方法dispatch(action)发送更新状态的意图
  • 4.通过方法subscribe(listener)注册监听函数、监听应用状态的改变

9.Redux实战

9.1.组织项目结构

  • 目前主流的方案有三种:按照类型、按照页面功能和Ducks
  • 按照类型:就是将充当component、container、action、reducer等不同角色的文件分别放在不同的文件夹下。问题:项目规模较大时,非常不方便
  • 按照页面功能:就是将一个页面功能使用到的组件、状态和行为都在同一个文件夹下。优点:方便开发、易于功能的扩展。问题:需要频繁在reducer、action、action type等不同文件间切换
  • Ducks:提倡将相关联的reducer、action types和action creators写到一个文件里。本质上是以应用的状态作为划分模块的依据,而不是以界面功能作为划分模块的依据。

9.2.设计state

  • 错误1:以API作为设计的state的依据
  • 错误2:以页面UI作为设计state的依据
  • 合理设计state:最重要的是记住一句话:像设计数据库一样设计state;类似设计数据库总结的三个原则(1.把整个应用的状态按照领域分为若干个子状态,子状态之间不能保存重复的数据;2.state以键值对的结构存储数据,以记录的key或ID作为记录的索引,记录中的其他字段都依赖于索引;3.state中不能保存可以通过state中的已有字段计算而来的数据,即state中的字段不互相依赖;)

9.3.设计模块

  • app模块、auth模块、posts模块、comments模块、users模块、ui模块、index模块

9.4.连接Redux

  • 注入state
  • 注入action creators
  • connecte连接PostList和Redux

9.5.Redux调试工具

  • Redux DevTools
  • 一款用于调试Redux应用的浏览器插件,可以实时的显示当前应用的state信息、action触发的记录以及state的变化

9.6.性能优化

  • React Router引起的组件重复渲染问题
  • Immutable.js:Redux的state必须是不可变对象,reducer中每次返回的state都是一个新对象;Immutable.js的作用在于以更高效的方式创建不可变对象;主要3个优点(保证数据的不可变、丰富的API、优异的性能)
  • Reselect:Redux state的任意改变都会导致所有容器组件的mapStateToProps的重新调用,进而导致使用到的selectors重新计算;Reselect可以创建具有记忆功能的selectors,当selectors计算使用的参数未发生变化时,不会再次计算,而是直接使用上次缓存的计算结果;

写在后面

2018-10-01 22:37:02 feng_zhiyu 阅读数 1675

本博客pdf链接: https://pan.baidu.com/s/1E_E4RqZUPgQiby1T7Afj8Q 提取码: h77d

React进阶之路系列学习笔记:React进阶之路系列学习笔记

github源码:React_learning

2.1 JSX

2.1.1JSX简介

    JSX是一种用于描述UI的JavaScript扩展语法,React使用这种语法描述组件的UI。

2.1.2JSX语法

1.基本语法

JSX的基本语法和XML语法相同,都是使用成对的标签构成一个树状结构的数据。

const element = (
   
<div>
        <h1>
Hello, world!</h1>
    </div>

)

2.标签类型

两种标签类型:

(1)DOM类型的标签(div,span等), 使用时,标签首字母必须小写

(2)React组件类型的标签,使用时,组件名称的首字母必须大写

React通过首字母的大小写判断渲染的是DOM类型标签还是React组件类型标签。

3.JavaScript表达式

JSX的本质是JavaScript。在JSX中使用JSX表达式需要用{}将表达式包起来。

表达式使用主要场景:

  1. 通过表达式给标签属性赋值
const element = <MyComponent foo={ 1 + 2 }/>
  1. 通过表达式定义子组件

 

const todos = ['item1', 'item2', 'item3'];

const element = {

    <ul>

        {todos.map(message => <Item key={message} message={message} />)}

    </ul>

};

=》是ES6中定义函数的方式。map是一种数组遍历方式,message是箭头函数的参数,key是索引,message是对应todos的值。这里将数组todos依次遍历在HTML代码中展示。

另外,JSX中只能使用JS表达式,不能使用多行JS语句。JSX可以使用三目运算符或者逻辑与(&&)运算符代替if语句。

4.标签属性

1.当JSX标签是DOM类型的标签时,对应的DOM标签支持的属性JSX也支持,部分属性名称有所变化,例如class写成className,之所以改变是因为React对DOM标签支持的事件重新做了封装。事件属性名称采用驼峰格式。

2.当JSX标签是React组件类型时,可以自定义标签的属性名。

5.注释

JSX的注释需要大括号”{}”将/**/包裹起来。

2.1.3 JSX是必需的

在《React进阶之路》一书中提到JSX不是必需的。

2.2 组件

2.2.1组件定义

定义组件的两种方式:使用ES6 class(类组件)和使用函数(函数组件)。

使用class定义组件满足的两个条件:

  1. class继承自React.Component
  2. class内部必须自定义render方法,render方法返回代表该组件UI的React元素。

使用ReactDom.render()将PostList挂载到页面的DOM节点上,在使用ReactDOM.render()需要先导入react-dom库,这个库会完成组件所代表的虚拟DOM节点到浏览器的DOM节点的转换。

import React from "react";

import ReactDOM from "react-dom";

import PostList from "./PostList";



ReactDOM.render(<PostList />, document.getElementById("root"));

 

2.2.2组件的props

组件的props用于把父组件中的数据或方法传递给子组件,供子组件使用。

Props是一个简单结构的对象,它包含的属性正是由组件作为JSX标签使用时的属性组成。 下面是一个使用User组件作为JSX作为JSX标签的声明:

<User name='React' age='4' address='America' >



props = {

    name: 'React',

    age: '4',

    address: 'America'

}

 

  {data.map(item =>

    <PostItem

      title={item.title}

      date={item.date}

      author={item.author}

   />

  )}

这是类组件PostList中使用PostItem的代码,对data数组中的元素遍历一遍,然后返回大小相同的数组(其中包含了title,author,date属性),最终在UI展示。

2.2.3组件的state

组件的state是组件内部的状态,state的变化最终将反映在组件UI的变化上。在构造方法constructor中通过this.state定义组件的初始状态,并调用this.setState方法改变组件的状态。

constructor(props){

    super(props);

    this.state = {

        vote: 0

    };

}

///处理点赞逻辑

handleClick(){

    let vote = this.state.vote;

    vote++;

    this.setState({

        vote:vote

    });

}
  1. constructor内首先调用super(props),这一步实际上是调用React.Component这个class的constructor方法来完成React组件的初始化;
  2. constructor通过this.state定义组件的初始状态;
  3. render方法中定义了处理点击事件的响应函数,响应函数内部会调用this.setState更新组件点赞数。

UI = Component(props,state)

React组件是通过props和state两中数据驱动渲染出组件UI。Props是组件对外的接口,它是只读的,组件通过props接受外部传入的数据;state是组件对内的接口,它是可变的,组件内部状态通过state来反映。

2.2.4有状态组件和无状态组件

state是用来反映组件内部状态的变化,如果一个组件的内部状态是不变的,这样的组件称为无状态组件;反之称为有状态组件。

    定义无状态组件除了使用ES6 class方式外,还可以用函数定义。一个函数组件接收props作为参数,返回代表这个组件UI的React元素结构。

function Welcome(props){

    return <h1>Hello, {props.name}</h1>;

}

    有状态组件主要关注状态业务变化的业务逻辑,无状态组件主要关注组件UI的渲染。

2.2.5属性校验和默认属性

React提供了ProTypes这个对象,用于校验组件属性的类型。ProTypes包含组件属性所有可能的类型,通过定义一个对象(对象的key是组件属性名,val是对应属性类型)实现组件属性类型的校验。

想要知道一个对象的结构或数据元素的类型,比较好的做法是使用ProTypes.shape或Protypes.arrayof。 如果属性是组件的必需属性,也就是使用某个组件时必须传入的属性,就要爱ProTypes的类型属性上调用isRequired。

PostItem.propTypes = {

  post: PropTypes.shape({

    id: PropTypes.number,

    title: PropTypes.string,

    author: PropTypes.string,

    date: PropTypes.string,

    vote: PropTypes.number

  }).isRequired,

  onVote: PropTypes.func.isRequired

}

    如上面代码,在PostItem组件中post和onVote是必需属性。

React还提供了为组件属性指定默认值的特性,这个特性通过组件的defaultProps实现。

2.2.6组件样式

1. 外部CSS样式表【首选】

    此方式和平时开发Web应用时使用外部CSS文件相同,CSS样式表中根据HTML标签类型、ID、class等选择器定义元素样式。唯一区别:React要用className代替class作为选择器。

样式表的引入方式:

  1. 使用组件的HTML页面中通过标签引入
  2. 把样式表文件当做一个模块,在使用该样式表的组件中,像导入其他组件一样导入样式表文件

注意class名称冲突

2. 内联样式

2.2.7组件和元素

React元素是一个普通的JS对象,这个对象通过DOM节点或React组件描述界面是什么样子的。JSX语法就是用来创建React元素的。

<div className='foo'>

    <Button color='blue'>

        OK

    </Button>

</div>

Button是一个自定义的React组件。

React组件是一个class或函数,它接收一些属性作为输入,返回一个React元素。React组件是由若干元素组件而成。下面的例子可以解释React组件与React元素的关系:

class Button extends React.Component{

    render(){

        return (<button>OK</button>);

    }

}



//在JSX中使用组件Button,button表示组件Button的一个react元素

const button = <Button />;

//在组件中使用React元素button

class Page extends React.Component{

    render(){

        return (

            <div>

                {button}

            </div>

        );

    }

}

 

2.3 组件的生命周期

2.3.1挂载阶段

这个阶段组件被创建,执行初始化,并被挂载到DOM中,完成组件的第一次渲染。依次调用的生命周期方法有:constructor,componentWillMount,render,componentDidMount。

  1. constructor

这是ES6class的构造方法,组件被创建时,会先调用组件的构造方法。如果父组件没有传入属性而组件自身定义了默认属性,那么参数props指向的是组件的默认属性。Constructo通常用于初始化组件的state以及绑定时间处理方法等工作。

  1. componentWillMount

这个方法在组件被挂载到DOM前调用,且只会被调用一次。在实际项目中很少会用到,因为可以将该方法提前到constructor中执行。在这个方法中调用this.setState不会引起组件的重新渲染。

  1. render

这是定义组件时唯一必要的方法(组件的其他生命周期方法都可以省略)。在这个方法中,根据组件的props和state返回一个React元素,用于描述组件的UI,通常React元素使用JSX语法定义。注意:render并不负责组件的实际渲染工作,它只是返回一个UI的描述,真正的渲染出页面DOM的工作由React自身负责。Render是一个纯函数,在这个方法中不能执行任何有副作用的操作。

  1. componentDidMount

在组件被挂载到DOM后调用,且只会被调用一次。这时候已经可以获取到DOM结构了。这个方法通常还会用于向服务器端请求数据。在这个方法中调用this.setState会引起组件的重新渲染。

2.3.2更新阶段

组件被挂载到DOM后,组件的props或state可以引起组件更新。props引起的组件更新,本质上是由渲染该组件的父组件引起的,也就是父组件的render方法被调用时,组件会发生更新过程,这个过程无论props是否改变,父组件render方法每一次调用,都会导致组件更新。State引起的组件更新,是通过调用this.setState修改组件 state触发的。组件更新阶段依次调用的声明周期方法有:compoentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render和componentDidUpdate。

  1. compoentWillReceiveProps(nextProps)

这个方法只有在props引起的组件更新过程中,才会被调用。State引起的组件更新并不会触发该方法的执行。方法的参数nextProps是父组件传递给当前组件的新的props。因此往往需要比较nextProps和this.props来决定是否执行props发生变化后的逻辑,比如根据新的props调用this.setState触发组件的重新渲染。

  1. shouldComponentUpdate(nextProps,nextState)

这个方法决定组件是否继续执行更新过程。方法返回true时,组件继续更新过程;返回false时,更新过程停止。后续的方法也不会再被调用给你。一般通过比较nextProps、nextState和当前组件的props、state决定这个方法的返回结果。这个方法可以用来减少组价不必要的渲染从而优化组件性能。

  1. componentWillUpdate(nextProps,nextState)

这个方法在组件render调用前执行,可以作为组件更新发生前执行,某些工作的地方,一般也很少用到。

  1. componentDidUpdate(prevProps,prevState)

组件更新后被调用,可以作为操作更新下后DOM的地方。这个方法的两个参数分别代表组件更新前的props和state。

2.3.3卸载阶段

组件从DOM中被卸载的过程,这个过程只有一个生命周期方法:componentWillUnMount。 这个方法在组件被卸载之前调用,可以在这里执行一些清理工作,比如清理组件中使用的定时器等等,避免引起内存泄露。

最后注意:只有类组件具有生命周期方法,函数组件是没有生命周期方法的

2.4 列表和keys

①当列表数据为多组时,调用数据运行时为提示警告(浏览器F12->Console):” Each child in an array or iterator should have a unique ‘ key ’ prop ”  说明如果没有给每条数据添加一个’ key ‘ 的话,当数据组数过多时,不同组数有相同的数据,调用就可能会出现错误。因此这里的’ key ’ 就相当于数据库里的主键,给每组数据调取都有一个参照。

<div>

  <h2>帖子列表</h2>

  <ul>

    {this.state.posts.map(item =>

      <PostItem

        key = {item.id}

        post = {item}

        onVote = {this.handleVote}

      />

    )}

  </ul>

</div>

②虽然列表元素的key不能重复,但这个唯一性仅限至于在当前列表中,而不是全局唯一。

③不推荐使用索引作为key,因为一旦列表中的数据发生重排,数据的索引也会发生变化,不利于React的渲染优化。

2.5 事件处理

1.React元素绑定事件有两点注意事项:

①在React中,事件的命名采用驼峰的形式(两个或多个单词组成的事件第一个单词首字母小写,其余的都要大写),而不是DOM元素中的小写字母命名方式。如:JavaScript里的onclick要改为onClick、onchange要改为onChange。

②处理事件的响应函数要以对象的形式赋值给事件属性,而不是DOM中的字符串形式。

如:在DOM中绑定一个点击事件是这样(传统的js写法):

<button onclick="clickButton()">

    Click

</button>

而在React元素中绑定一个点击事件变成这种形式:

class App extends Component {

    clickButton(){}; //clickButton函数

    render() {

        return (

            <div>

                <button 
                 onclick={ this.clickButton }> //clickButton是一个函数

                    Click

                </button>

            </div>

        )

    }

}

2.React事件处理函数的三种写法:
    ①使用箭头函数(以下几种情况不使用箭头函数

https://www.jianshu.com/p/1357112985ef  )

class ...... {

    constructor(props){

        super(props);

        this.state = { number:0 }

    }

    ......

    <button onClick={(event)=>{console.log(this.state.number)}>Click</button>

}

②使用组件方法(直接将组件的方法赋值给元素的事件属性,同时在类的构造中,将这个方法的this绑定到当前对象)

class ...... {

    constructor(props){

        super(props);

        this.state = { number:0 };

        this.handleClick = this.handleClick.bind(this);

    }

    handleClick(event){

    const number = ++this.state.number;

    this.setState({

        number:number;

    })

    }

}

    ......

    <div>{ this.state.number }</div>

    <button onClick={ this.handleClick }>Click</button>

}

③属性初始化语法(property initializer syntax) (使用ES7的property initializers会自动为class中定义的方法绑定this)

handleClick = (event) => {

    const number = ++this.state.number;

    this.setState({

        number:number

    })

}

三种方法进行比较:第一种每次调用render时,会重新创建一个新的事件处理函数,带来额外的性能开销;第二种构造函数中需要为事件处理函数绑定this,多个处理事件时,代码就会显得繁琐;第三种为推荐使用方法,这种方式既不需要在构造函数中手动绑定this,也不需要担心组件重复渲染导致的函数重复创建问题。

2.6表单

2.6.1 受控组件

如果一个表单元素的值是由React来管理的,那它就是一个受控组件对于不同的表单元素,React的控制方式略有不同。下面展示一下三种长用的表单元素的控制方式。

1.文本框 文本框包含类型为text的input元素和textarea元素。它们受控的主要原理是:通过表单元素的value属性设置表单元素的值,通过表单元素的o'nChange事件监听值的变化,并将变化同步到React组件的state中。

import React,{Component} from 'react';

export default class Demo1 extends Component{

    constructor(props){

        super(props);

        this.state = {name:'',password:''};

        handleChange(event){ //监听用户名和密码两个input值的变化

            const target = event.target;

            this.setState({[target.name]: target.value});

        }

        handleSubmit(event){ //表单提交的响应函数

            console.log('login successfully');

            event.preventDefault();

        }

    }

    render(){

        return(

            <form onSubmit={ this.handleSubmit }>

                <label>

                    用户名:<input type="text" name="name" value={this.state.name} onChange={this.handleChange}/>

                </label>

                <label>

                    密码:<input type="password" name="password" value={this.state.password} onChange={this.handleChange}/>

                </label>

                <input type="submit" value="登陆"/>

            </form>

        )

    }

}

用户名和密码两个表单元素的值是从组件的state中获取的,当用户更改表单元素的值时,onChange事件会被触发,对应的handleChange处理函数会把变化同步到组件的state,新的state又会触发表单元素重新渲染,从而实现表单元素状态的控制。

  1. 列表 列表select元素时最复杂的表单元素,他可以用来创建一个下拉表
    在React中,通过在select上定义value属性来决定哪一个option元素处于选中状态。这样,对select的控制只需要在select这一个元素上修改即可,不需要再关注option元素
  2. 复选框和单选框  复选框的类型为checkbox的input元素,单选框为radio的input元素。复和单选框的值时不变的,需要改变的是他们的checked状态。React控制的属性变为checked
     

 

 

P48 代码类图及关系的描述

 

PostItem和PostList都继承于Component,两个类之间相互关联
PostList渲染的是

{this.state.posts.map(item => 

    <PostItem key = {item.id} post= {item} onVote={this.handleVote} onSave={this.handleSave} /> )}


onVote这里用到自己定义的方法handleVote(id),根据帖子的id进行过滤,找到待修改vote属性的帖子,返回新的posts对象,然后更新state。
onSave这里用到自己定义的方法handleSave(post),但用到PostItem中的属性prop,根据post的id,过滤出当前要更新的post
PostItem渲染的是标题(通过handleTitleChange方法处理<textarea>值的变化)
点赞图片实现函数绑在了onClick(即handleVote) handleEditPost来处理保存编辑按钮点击后的逻辑

 

2.6.2 非受控组件

非受控组件看似简化了操作表单元素的过程,但这种方式破坏了React对组件状态管理的一致性,往往出现不容易排查的问题,因此不建议使用。

2.7 本章小结

本章详细介绍了React的主要特性及其用法。React通过JSX语法声明界面UI,将界面UI和它的逻辑封装在同一个JS文件中。组件是React的核心,根据组件的外部接口props和内部接口state完成自身的渲染。使用组件需要理解它的生命周期,借助不同的生命周期方法,组件可以实现复杂逻辑。渲染时,注意key的使用,事件处理时,注意事件名称和事件处理函数的写法。最后介绍表单元素的用法,使用方式分受控组价和非受控组件。另外,在工程项目中,注意有状态组件和无状态组件的划分从而妥善运用props和state。

 

 

React进阶

阅读数 326

react进阶

阅读数 232