微服务框架相关技术整理

微服务整体框架

  • 开发前后台分离:前台与后台之间,通过 Restful 风格接口通信(HTTP协议)
  • 内部服务: Dubbo ( RPC框架)
  • 外部服务: SpringCloud Zuul (提供Restful API接口)
     

    锦江网站制作公司哪家好,找创新互联建站!从网页设计、网站建设、微信开发、APP开发、响应式网站等网站项目制作,到程序开发,运营维护。创新互联建站2013年至今到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联建站

  • 微服务应用开发  

API Gateway

  • API Gateway :网关,统一应用请求接口.API 网关在微服务们的最前端,让 API 网关变成由应用所发起的每个请求的入口,简化客户端实现和微服务应用程序间的沟通方式。

API Gateway两种方式:

  • 单节点API Gateway
     

  • BFF (Backends for frontends) Gateway
     

API Gateway的作用

  • 请求路由,版本控制: API Gateway 是微服务的入口,可以根据不同的请求路由到不同的服务上. 也可以进行路由的版本控制,这样即使后服务发生了变化,Gateway 的路径依然可以不改变
  • 用户登录,权限认证: 客户端在与我们后端服务进行交互之前,由API Gateway先进行登录鉴权操作,这是后端所有的服务都需要有的共有逻辑
  • 数据聚合: 由于不同的客户端往往需要的数据完全不同,而这些数据又是不同的 service 提供的,可以借助 Gateway 方便完成来自不同 service 的数据聚合
  • 协议转换: 在项目实践中,CS(Client to Server)协议和SS(Server to Server)协议是不一样的,为了保证数据传输的可靠性,CS协议会有鉴权以及加密解密的逻辑,而在内部的SS协议则不需要这些逻辑,因此在 Gateway 我们需要有一个协议转换的过程
  • 熔断,降级,限流: 通过API Gateway可以在监测到某个服务发生异常,或者当服务的流量超过服务的承载能力等情况时,可以采取相应的措施. 提高整个系统的容错性、稳定性
  • 负载均衡: API Gateway知道所有服务实例的地址,可以根据不同服务采取不同的负载均衡策略
  • 灰度发布: 灰度发布允许直接只导入指定量的流量请求到新的版本

API Gateway的架构

  • 多网关集群(Backends for frontends): 针对不同的客户端,都有相应的网关层来接入.功能主要有:用户登录,鉴权,服务发现注册,协议转换,接口版本控制等以及监控,APM调用链,日志,流控策略等
  • 聚合服务(Merge Service): 在某些客户端的需求中,需要从多个服务拉取数据,为了减少客户端的复杂度,以及加快客户端的访问速度,可以加一个聚合层,用来做聚合查询,在某些接口中可以把多个服务的数据一次性返回给客户端
  • 仪表盘管理端(Dashboard): Dashboard 提供可视化的分析平台,包括服务的管理,监控数据报警配置,日志查询,灰度发布操作,API文档管理等

Eureka(服务发现框架)

  • Eureka是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的. SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能

Eureka的两个组件

  • Eureka Server: Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中看到. Eureka Server之间通过复制的方式完成数据的同步
  • Eureka Client: 是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器
  • Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性
    • 在应用启动后,将会向Eureka Server发送心跳, 如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除。
    • Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性

RPC框架

RPC定义

  • RPC(Remote Procedure Call Protocol): 远程过程调用协议,一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.也就是

客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样

  • RPC是协议: 协议就是一套规范,目前典型的RPC实现包括:Dubbo,Thrift,GRPC,Hetty等.从目前技术的发展趋势来看,实现了RPC协议的应用工具往往都会附加其他重要功能
  • 网络协议和网络IO模型对其透明: 既然RPC的客户端认为自己是在调用本地对象。那么传输层使用的是TCP/UDP还是HTTP协议,又或者是一些其他的网络协议它就不需要关心了。既然网络协议对其透明,那么调用过程中,使用的是哪一种网络IO模型调用者也不需要关心
  • 信息格式对其透明: 我们知道在本地应用程序中,对于某个对象的调用需要传递一些参数,并且会返回一个调用结果。至于被调用的对象内部是如何使用这些参数,并计算出处理结果的,调用方是不需要关心的。那么对于远程调用来说,这些参数会以某种信息格式传递给网络上的另外一台计算机,这个信息格式是怎样构成的,调用方是不需要关心的
  • 应该有跨语言能力: 调用方实际上也不清楚远程服务器的应用程序是使用什么语言运行的。那么对于调用方来说,无论服务器方使用的是什么语言,本次调用都应该成功,并且返回值也应该按照调用方程序语言所能理解的形式进行描述
     

RPC主要组成部分

  • Client: RPC协议的调用方.最理想的情况是RPC Client在完全不知道有RPC框架存在的情况下发起对远程服务的调用.但实际情况来说Client或多或少的都需要指定RPC框架的一些细节
  • Server: 在RPC规范中,这个Server并不是提供RPC服务器IP,端口监听的模块。而是 远程服务方法的具体实现(在JAVA中就是RPC服务接口的具体实现) .其中的代码是最普通的和业务相关的代码,甚至其接口实现类本身都不知道将被某一个RPC远程客户端调用
  • Stub/Proxy: RPC代理存在于客户端,因为要实现客户端对RPC框架“透明”调用,那么客户端不可能自行去管理消息格式、不可能自己去管理网络传输协议,也不可能自己去判断调用过程是否有异常。这一切工作在客户端都是交给RPC框架中的“代理”层来处理的
  • Message Protocol: 一次完整的client-server的交互肯定是携带某种两端都能识别的,共同约定的消息格式. RPC的消息管理层专门对网络传输所承载的消息信息进行编码和解码操作 .目前流行的技术趋势是不同的RPC实现,为了加强自身框架的效率都有一套(或者几套)私有的消息格式
  • Transfer/Network Protocol: 传输协议层负责管理RPC框架所使用的网络协议,网络IO模型. 传输层还需要统一RPC客户端和RPC服务端所使用的IO模型
  • Selector/Processor: 存在于RPC服务端,用于服务器端某一个RPC接口的实现的特性(它并不知道自己是一个将要被RPC提供给第三方系统调用的服务).所以在RPC框架中应该有一种 " 负责执行RPC接口实现 " 的角色.包括: 管理RPC接口的注册,判断客户端的请求权限,控制接口实现类的执行在内
  • IDL: IDL(接口定义语言)并不是RPC实现中所必须的.但是需要跨语言的RPC框架一定会有IDL部分的存在.这是因为要找到一个 各种语言能够理解的消息结构、接口定义的描述形式 . 如果RPC实现没有考虑跨语言性,那么IDL部分就不需要包括 ,例如JAVA RMI因为就是为了在JAVA语言间进行使用,所以JAVA RMI就没有相应的IDL

不同的RPC框架实现都有一定设计差异。例如生成Stub的方式不一样,IDL描述语言不一样、服务注册的管理方式不一样、运行服务实现的方式不一样、采用的消息格式封装不一样、采用的网络协议不一样。但是基本的思路都是一样的,上图中的所列出的要素也都是具有的

影响RPC框架性能的因素

  • 使用的网络IO模型: RPC服务器可以只支持传统的阻塞式同步IO,也可以做一些改进让RPC服务器支持非阻塞式同步IO,或者在服务器上实现对多路IO模型的支持.这样的RPC服务器的性能在高并发状态下,会有很大的差别.特别是单位处理性能下对内存,CPU资源的使用率
  • 基于的网络协议: 一般来说可以选择让RPC使用应用层协议,例如HTTP或者HTTP/2协议,或者使用TCP协议.让RPC框架工作在传输层.工作在哪一层网络上会对RPC框架的工作性能产生一定的影响,但是对RPC最终的性能影响并不大.但是至少从各种主流的RPC实现来看,没有采用UDP协议做为主要的传输协议的
  • 消息封装格式: 选择或者定义一种消息格式的封装,要考虑的问题包括: 消息的易读性,描述单位内容时的消息体大小,编码难度,解码难度,解决半包/粘包问题的难易度. 当然如果您只是想定义一种RPC专用的消息格式,那么消息的易读性可能不是最需要考虑的.消息封装格式的设计是目前各种RPC框架性能差异的最重要原因,这就是为什么几乎所有主流的RPC框架都会设计私有的消息封装格式的原因. dubbo 中消息体数据包含 dubbo版本号,接口名称,接口版本,方法名称,参数类型列表,参数,附加信息
  • 序列化和反序列化(Schema & Data Serialization): 序列化和反序列化,是对象到二进制数据的转换,程序是可以理解对象的,对象一般含有 schema 或者结构,基于这些语义来做特定的业务逻辑处理.

序列化框架一般会关注以下几点:

  1. Encoding format:是human readable(是否能直观看懂 json)还是binary(二进制)
  2. Schema declaration:也叫作契约声明,基于IDL,比如 Protocol Buffers/Thrift.还是自描述的,比如 JSON、XML.另外还需要看是否是强类型的
  3. 语言平台的中立性:比如Java的Native Serialization就只能自己玩,而Protocol Buffers可以跨各种语言和平台
  4. 新老契约的兼容性:比如IDL加了一个字段,老数据是否还可以反序列化成。
  5. 和压缩算法的契合度 :运行benchmark(基准)和实际应用都会结合各种压缩算法,例如gzip,snappy
  6. 性能 :这是最重要的,序列化,反序列化的时间,序列化后数据的字节大小是考察重点。
  7. 序列化方式非常多,常见的有Protocol Buffers,Avro,Thrift,XML,JSON,MessagePack,Kyro,Hessian,Protostuff,Java Native Serialize,FST
  • 实现的服务处理管理方式: 在高并发请求下,如何管理注册的服务也是一个性能影响点.可以让RPC的Selector/Processor使用单个线程运行服务的具体实现(这意味着上一个客户端的请求没有处理完,下一个客户端的请求就需要等待). 也可以为每一个RPC具体服务的实现开启一个独立的线程运行(可以一次处理多个请求,但是操作系统对于“可运行的最大线程数”是有限制的). 也可以线程池来运行RPC具体的服务实现(目前看来,在单个服务节点的情况下,这种方式是比较好的). 还可以通过注册代理的方式让多个服务节点来运行具体的RPC服务实现

工业界的 RPC 框架

  • 国内
    • Dubbo: 来自阿里巴巴 http://dubbo.I/O/
    • Motan: 新浪微博自用 https://github.com/weibocom/motan
    • Dubbox: 当当基于 dubbo 的 https://github.com/dangdangdotcom/dubbox
    • rpcx: 基于 Golang 的 https://github.com/smallnest/rpcx
  • 国外
    • Thrift from facebook: https://thrift.apache.org
    • Avro from hadoop: https://avro.apache.org
    • Finagle by twitter: https://twitter.github.I/O/finagle
    • gRPC by Google: http://www.grpc.I/O (Google inside use Stuppy)
    • Hessian from cuacho: http://hessian.caucho.com
    • Coral Service inside amazon: not open sourced

如何选择RPC框架

选择一个rpc框架会基于多方面的考虑: 框架特性、性能、成熟度、技术支持、社区活跃度等 多个方面.最重要一点,这也是往往很多技术人员进入的误区, "对于技术,不要为了使用而使用, 用最简单合适的技术实现解决问题才是正道 " . 架构是服务于业务的,能快速方便的满足业务需求的架构才是好的架构 .没有最好的,只有适合自己的

Dubbo

  • Dubbo是一个开源分布式服务框架,阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成.
  • Dubbo是一款高性能,轻量级的开源Java RPC框架,它提供了三大核心能力: 面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现

核心组件

  • Remoting: 网络通信框架,实现了 sync-over-async 和 request-response 消息机制
  • RPC: 一个远程过程调用的抽象.支持负载均衡,容灾和集群功能
  • Registry: 服务目录框架,用于服务的注册和服务事件发布和订阅

工作原理

Provider:暴露服务方称之为“服务提供者”

Consumer:调用远程服务方称之为“服务消费者”

Registry:服务注册与发现的中心目录服务称之为“服务注册中心”

Monitor:统计服务的调用次数和调用时间的日志服务称之为“服务监控中心”

连通性:

注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小

监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示

服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销

服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销

注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外

注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者

注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表

注册中心和监控中心都是可选的,服务消费者可以直连服务提供者

健壮性:

监控中心宕掉不影响使用,只是丢失部分采样数据

数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

注册中心对等集群,任意一台宕掉后,将自动切换到另一台

注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

服务提供者无状态,任意一台宕掉后,不影响使用

服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

伸缩性:

注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心

服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者

Dubbo特性

  • 面向接口代理的高性能RPC调用: 提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节
  • 智能负载均衡: 内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量
  • 服务自动注册与发现: 支持多种注册中心服务,服务实例上下线实时感知
  • 高度可扩展能力: 遵循微内核+插件的设计原则,所有核心能力如 Protocol,Transport,Serialization 被设计为扩展点,平等对待内置实现和第三方实现
  • 运行期流量调度: 内置条件,脚本等路由策略.通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能
  • 可视化的服务治理与运维: 提供丰富服务治理,运维工具:随时查询服务元数据,服务健康状态及调用统计,实时下发路由策略,调整配置参数

使用示例

Zuul

  • Zuul是netflix开源的一个API Gateway 服务器, 本质上是一个web servlet应用

    Zuul 是一个基于JVM路由和服务端的负载均衡器,提供动态路由,监控,弹性,安全等边缘服务的框架,相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门

Zuul工作原理

  • 过滤器机制
    • Zuul提供了一个框架,可以对过滤器进行动态的加载,编译,运行
    1.Zuul的过滤器之间没有直接的相互通信,他们之间通过一个RequestContext的静态类来进行数据传递的。RequestContext类中有ThreadLocal变量来记录每个Request所需要传递的数据
    2.Zuul的过滤器是由Groovy写成,这些过滤器文件被放在Zuul Server上的特定目录下面,Zuul会定期轮询这些目录,修改过的过滤器会动态的加载到Zuul Server中以便过滤请求使用
    • 标准过滤器类型:
      Zuul大部分功能都是通过过滤器来实现的。Zuul中定义了四种标准过滤器类型,这些过滤器类型对应于请求的典型生命周期
      • PRE: 在请求被路由之前调用,利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等
      • ROUTING: 请求路由到微服务,用于构建发送给微服务的请求,使用Apache HttpClient或Netfilx Ribbon请求微服务
      • POST: 在路由到微服务以后执行,用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等
      • ERROR: 在其他阶段发生错误时执行该过滤器
    • 内置的特殊过滤器:
      • StaticResponseFilter: StaticResponseFilter允许从Zuul本身生成响应,而不是将请求转发到源
      • SurgicalDebugFilter: SurgicalDebugFilter允许将特定请求路由到分隔的调试集群或主机
    • 自定义的过滤器:
      除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。如STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务
  • 过滤器的生命周期
    Zuul请求的生命周期详细描述了各种类型的过滤器的执行顺序

  • 过滤器调度过程
     

  • 动态加载过滤器
     

Zuul的作用

Zuul可以通过加载动态过滤机制实现Zuul的功能:

  • 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求
  • 审查与监控: 在边缘位置追踪有意义数据及统计结果,得到准确的生产状态结论
  • 动态路由: 以动态方式根据需要将请求路由至不同后端集群处
  • 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平
  • 负载分配: 为每一种负载类型分配对应容量,并弃用超出限定值的请求
  • 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群
  • 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近

Zuul与应用的集成方式

  • ZuulServlet - 处理请求(调度不同阶段的filters,处理异常等)
    • 所有的Request都要经过ZuulServlet的处理,
    • Zuul对request处理逻辑的三个核心的方法: preRoute(),route(), postRoute()
    • ZuulServletZuulServlet交给ZuulRunner去执行。由于ZuulServlet是单例,因此ZuulRunner也仅有一个实例。ZuulRunner直接将执行逻辑交由FilterProcessor处理,FilterProcessor也是单例,其功能就是依据filterType执行filter的处理逻辑
    • FilterProcessor对filter的处理逻辑:
      1.首先根据Type获取所有输入该Type的filter:List list
      2.遍历该list,执行每个filter的处理逻辑:processZuulFilter(ZuulFilter filter)
      3.RequestContext对每个filter的执行状况进行记录,应该留意,此处的执行状态主要包括其执行时间、以及执行成功或者失败,如果执行失败则对异常封装后抛出
      4.到目前为止,Zuul框架对每个filter的执行结果都没有太多的处理,它没有把上一filter的执行结果交由下一个将要执行的filter,仅仅是记录执行状态,如果执行失败抛出异常并终止执行
    • ContextLifeCycleFilter - RequestContext 的生命周期管理:
      • ContextLifecycleFilter的核心功能是为了清除RequestContext;请求上下文RequestContext通过ThreadLocal存储,需要在请求完成后删除该对象RequestContext提供了执行filter Pipeline所需要的Context,因为Servlet是单例多线程,这就要求RequestContext即要线程安全又要Request安全。context使用ThreadLocal保存,这样每个worker线程都有一个与其绑定的RequestContext,因为worker仅能同时处理一个Request,这就保证了Request Context 即是线程安全的由是Request安全的。
    • GuiceFilter - GOOLE-IOC(Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IOC).Guice非常小而且快.)
    • StartServer - 初始化 zuul 各个组件(ioc,插件,filters,数据库等)
    • FilterScriptManagerServlet - uploading/downloading/managing scripts, 实现热部署
      Filter源码文件放在zuul 服务特定的目录, zuul server会定期扫描目录下的文件的变化,动态的读取\编译\运行这些filter,如果有Filter文件更新,源文件会被动态的读取,编译加载进入服务,接下来的Request处理就由这些新加入的filter处理

React前端框架

React定义

  • React前端框架是Facebook开源的一个js库,用于动态构建用户界面.
  • React解决的问题:
    • 数据绑定的时候,大量操作真实dom,性能成本太高
    • 网站的数据流向太混乱,不好控制
  • React 把用户界面抽象成一个个组件.如按钮组件 Button,对话框组件 Dialog,日期组件 Calendar.开发者通过组合这些组件,最终得到功能丰富,可交互的页面.通过引入 JSX 语法,复用组件变得非常容易,同时也能保证组件结构清晰.有了组件这层抽象,React 把代码和真实渲染目标隔离开来,除了可以在浏览器端渲染到 DOM 来开发网页外,还能用于开发原生移动应用

React核心

虚拟DOM是React的基石,React的核心是 组件 ,React的精髓是 函数式编程 ,在React中是 单向响应的数据流

组件的设计目的是提高代码复用率,降低测试难度和代码复杂度:

  1. 提高代码复用率:组件将数据和逻辑封装,类似面向对象中的类
  2. 降低测试难度:组件高内聚低耦合,很容易对单个组件进行测试
  3. 降低代码复杂度:直观的语法可以极大提高可读性

React特点

  • JSX: JSX 是 JavaScript 语法的扩展
  • 组件: 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中
  • 单向响应的数据流: React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单
  • Declarative(声明式编码): React采用声明范式,可以轻松描述应用(自动dom操作)
  • Component-Based(组件化编码)
  • Learn Once,Write Anywhere(支持客户端与服务器渲染)
  • 高效:React通过对DOM的模拟(虚拟dom),最大限度地减少与DOM的交互

1.虚拟(virtual)DOM, 不总是直接操作DOM,减少页面更新次数;
2.高效的DOM Diff算法, 最小化页面重绘;

  • 灵活:React可以与已知的库或框架很好地配合

React的虚拟DOM

  • 传统DOM更新
    真实页面对应一个 DOM 树.在传统页面的开发模式中,每次需要更新页面时,都要手动操作 DOM 来进行更新

虚拟DOM
DOM操作非常昂贵.我们都知道在前端开发中,性能消耗最大的就是DOM操作,而且这部分代码会让整体项目的代码变得难以维护.React把真实DOM树转换成JavaScript对象树,也就是Virtual DOM

  • 虚拟DOM定义:
    • 一个虚拟DOM(元素)是一个一般的js对象,准确的说是一个对象树(倒立的)
    • 虚拟DOM保存了真实DOM的层次关系和一些基本属性,与真实DOM一一对应
    • 如果只是更新虚拟DOM, 页面是不会重绘的
  • Virtual DOM算法步骤:
    • 用JS对象树表示DOM树的结构.然后用这个树构建一个真正的DOM树插入到文档中
    • 当状态变更的时候,重新构造一棵新的对象树.然后用新的树和旧的树进行比较,记录两棵树差异
    • 把差异应用到真实DOM树上,视图就更新了
  • 进一步理解:
    • Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个 缓存

可以类比CPU和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,
我们就在它们JS和DOM之间加个缓存.CPU(JS)只操作内存(Virtual DOM,最后的时候再
把变更写入硬盘(DOM)

  • React提供了一些API来创建一种特别的一般js对象
 
 
 
 
  1. //创建的就是一个简单的虚拟DOM对象 
  2. var element = React.createElement('h1', {id:'myTitle'}, 'hello'); 
  • 虚拟DOM对象最终都会被React转换为真实的DOM
  • 我们编码时基本只需要操作react的虚拟DOM相关数据,react会转换为真实DOM变化而更新界面
  • 创建虚拟DOM的2种方式
    • JSX方式
 
 
 
 
  1. //  jsx方式创建虚拟dom元素对象 
  2. const vDOM2 = {msg.toLowerCase()} 

还有一种是纯JS,一般不使用:

 
 
 
 
  1. //  纯JS方式 
  2. const msg = 'I like you'; 
  3. const myId = 'atguigu'; 
  4. const vDOM1 = React.createElement('h2',{id:myId},msg); 
  • 渲染虚拟DOM(元素)
    • 语法: ReactDOM.render(virtualDOM,containerDOM)
    • 作用: 将虚拟DOM元素渲染到真实容器DOM中显示
    • 参数说明:
      • 参数一: 纯js或jsx创建的虚拟DOM对象
      • 参数二: 用来包含虚拟DOM元素的真实dom元素对象(一般是一个div)
 
 
 
 
  1. //  渲染到真实的页面中 
  2.  ReactDOM.render(vDOM1,document.getElementById('example1')); 
  3.  ReactDOM.render(vDOM2,document.getElementById('example2')); 

使用示例:

 
 
 
 
  1.  
  2.  
  3.  
  4.  
  5. 02_JSX_DEMO 
  6.  
  7.  
    •  
    • A
    •  
    • B
    •  
    • C
    •  
     

  8.  
  9.  
 
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  • /* 
  •  功能: 动态展示列表数据 
  •  */ 
  • /* 
  •  技术点: 
  •  1). 使用JSX创建虚拟DOM 
  •  2). React能自动遍历显示数组中所有的元素 
  •  3). array.map()的使用 
  •  */ 
  • //数据的数组 
  • var names = ['Tom2', 'Jack2', 'Bob2']; 
  • //数据的数组——>标签数组 
  • var lis = []; 
  • names.forEach((item,index)=>lis.push({item}
  • )); 
  • //创建虚拟的DOM 
  • const ul=
  • //  将虚拟的Dom渲染到页面中的某个DOM元素中 
  • ReactDOM.render(ul,document.getElementById('example1')) 
  • const ul2 =  
  • ReactDOM.render(ul2, document.getElementById('example2')) 
  •  
  •  
  •  
  •  
  • React的组件

     
     
     
     
    1. 1.工厂(无状态)函数(简单组件,推荐使用) 
    2.     
    3.     //  方式一:工厂函数,推荐使用 
    4.   function MyComponent() { 
    5.   return 

      工厂函数

       
    6.   } 
    7.  
    8. 2.ES6类语法 
    9. //  方式二:ES6类语法(复杂组件,推荐使用) 
    10. class MyComponent2 extends React.Component{ 
    11.      render(){ 
    12.          return 

      ES6的语法

       
    13.      } 
    14.  } 

    2. 渲染组件标签

     
     
     
     
    1. //语法规则 
    2. ReactDOM.render(, document.getElementById('example')); 
     
     
     
     
    1. 1.React内部会创建组件实例对象; 
    2. 2.得到包含的虚拟DOM并解析为真实DOM; 
    3. 3.插入到指定的页面元素内部; 

    组件的三大属性

    props属性

    1.每个组件对象都会有props(properties的简写)属性

    2.组件标签的所有属性都保存在props中

    3.内部读取某个属性值:this.props.propertyName

    4. 作用: 通过标签属性从组件外向组件内传递数据(只读)

    5.对props中的属性值进行类型限制和必要性限制:

     
     
     
     
    1. //  对标签属性进行限制 
    2. Person.propTypes = { 
    3.     name:React.PropTypes.string.isRequired, 
    4.     sex:React.PropTypes.string, 
    5.     age:React.PropTypes.number 

    6. 扩展属性: 将对象的所有属性通过props传递

     
     
     
     
    1.  
    2.  //具体如下: 
    3.  ReactDOM.render(,document.getElementById('example')) 

    7.默认属性值

     
     
     
     
    1. //  指定属性的默认值 
    2. Person.defaultProps = { 
    3.      sex:'男', 
    4.      age:18 
    5.  } 

    8.组件类的构造函数

     
     
     
     
    1. constructor (props) { 
    2.   super(props) 
    3.   console.log(props) // 查看所有属性 

    refs属性

    1.组件内的标签都可以定义ref属性来标识本身

    2.在组件中可以通过this.refs.refName来得到对应的真实DOM对象

    3. 作用: 用于操作指定的ref属性的dom元素对象(表单标签居多)

     
     
     
     
    1.  
    2.       handleFocus(event) { 
    3.  event.target  //返回input对象 
    4.       } 
     
     
     
     
    1. this.change = this.change.bind(this); 

    箭头函数(ES6模块化编码时才能使用)

    state属性

     
     
     
     
    1. constructor (props) { 
    2.    super(props) 
    3.    this.state = { 
    4.      stateProp1 : value1, 
    5.      stateProp2 : value2 
    6.    } 
     
     
     
     
    1. this.state.statePropertyName 
     
     
     
     
    1. this.setState({ 
    2. stateProp1 : value1, 
    3. stateProp2 : value2 
    4. }) 

    组件的生命周期

    React的函数式编程

     
     
     
     
    1. var arr = [1, 3, 5, 7] 
    2. // 需求: 得到一个新的数组, 数组中每个元素都比arr中对应的元素大10: [11, 13, 15, 17] 
    3. // 命令式编程 
    4. var arr2 = [] 
    5. for(var i =0;i
    6.     arr2.push(arr[i]+10) 
    7. console.log(arr2) 
    8. // 声明式编程 
    9. var arr3 = arr.map(function(item){ 
    10.     return item +10 
    11. }) 
    12. // 声明式编程是建立命令式编程的基础上 

    React的JSX

     
     
     
     
    1. var liArr = dataArr.map(function(item, index){ 
    2.               return {item}
    3.  
    4.           }) 

    React的其它操作

    双向绑定

     
     
     
     
    1.  
    2.  class Control extends React.Component{ 
    3.      constructor(props){ 
    4.          super(props) 
    5.          //初始化状态 
    6.          this.state = { 
    7.              msg:'ATGUIGU' 
    8.          } 
    9.          this.handleChange = this.handleChange.bind(this) 
    10.  
    11.      } 
    12.  
    13.      handleChange(event){ 
    14.          //得到最新的state的值 
    15.          const msg=event.target.value; 
    16. //          console.log(event.target) 
    17. //          console.log(event.target.value) 
    18.          //更新状态 
    19.          this.setState({msg}) 
    20.      } 
    21.      render(){ 
    22.          const {msg} = this.state 
    23.          return( 
    24.              
       
    25.                 
    26.                

      {msg}

       
    27.              
     
  •          ) 
  •      } 
  •  } 
  •  ReactDOM.render(,document.getElementById('example')) 
  •  
  • React发送ajax请求

     
     
     
     
    1. //做一个跳转页面 
    2.  
    3.  
    4.  
    5.  
    6.  
    7. class UserLastGist extends React.Component { 
    8.     constructor (props) { 
    9.         super(props) 
    10.         this.state = { 
    11.             url: null 
    12.         } 
    13.     } 
    14.     componentDidMount () { 
    15.         // 发送ajax请求 
    16.         const url = `https://api.github.com/users/${this.props.username}/gists` 
    17.         axios.get(url) 
    18.             .then(response => { 
    19.                 console.log(response) 
    20.                 // 读取响应数据 
    21.                 //0索引位代表最后更新的网页内容 
    22.                 const url = response.data[0].html_url 
    23.                 // 更新状态 
    24.                 this.setState({url}) 
    25.             }) 
    26.             .catch(function (error) { 
    27.  
    28.                 console.log('----', error); 
    29.             }) 
    30.     } 
    31.     render () { 
    32.         const {url} = this.state 
    33.         if(!url) { 
    34.             return 

      loading...

       
    35.         } else { 
    36.             return 

      {this.props.username}'s last gist is here 

       
    37.         } 
    38.     } 
    39. UserLastGist.propTypes = { 
    40.     username: React.PropTypes.string.isRequired 
    41. ReactDOM.render(, document.getElementById('example')) 
    42.  

    RESTful

    RESTful的关键

    RESTful与 RPC

    RESTful Web 服务的Java框架

    RESTful API

    URL定位资源,用HTTP动词(GET,POST,PUT,DELETE)描述操作

    RESTful API设计原则