精华内容
下载资源
问答
  • Spring全家桶之SpringCloud——初级阶段

    千次阅读 多人点赞 2019-09-03 23:20:55
    SpringCloud 初级阶段知识体系搭建分享 ,包括微服务架构介绍, 微服务设计原则, SpringCloud介绍以及基本环境搭建, SpringBoot项目的构建, 日志配置文件和yml的介绍, 多环境配置文件的命名与使用, 核心注解与异常处理...

    SpringCloud

    介绍

    前提

    ⚫ 需要对分布式架构有一定等了解
    ⚫ 需要对Spring Boot有一定的了解

    简介

    Spring Cloud是一系列框架的有序集合。 它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。
    Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装 , 屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。

    学习导图

    在这里插入图片描述

    第一章 微服务架构介绍

    一. 单体架构

    单体架构也称之为单体系统或者是单体应用。就是一种把系统中所有的功能、模块耦合在一个应用中的架构方式。
    特点 : 打包成一个独立的单元 jar包或者 war包 ;会以进程的方式运行
    优点:项目易于管理、部署简单。
    缺点:测试成本高、可伸缩性差、可靠性差、迭代困难、跨语言程度差、团队协作难

    二. 微服务架构

    常见项目架构风格

    架构风格: 项目的一种设计模式

    架构风格举例
    客户端与服务端(C/S)QQ, 微信等
    基于组件模型的架构( 基于组件模型的架构 EJB )https://blog.csdn.net/lovemenghaibin/article/details/50666964
    分层架构 (MVC)OA系统,CMS内容管理系统等等
    面向服务架构 (SOA)分布式项目-Ego电商项目等

    三. MVC、RPC、SOA、微服务架构的介绍

    MVC 架构
    MVC(Model-View-Controller) : MVC架构就是一个单体架构。
    代表技术: Struts2 、SpringMVC 、Spring 、Mybatis 等。

    RPC 架构
    RPC(Remote Procedure Call) :远程过调用。通过网络从计算机序上请求服务,而不需要了解底层网络技术的协议的架构方式。
    代表技术:Thrift 、Hessian等

    SOA(Service oriented Architecture): 面向服务架构
    ESB(Enterparise Servce Bus): 企业服务总线,中介。主要是提供了一个于企业服务总线,
    ESB 包含的功能如:负载均衡,流量控制, 加密处理, 服务的监控, 异常处理, 监控告急等。
    代表技术: Mule 、WSO2

    微服务架构
    微服务就是一个轻量级的治理方案。
    代表技术: SpringCloud、dubbo 等

    在这里插入图片描述

    四. 微服务的设计原则

    AKF拆分原则

    AKF可扩展立方

    诞生背景
    业界对于可扩展的系统架构设计有一个朴素的理念,就是: 通过加机器就可以解决容量和可用性问题。(如果一台不行那就两台)。 这一理念在“云计算”概念疯狂流行的今天,得到了广泛的认可!对于一个规模迅速增长的系统而言,容量和性能问题当然是首当其冲的。但是随着时间的向前,系统规模的增长,除了面对性能与容量的问题外,还需要面对功能与模块数量上的增长带来的系统复杂性问题以及业务的变化带来的提供差异化服务问题。而许多系统,在架构设计时并未充分考虑到这些问题,导致系统的重构成为常态,从而影响业务交付能力,还浪费人力财力!对此,《可扩展的艺术》一书提出了一个更加系统的可扩展模型—— AKF可扩展立方 (Scalability Cube)。这个立方体中沿着三个坐标轴设置分别为:X、Y、Z。
    在这里插入图片描述

    坐标介绍
    Y轴(功能 ) —— 关注应用中功能划分, 基于不同的业务拆分
    X轴(水平扩展) —— 关注水平扩展,也就是”加机器解决问题”
    Z轴(数据分区) —— 关注服务和数据的优先级划分,如按地域划分

    Y轴(功能 )

    Y 轴扩展会将庞大的整体应用拆分为多个服务。每个服务实现一组相关的功能,如订单管理、客户管理等。在工程上常见的方案是面向服务架构(SOA) 。比如对于一个电子商务平台,我们可以拆分成不同的服务,组成下面这样的架构:
    在这里插入图片描述
    但通过观察上图容易发现,当服务数量增多时,服务调用关系变得复杂。为系统添加一个新功能,要调用的服务数也变得不可控,由此引发了服务管理上的混乱。所以,一般情况下,需要采用服务注册的机制形成服务网关来进行服务治理。系统的架构将变成下图所示:
    在这里插入图片描述

    X 轴(水平扩展)

    为了提升单个服务的可用性和容量, 对每一个服务进行X 轴扩展划分。
    X 轴扩展与我们前面朴素理念是一致的,通过绝对平等地复制服务与数据,以解决容量和可用性的问题。其实就是将微服务运行多个实例,做集群加负载均衡的模式。
    在这里插入图片描述

    Z 轴(数据分区)

    Z 轴扩展通常是指基于请求者或用户独特的需求,进行系统划分,并使得划分出来的子系统是相互隔离但又是完整的。以生产汽车的工厂来举例:福特公司为了发展在中国的业务,或者利用中国的廉价劳动力,在中国建立一个完整的子工厂,与美国工厂一样,负责完整的汽车生产。这就是一种Z 轴扩展。

    工程领域常见的Z 轴扩展有以下两种方案

    • 单元化架构
      在分布式服务设计领域,一个单元(Cell)就是满足某个分区所有业务操作的自包含闭环。如上面我们说到的Y 轴扩展的SOA 架构,客户端对服务端节点的选择一般是随机的,但是,如果在此加上Z 轴扩展,那服务节点的选择将不再是随机的了,而是每个单元自成一体。如下图:

    在这里插入图片描述

    • 数据分区
      为了性能数据安全上的考虑,我们将一个完整的数据集按一定的维度划分出不同的子集。一个分区(Shard),就是是整体数据集的一个子集。比如用尾号来划分用户,那同样尾号的那部分用户就可以认为是一个分区。数据分区为一般包括以下几种数据划分的方式:
      数据类型(如:业务类型)
      数据范围(如:时间段,用户ID)
      数据热度(如:用户活跃度,商品热度)
      按读写分(如:商品描述,商品库存)

    前后端分离原则

    前后端分离原则,简单来讲就是前端和后端的代码分离,我们推荐的模式是最好采用物理分离的方式部署,进一步促使更彻底的分离。如果继续直接使用服务端模板技术,如:jsp,把java、js、html、css 都堆到一个页面里,稍微复杂一点的页面就无法维护了。
    在这里插入图片描述
    何为前后端分离?前后端本来不就分离么?这要从尴尬的jsp 讲起。分工精细化从来都是蛋糕做大的原则,多个领域工程师最好在不需要接触其他领域知识的情况下合作,才可能使效率越来越高,维护也会变得简单。jsp 的模板技术融合了html 和java 代码,使得传统MVC 开发中的前后端在这里如胶似漆,前端做好页面,后端转成模板,发现问题再找前端,前端又看不懂java 代码…前后端分离的目的就是将这尴尬局面打破。

    这种分离方式有几个好处

    • 前后端技术分离,可以由各自的专家来对各自的领域进行优化,这样前段的用户体验优化效果更好。
    • 分离模式下,前后端交互界面更清晰,就剩下了接口模型,后端的接口简洁明了,更容易维护。
    • 前端多渠道集成场景更容易实现,后端服务无需变更,采用统一的数据和模型,可以支持多个前端:例如:微信h5 前端、PC 前端、安卓前端、IOS 前端。

    无状态服务

    在这里插入图片描述

    对于无状态服务,首先说一下什么是状态:如果一个数据需要被多个服务共享,才能完成一笔交易,那么这个数据被称为状态。进而依赖这个“状态”数据的服务被称为有状态服务,反之称为无状态服务。 那么这个无状态服务原则并不是说在微服务架构里就不允许存在状态,表达的真实意思是要把有状态的业务服务改变为无状态的计算类服务,那么状态数据也就相应的迁移到对应的“有状态数据服务”中。
    场景说明:例如我们以前在本地内存中建立的数据缓存、Session缓存,到现在的微服务架构中就应该把这些数据迁移到分布式缓存中存储,让业务服务变成一个无状态的计算节点。迁移后,就可以做到按需动态伸缩,微服务应用在运行时动态增删节点,就不再需要考虑缓存数据如何同步的问题。

    Restful通信风格

    在这里插入图片描述

    作为一个原则来讲本来应该是个“无状态通信原则”,在这里我们直接推荐一个实践优选的Restful 通信风格 ,因为他有很多好处:

    1. 无状态协议HTTP,具备先天优势,扩展能力很强。例如需要安全加密,有现成的成熟方案HTTPS即可。
    2. JSON 报文序列化,轻量简单,人与机器均可读,学习成本低,搜索引擎友好。
    3. 语言无关,各大热门语言都提供成熟的Restful API框架,相对其他的一些RPC框架生态更完善。

    第二章 SpringCloud入门

    一. SpringCloud简介

    SpringCloud概念

    是一个服务治理平台,是一系列框架的有序集合。包含了:服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等

    与Dubbo的区别

    Spring Cloud 是一个微服务框架,相比Dubbo 等RPC 框架, Spring Cloud 提供的全套的分布式系统解决方案。
    Spring Cloud 对微服务基础框架Netflix 的多个开源组件进行了封装,同时又实现了和云端平台以及和Spring Boot 开发框架的集成。
    Spring Cloud 为微服务架构开发涉及的配置管理,服务治理,熔断机制,智能路由,微代理,控制总线,一次性token,全局一致性锁,leader 选举,分布式session,集群状态管理等操作提供了一种简单的开发方式。
    Spring Cloud 为开发者提供了快速构建分布式系统的工具,开发者可以快速的启动服务或构建应用、同时能够快速和云平台资源进行对接。

    SpringCloud的项目的位置

    Sping Cloud 是Spring 的一个顶级项目与Spring Boot、Spring Data 位于同一位置。

    Spring Cloud 包含了很多子项目,如:

    1. Spring Cloud Config:配置管理工具,支持使用Git 存储配置内容,支持应用配置的外部化存储,支持客户端配置信息刷新、加解密配置内容等
    2. Spring Cloud Bus:事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config 联合实现热部署。
    3. Spring Cloud Netflix:针对多种Netflix 组件提供的开发工具包,其中包括Eureka、Hystrix、Zuul、Archaius 等。
      • Netflix Eureka:一个基于rest 服务的服务治理组件,包括服务注册中心、服务注册与服务发现机制的实现,实现了云端负载均衡和中间层服务器的故障转移。
      • Netflix Hystrix:容错管理工具,实现断路器模式,通过控制服务的节点,
        从而对延迟和故障提供更强大的容错能力。
      • Netflix Ribbon:客户端负载均衡的服务调用组件。
      • Netflix Feign:基于Ribbon 和Hystrix 的声明式服务调用组件。
      • Netflix Zuul:微服务网关,提供动态路由,访问过滤等服务。
      • Netflix Archaius:配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。
    4. Spring Cloud for Cloud Foundry : 通过Oauth2 协议绑定服务到CloudFoundry,CloudFoundry 是VMware 推出的开源PaaS 云平台。
    5. Spring Cloud Sleuth:日志收集工具包,封装了Dapper,Zipkin 和HTrace操作。
    6. Spring Cloud Data Flow:大数据操作工具,通过命令行方式操作数据流。
    7. Spring Cloud Security:安全工具包,为你的应用程序添加安全控制,主要是指OAuth2。
    8. Spring Cloud Consul:封装了Consul 操作,consul 是一个服务发现与配置工具,与Docker 容器可以无缝集成。
    9. Spring Cloud Zookeeper : 操作Zookeeper 的工具包, 用于使用zookeeper 方式的服务注册和发现。
    10. Spring Cloud Stream:数据流操作开发包,封装了与Redis,Rabbit、Kafka 等发送接收消息。
    11. Spring Cloud CLI:基于Spring Boot CLI,可以让你以命令行方式快速建立云组件。

    在这里插入图片描述

    二. SpringCloud与Dubbo的区别

    在这里插入图片描述

    虽然在对项目的技术选型时 ,性能是个比较好的评价指标 ,但不是唯一的评价的指标. 就拿上图来说,虽然Dobbo与 SpringCloud的性能比大约是3 :1 ,在性能上虽然优于SpringCloud , 但是在看具体使用时 ,还需要根据项目的功能进行综合的选型.

    三. SpringCloud版本说明

    常见版本号说明

    软件版本号:2.0.2.RELEASE
    2:主版本号。当功能模块有较大更新或者整体架构发生变化时,主版本号会更新
    0:次版本号。次版本表示只是局部的一些变动。
    2:修改版本号。一般是bug 的修复或者是小的变动
    RELEASE:希腊字母版本号。次版本号用户标注当前版本的软件处于哪个开发阶段
    
    希腊字母版本号作用
    Base设计阶段。只有相应的设计没有具体的功能实现。
    Alpha软件的初级版本。存在较多的bug
    Bate表示相对alpha 有了很大的进步,消除了严重的bug,还存在一些潜在的bug。
    Release该版本表示最终版。

    为什么Spring Cloud 版本用的是单词而不是数字

    设计的目的是为了更好的管理每个Spring Cloud 的子项目的清单。避免子的版本号与子项目的版本号混淆。

    版本号单词的定义规则

    采用伦敦的地铁站名称来作为版本号的命名,根据首字母排序,字母顺序靠后的版本号越大。

    版本发布计划说明

    在这里插入图片描述

    注意 : 在使用SpringCloud时 ,建议使用SR 版本(如下图),因为这样的版本是正式发布版 ,稳定且无重大bug

    在这里插入图片描述

    第三章 SpringBoot实战

    一. SpringBoot复习

    简介

    Spring Boot 是在Spring 的基础之上产生的(确切的说是在Spring4.0 的版本的基础之上),
    其中“Boot”的意思就是“引导”,意在简化开发模式,是开发者能够快速的开发出基于Spring 的应用。Spring Boot 含有一个内嵌的web 容器。我们开发的web 应用不需要作为war包部署到web 容器中,而是作为一个jar 包,在启动时根据web 服务器的配置进行加载。

    出现背景

    在项目中存在大量的xml 文件,配置相当繁琐
    整合第三方框架时的配置问题
    低效的开发效率与部署效率问题

    作用

    Spring Boot 使配置简单
    Spring Boot 使编码加单
    Spring Boot 使部署简单
    Spring Boot 使监控简单

    二. 快速创建SpringBoot项目(通过官网)

    1. 打开快速创建SpringBoot的官网 点击这里快速创建SpringBoot项目
    2. 根据下图提示创建项目 ,导入工作空间即可
      在这里插入图片描述

    项目结构
    注意 :
    1.可能是Maven版本的问题导致pom.xml出现异常,但是运行不报错就可忽略
    2.第一次导入SpringBoot项目(Maven)时,需要下载相关的jar 等待下载后即可使用!!!

    三. SpringBoot配置文件详解

    全局配置文件 application.properties

    1. 修改内嵌容器的端口号

      server.port=8888
      
    2. 自定义属性配置

      msg=Hello World
      
      @Value("${msg}")
      private String msg;
      
    3. 配置变量引用

      hello=szxy
      msg=Hello World ${hello}
      
      @Value("${msg}")
      private String msg;
      
    4. 随机值配置

      配置随机值

      num=${random.int}
      msg=Hello World ${num}
      
      @Value("${msg}")
      private String msg;
      

      用处:配置随机值,在程序中如果有一些运算需要一个随机值,那么可以使用该方式来生成。
      注意: 启动一次SpringBoot项目只生成一次。

      配置随机启动端口

      server.port=${random.int[1024,9999]}
      

      用处:在SpringCloud 的微服务中,我们是不需要记录IP 与端口号的。让其随机生成就可以了。

    yml配置文件

    yml 配置文件的语法

    1. 在properties 文件中是以“.”进行分割,在yml 中使用“:”
      进行分割
    2. yml 的数据格式和json 的格式很像,都是K-V 结构的。并且
      是通过“:”赋值
    3. 在yml 中缩进一定不能使用TAB 键,否则会报错。
    4. 每个Key 的冒号后面一定要加一个空格
      示例:
    #配置SpringBoot的启动端口号
    server:
          port: 9999
        
    #配置可以被引用的常量   
    msg: 
          ymlmsg: hello
          othermsg: world
    

    引用yml中的常量

    @Value("${msg.ymlmsg}")
    	private String msg;
    @Value("${msg.othermsg}")
    	private String othermsg;
    

    yml 配置文件与properties 文件的区别

    1. 配置文件的扩展名有变化
    2. 配置文件中的语法有变化

    注 :在使用SpringBoot时 ,我们可以使用全局配置文件 application.properties,也可以使用 yml ,他们两个的作用都是定义全局属性 ,我们根据自己的喜好选择

    logback 日志记录讲解

    1 导入相关的jar 包(使用SpringBoot项目时 ,自动导入)
    2 添加logback.xml 配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
     <configuration>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->  
        <property name="LOG_HOME" value="${catalina.base}/logs/" />  
        <!-- 控制台输出 -->   
        <appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
           <!-- 日志输出编码 -->  
            <layout class="ch.qos.logback.classic.PatternLayout">   
                 <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> 
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n   
                </pattern>   
            </layout>   
        </appender>   
        <!-- 按照每天生成日志文件 -->   
        <appender name="RollingFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">   
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>   
                <MaxHistory>30</MaxHistory>
            </rollingPolicy>   
            <layout class="ch.qos.logback.classic.PatternLayout">  
                <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> 
                <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n   
                </pattern>   
           </layout> 
            <!--日志文件最大的大小-->
           <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
             <MaxFileSize>10MB</MaxFileSize>
           </triggeringPolicy>
        </appender>     
    
        <!-- 日志输出级别 -->
        <root level="info">   
            <appender-ref ref="Stdout" />   
            <appender-ref ref="RollingFile" />   
        </root> 
    
    
    
    <!--日志异步到数据库 -->  
    <!--     <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
            日志异步到数据库 
            <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
               连接池 
               <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
                  <driverClass>com.mysql.jdbc.Driver</driverClass>
                  <url>jdbc:mysql://127.0.0.1:3306/databaseName</url>
                  <user>root</user>
                  <password>root</password>
                </dataSource>
            </connectionSource>
      </appender> -->
    
    </configuration>
    

    日志的访问级别(由高到低)

    日志级别作用
    FATAL致命的, 表示非常严重的错误, 一般是系统错误
    ERROR错误, 表示代码错误, 比较严重
    WARN警告, 不影响程序的运行, 但是可能存在风险.
    INFO信息, 表示一个普通的输出信息
    DEBUG调试, 表示程序员人为的一些调试信息

    Spring Boot 的配置文件- 多环境配置

    在不同的开发阶段需要不同的配置来运行项目,我们避免重复配置和打包,打包前配置多个配置文件,运行时使用参数指定加载的配置文件。

    profile:代表的就是一个环境变量
    语法结构:application-{profile}.properties
    

    0 需求:

    application-dev.properties 开发环境
    application-test.properteis 测试环境
    application-prod.properteis 生产环境
    

    1 打包项目 , 放到相应的磁盘下(我放的是f盘 )
    注意打包时可能出现如下图所示异常
    在这里插入图片描述

    解决方案
    调整单元测试用例所在包路径 <= @SpringBootConfiguration标记的类和启动类

    大意为: 测试SpringBoot应用时,不需要像Spring一样通过注解@ContextConfiguration(classes=…​)或者@Configuration来指定需要加载的配置文件, @*Test注解会在没有配置的情况下自动寻找.
    #搜寻算法是从单元测试类所在包向上层寻找,被@SpringBootApplication 或@SpringBootConfiguration 注解的class.

    2 运行项目:
    java -jar xxx.jar --spring.profiles.active={profile}
    3 完成的命令:
    java -jar springbootDemo-0.0.1-SNAPSHOT.jar --spring.profiles.active=test|dev|prod

    在这里插入图片描述

    四. SpringBoot核心注解与异常处理

    相关注解作用
    @SpringBootApplication代表是SpringBoot 的启动类。
    @SpringBootConfiguration通过bean 对象来获取配置信息
    @Configuration通过对bean 对象的操作替代spring 中xml 文件
    @EnableAutoConfiguration完成一些初始化环境的配置。
    @ComponentScan来完成spring 的组件扫描。替代之前我们在xml 文件中配置组件扫描的配置<context:component-scan pacage=”....”>
    @RestController:1、表示一个Controller。
    2、表示当前这个Controller 下的所有的方法都会以json 格式的数据响应。

    异常处理(见第二部分)
    https://blog.csdn.net/qq_43371556/article/details/100108538

    五. 如何监控 SpringBoot 的健康状况

    使用Actuator 检查与监控的步骤

    1. 在pom 文件中添加Actuator 的坐标

      <dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-actuator</artifactId>
      		</dependency>
      
    2. 修改配置文件
      2.0.0版本一下需要在全局配置文件中设置关闭安全限制
      management.security.enabled=false
      2.0.0版本以上需要修改默认显示的信息,使显示所有
      management.endpoints.web.exposure.include=*

    3. 通过访问url获取信息。
      例如:http://localhost:8080/actuator

    使用可视化的监控报表-Spring Boot Admin

    搭建服务端

    服务端其实也是一个SpringBoot 项目.
    官网:https://github.com/codecentric/spring-boot-admin
    官网有详细的安装流程
    添加依赖

    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-server</artifactId>
        <version>2.1.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    注意:
    使用的SpringBoot的版本最好是和spring-boot-admin管理工具的版本一样!!!,否则很大可能报错

    在启动类上添加@EnableAdminServer注解

    @SpringBootApplication
    @EnableAdminServer
    public class SpringBootAdminApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(SpringBootAdminApplication.class, args);
    	}
    }
    

    搭建客户端

    添加依赖:

    <dependency>
        <groupId>de.codecentric</groupId>
        <artifactId>spring-boot-admin-starter-client</artifactId>
        <version>2.1.6</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    

    注意:
    1.使用的SpringBoot的版本最好是和spring-boot-admin管理工具的版本一样!!!,否则很大可能报错,客户端
    2.客户端使用的 spring-boot-security需要在指定的仓库才能下载!!!所以将客户端的pom文件放在下面

    <!-- SpringBoot的安装工具 -->
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    	<!-- 指定本项目的仓库 -->
    	<repositories>
    		<repository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    		</repository>
    	</repositories>
    	<pluginRepositories>
    		<pluginRepository>
    			<id>spring-milestones</id>
    			<name>Spring Milestones</name>
    			<url>https://repo.spring.io/milestone</url>
    		</pluginRepository>
    	</pluginRepositories>
    

    修改配置文件:

    #2.0.0版本以后需要设置显示所有
    management.endpoints.web.exposure.include=*
    #设置SpringBootAdmin监控服务的地址(client端用)
    spring.boot.admin.client.url=http://localhost:9090  
    
    #设置安全管理的用户名密码
    spring.security.user.name=admin
    spring.security.user.password=admin
    

    添加一个类使执行器端点可访问

    @Configuration
    public class SecurityPermitAllConfig extends WebSecurityConfigurerAdapter {
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests().anyRequest().permitAll()  
                .and().csrf().disable();
        }
    }
    

    第四章 RabbitMQ实战

    一. 安装RabbitMQ单机版

    1. 安装环境依赖

      yum -y install make gc-c gcc-c++ kernel-devel m4 ncurses-devel openssl-devel
      yum -y install perl
      
    2. 上传安装包
      otp_src_18.3.tar.gz
      rabbitmq-server-generic-unix-3.6.1.tar.xz

      软件和客户端安装包分享 https://pan.baidu.com/s/1T1RwvZ_Rj3f_ui2i7Dsnzw

    3. 解压缩otp_src_18.3.tar.gz
      tar -xvf otp_src_18.3.tar.gz

    4. 建立erlang安装环境
      创建目录mkdir /usr/local/erlang
      在解压目录下运行
      ./configure --prefix=/usr/local/erlang --with-ssl -enable-threads -enable-smmp-support -enable-kernel-poll --enable-hipe --without-javac

    5. 编译安装erlang
      在解压目录运行
      make && make install

    6. 配置erlang的环境变量

      vim /etc/profile

      ERLANG_HOME=/usr/local/erlang
      PATH=$ERLANG_HOME/bin:$PATH
      export ERLANG_HOME
      export PATH
      

    特别注意: 涉及修改环境变量 ,需要重启虚拟机或者重新加载修改环境变量的这个文件,重新加载环境变量文件命令如下 source /etc/profile

    1. 测试erlang安装
      执行命令:erl
      出现下图所示则表示安装成功!!!
      在这里插入图片描述

    2. 解压rabbitmq

      在压缩包所在目录下, 先执行yum -y install xz
      再执行xz -d rabbitmq-server-generic-unix-3.6.1.tar.xz
      最后执行tar -xvf rabbitmq-server-generic-unix-3.6.1.tar
      拷贝解压后的文件夹到/usr/local/rabbitmq

      [root@bogon Downloads]# cp rabbitmq_server-3.6.1/ /usr/local/rabbitmq/ -rf
      
    3. 运行rabbitmq

      进入rabbitmq的sbin目录

      • 启动命令,该命令ctrl+c后会关闭服务 [不推荐]
        ./rabbitmq-server
      • 在后台启动Rabbit
        ./rabbitmq-server -detached
      • 关闭服务
        ./rabbitmqctl stop
      • 关闭服务(kill) 找到rabbitmq服务的pid [不推荐]
        ps -ef|grep rabbitmq
        kill -9 ****
    4. 设置管理员用户
      在这里插入图片描述

      #创建用户(账号mquser密码mquser),最好手动输入
      ./rabbitmqctl add_user mquser mquser
      #设置管理员权限
      ./rabbitmqctl set_user_tags mquser administrator
      #查看用户和权限
      ./rabbitmqctl list_users
      #给用户访问虚拟机的权限
      ./rabbitmqctl set_permissions -p / mquser '.*' '.*' '.*'
      
    5. 开放端口5672和15672端口( 或者使用时关闭防火墙 )

      #编辑防火墙配置文件
      vim /etc/sysconfig/iptables
      # 开放5672(RabbitMQ的端口号). 15672(RabbitMQ客户端的端口号)
      -A INPUT -m state --state NEW -m tcp -p tcp --dport 5672 -j ACCEPT
      -A INPUT -m state --state NEW -m tcp -p tcp --dport 15672 -j ACCEPT
      
    6. 启动mq的管理ui
      ./rabbitmq-plugins enable rabbitmq_management

    7. 访问rabbitmq的管理员UI
      http://服务器IP地址:15672/

    注: 如果访问不到管理界面配置文件没有生效,最好是重启防火墙 ,让配置文件生效
    在这里插入图片描述

    二. RabbitMQ介绍

    简介

    RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

    作用

    • 同步变异步
      在这里插入图片描述
      在这里插入图片描述
    • 解耦合
      在这里插入图片描述
    • 流量削锋
      在这里插入图片描述
    1. 消息队列基础知识
    • Provider

    消息生产者,就是投递消息的程序。

    • Consumer

    消息消费者,就是接受消息的程序。

    • 没有使用消息队列时消息传递方式

    在这里插入图片描述

    • 使用消息队列后消息传递方式

    在这里插入图片描述

    • 什么是队列?

    队列就像存放了商品的仓库或者商店,是生产商品的工厂和购买商品的用户之间的中转站

    • 队列里存储了什么?

    在 rabbitMQ 中,信息流从应用程序出发,来到 Rabbitmq 的队列,所有信息可以只 存储在一个队列中。
    队列可以存储很多信息,因为它基本上是一个无限制的缓冲区,前提是你的机器有足够的存储空间。

    • 队列和应用程序的关系?

    多个生产者可以将消息发送到同一个队列中,多个消息者也可以只从同一个队列接收数据。

    三. 入门案例

    在这里插入图片描述
    pom文件头部报错可能是eclipse或jdk版本问题 ,不影响程序运行, 可忽略

    1. 创建jar项目,修改pom文件

      <!-- 继承的启动器的父项目,版本根据自己需求修改  -->
      <parent>
      		<groupId>org.springframework.boot</groupId>
      		<artifactId>spring-boot-starter-parent</artifactId>
      		<version>2.1.6.RELEASE</version>
      		<relativePath /> <!-- lookup parent from repository -->
      	</parent>
      
      	<dependencies>
      		<!-- springBoot 的启动器 -->
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-web</artifactId>
      		</dependency>
      		<!-- rabbitmq -->
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-amqp</artifactId>
      		</dependency>
      		<!-- Sprng test 测试 -->
      		<dependency>
      			<groupId>org.springframework.boot</groupId>
      			<artifactId>spring-boot-starter-test</artifactId>
      			<scope>test</scope>
      		</dependency>
      	</dependencies>
      
    2. 全局配置文件

      #项目名(随便起)/ RabbitMQ的ip/端口
      spring.application.name=springcloud-rabbitmq
      spring.rabbitmq.host=192.168.179.136
      spring.rabbitmq.port=5672
      #RabbitMQ的用户名与密码
      spring.rabbitmq.username=mquser
      spring.rabbitmq.password=mquser
      
    3. 创建消息队列,注意所导的包
      在这里new一个队列后 ,发送者和使用者必须通过这个名字才能对改消息进行发送和接收!!!

      import org.springframework.amqp.core.Queue;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      /**
       * 1. 创建消息队列
       * @Configuration即用来代替Spring配置文件的,
       *  它就是一个@Component组件,接收一个value值也就是bean的名字,value可以不填。
       * @author chy
       *
       */
      @Configuration
      public class QueueConfig {
      	/**
      	 * 创建队列
      	 * 
      	 * @return
      	 */
      	@Bean
      	public Queue createQueue() {
      		return new Queue("hello-queue-队列名称保持一致");
      	}
      }
      
      
    4. 消息发送者类

      /**
       * 2. 创建消息发送者
       * @author chy
       *
       */
      @Component
      public class Sender {
      	@Autowired
      	private AmqpTemplate rabbitAmqpTemplate;
      
      	/*
      	 * 发送消息的方法
      	 */
      	public void send(String msg) {
      		// 向消息队列发送消息
      		// 参数一:队列的名称。
      		// 参数二:消息
      		this.rabbitAmqpTemplate.convertAndSend("hello-queue-队列名称保持一致", msg);
      	}
      }
      
      
    5. 消息接收者类

      /**
       * 3. 消息接收者
       * 
       * @author chy
       *
       */
      @Component
      public class Receiver {
      	/**
      	 * 接收消息的方法。采用消息队列监听机制
      	 * @param msg
      	 */
      	@RabbitListener(queues = "hello-queue-队列名称保持一致")
      	public void process(String msg) {
      		System.out.println("receiver: " + msg);
      	}
      }
      
    6. 启动类

      /**
       *  4. 启动类
       * @author chy
       *
       */
      @SpringBootApplication
      public class Application {
      
      	public static void main(String[] args) {
      		SpringApplication.run(Application.class, args);
      	}
      
      }
      
    7. 测试类

      @RunWith(SpringRunner.class)
      @SpringBootTest(classes = Application.class)
      public class QueueTest {
      	@Autowired
      	private Sender sender;
      
      	/*
      	 * 测试消息队列
      	 */
      	@Test
      	public void test1() {
      		this.sender.send("Meeeage: Hello RabbitMQ");
      	}
      }
      

      测试结果-控制台
      在这里插入图片描述
      测试结果-RabbitMQ客户端
      在这里插入图片描述

    四. RabbitMQ原理

    在这里插入图片描述

    组件/说明
    1. Message 消息
    消息是不具名的,它由消息头消息体组成。消息体是不透明的,而消息头则由一系列可选属性组成,这些属性包括:routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出消息可能持久性存储)等。
    2. Publisher 消息的生产者
    也是一个向交换器发布消息的客户端应用程序
    3. Consumer 消息的消费者
    表示一个从消息队列中取得消息的客户端应用程序。
    4. Exchange 交换器
    用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
    三种常用的交换器类型 1. direct(发布与订阅 完全匹配) 2. fanout(广播) 3. topic(主题,规则匹配)
    5. Binding 绑定
    用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息 队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表
    6. Queue 消息队列
    用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一 个消息可投入一个或多个队列。消息一直在队列里面,等待消费者链接到这个队列将其取走。
    7. Routing-key 路由键
    RabbitMQ 决定消息该投递到哪个队列的规则。 队列通过路由键绑定到交换器。 消息发送到 MQ 服务器时,消息将拥有一个路由键,即便是空的,RabbitMQ 也会将其 和绑定使用的路由键进行匹配。 如果相匹配,消息将会投递到该队列。 如果不匹配,消息将会进入黑洞。
    8. Connection 链接
    指 rabbit 服务器和服务建立的 TCP 链接。
    9. Channel 信道
    1. Channel 中文叫做信道,是 TCP 里面的虚拟链接。例如:电缆相当于 TCP,信道是 一个独立光纤束,一条 TCP 连接上创建多条信道是没有问题的。
    2. TCP 一旦打开,就会创建 AMQP 信道。
    3. 无论是发布消息、接收消息、订阅队列,这些动作都是通过信道完成的。
    10. Virtual Host虚拟主机
    表示一批交换器,消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在链接时指定, RabbitMQ 默认的 vhost 是"/"
    11. Borker
    表示消息队列服务器实体。
    12. 交换器和队列的关系
    交换器是通过路由键和队列绑定在一起的,如果消息拥有的路由键跟队列和交换器的 路由键匹配,那么消息就会被路由到该绑定的队列中。也就是说,消息到队列的过程中,消息首先会经过交换器,接下来交换器在通过路由 键匹配分发消息到具体的队列中。路由键可以理解为匹配的规则。

    五. RabbitMQ为什么要使用信道

    1. TCP 的创建和销毁开销特别大。创建需要 3 次握手,销毁需要 4 次分手
    2. 如果不用信道,应用程序就会以TCP 链接 Rabbit,高峰时每秒成千上万条链接会造成资源巨大的浪费,而且操作系统每秒处理 TCP 链接数也是有限制的,必定造成性能瓶颈。
    3. 信道的原理是一条线程一条通道,多条线程多条通道同用一条 TCP 链接。 一条 TCP 链接可以容纳无限的信道,即使每秒成千上万的请求也不会成为性能的瓶颈。

    六. RabbitMQ交换器

    pom.xml

    <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>1.5.21.RELEASE</version>
    		<relativePath /> <!-- lookup parent from repository -->
    	</parent>
    
    	<dependencies>
    		<!-- Spring Boot Web 启动器 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    		<!-- Spring Boot RabbitMQ 消息队列启动器 -->
    		<dependency>
    			<groupId>org.springframework.amqp</groupId>
    			<artifactId>spring-rabbit</artifactId>
    		</dependency>
    		<!-- Spring Boot 监控启动器 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-actuator</artifactId>
    		</dependency>
    		<!-- Spring Boot Test 启动器 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    		<!-- Spring Boot 监控客户端启动类 -->
    		<dependency>
    			<groupId>de.codecentric</groupId>
    			<artifactId>spring-boot-admin-starter-client</artifactId>
    			<version>1.5.7</version>
    		</dependency>
    	</dependencies>
    	<build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    

    Direct交换器(发布与订阅完全匹配)

    在这里插入图片描述
    消息提供者

    1.配置文件

    #项目名/ RabbitMQ的ip/端口
    #spring.application.name=springcloud-provider
    #spring.rabbitmq.host=192.168.179.136
    #spring.rabbitmq.port=5672
    #RabbitMQ的用户名与密码
    #spring.rabbitmq.username=mquser
    #spring.rabbitmq.password=mquser
    
    # 交换器
    mq.config.exchange=log-direct
    # 队列
    mq.config.queue.info=log.info 
    # 路由键
    mq.config.queue.info.routing.key=log.info.routing.key
    # 队列
    mq.config.queue.error=log.error 
    # 路由键
    mq.config.queue.error.routing.key=log.error.routing.key
    

    2.发送者类(两个,作用相同)

    /**
     * 消息发送者-发送错误消息
     *
     */
    @Component
    public class ErrorSender {
    
    	@Autowired
    	private RabbitTemplate rabbiteTemplate;
    	@Value("${mq.config.exchange}")
    	private String exchange;
    	@Value("${mq.config.queue.error.routing.key}")
    	private String key;
    	/**
    	 * 发送消息
    	 * @param msg
    	 */
    	public void sendMsg(String msg) {
    		/**
    		 * 第一个参数表示 交换器的名称
    		 * 第二个参数表示  路由键
    		 * 第三个参数表示 消息,将要发送的消息
    		 */
    		this.rabbiteTemplate.convertAndSend(exchange,key,msg);
    	}
    	
    }
    
    
    /**
     * 消息发送者-发送info消息
     *
     */
    @Component
    public class InfoSender {
    
    	@Autowired
    	private RabbitTemplate rabbiteTemplate;
    	@Value("${mq.config.exchange}")
    	private String exchange;
    	@Value("${mq.config.queue.info.routing.key}")
    	private String key;
    	/**
    	 * 发送消息
    	 * @param msg
    	 */
    	public void sendMsg(String msg) {
    		/**
    		 * 第一个参数表示 交换器的名称
    		 * 第二个参数表示  路由键
    		 * 第三个参数表示 消息,将要发送的消息
    		 */
    		this.rabbiteTemplate.convertAndSend(exchange,key,msg);
    	}
    	
    }
    
    

    3.启动类
    注意
    1.使用RabbitMQ启动类需要加@EnableRabbit注解!!!
    2.在这里提供者的启动类需要配合测试类使用

    @SpringBootApplication
    @EnableRabbit
    public class App {
    
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    	
    }
    
    

    4.测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest
    public class QueueTest {
    
    	@Autowired
    	private ErrorSender errorSender;
    	@Autowired
    	private InfoSender infoSender;
    	
    	@Test
    	public void testError() throws Exception{
    		   // TODO 
    		   int flag = 0; 
    		  while(true) {
    			  Thread.sleep(2000);
    			  this.errorSender.sendMsg("Hello World RabbitMq"+(flag++));
    		  }
    		 
    	}
    	@Test
    	public void testInfo() throws Exception{
    		// TODO 
    		int flag = 0; 
    		while(true) {
    			Thread.sleep(2000);
    			this.infoSender.sendMsg("Hello World RabbitMq"+(flag++));
    		}
    		
    	}
    	
    }
    
    

    消息消费者
    1.配置文件

    #项目名/ RabbitMQ的ip/端口
    spring.application.name=springcloud-consumer
    spring.rabbitmq.host=192.168.179.136
    spring.rabbitmq.port=5672
    #RabbitMQ的用户名与密码
    spring.rabbitmq.username=mquser
    spring.rabbitmq.password=mquser
    
    # 交换器
    mq.config.exchange=log-direct
    # 队列
    mq.config.queue.info=log.info 
    # 路由键
    mq.config.queue.info.routing.key=log.info.routing.key
    # 队列
    mq.config.queue.error=log.error 
    # 路由键
    mq.config.queue.error.routing.key=log.error.routing.key
    
    # 解决 ACK 反馈
    #开启重试 
    spring.rabbitmq.listener.retry.enabled=true 
    #重试次数,默认为 3 次 
    spring.rabbitmq.listener.retry.max-attempts=5
    
    

    2.接收者类(两个,结构相同)
    重点掌握相关注解的使用
    @RabbitHandler用于令消费者的相关方法监听服务队列

    /**
     * 消息接收者 消息接收者
     * 
     * @RabbitListener bindings:绑定队列
     * 
     * @QueueBinding value:绑定队列的名称 
     *               exchange:配置交换器 
     *               key:路由键
     * @Queue value:配置队列名称 
     *        autoDelete:是否是一个可删除的临时队列 
     * 
     * @Exchange value:为交换器起个名称  
     *           type:指定具体的交换器类型
     */
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.DIRECT),
    		value = @Queue(value = "${mq.config.queue.error}", autoDelete = "false"),
    		key = "${mq.config.queue.error.routing.key}")
    )
    public class ErrorReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 * @throws Exception 
    	 */
    	@RabbitHandler
    	public void receiveMsg(String msg) throws Exception {
    		System.out.println("Error------"+msg);
    		// 手动抛出异常
    		//throw new Exception();
    	}
    }
    
    
    /**
     * 消息接收者 消息接收者
     * 
     * @RabbitListener bindings:绑定队列
     * 
     * @QueueBinding value:绑定队列的名称 
     *               exchange:配置交换器 
     * @Queue value:配置队列名称 
     *        autoDelete:是否是一个可删除的临时队列 
     * 
     * @Exchange value:为交换器起个名称  
     *           type:指定具体的交换器类型
     */
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.DIRECT),
    		value = @Queue(value = "${mq.config.queue.info}", autoDelete = "true"),
    		key = "${mq.config.queue.info.routing.key}")
    )
    public class InfoReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 */
    	@RabbitHandler
    	public void receiveMsg(String msg) {
    		System.out.println("Info------"+msg);
    	}
    }
    
    

    3.启动类(启动消费者)
    注意使用RabbitMQ启动类需要加@EnableRabbit注解!!!

    @SpringBootApplication
    @EnableRabbit
    public class App {
    
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    	
    }
    

    在这里插入图片描述

    Topic交换器(主题,规则匹配)

    在这里插入图片描述
    消息生产者
    1.配置文件

    ##项目名/ RabbitMQ的ip/端口
    #spring.application.name=springcloud-provider
    #spring.rabbitmq.host=192.168.179.136
    #spring.rabbitmq.port=5672
    ##RabbitMQ的用户名与密码
    #spring.rabbitmq.username=mquser
    #spring.rabbitmq.password=mquser
    
    # 交换器
    mq.config.exchange=log.topic
    

    2.发送者类(3个)

    @Component
    public class OrderSender {
    
    	@Autowired
    	private RabbitTemplate rabbiteTemplate;
    	@Value("${mq.config.exchange}")
    	private String exchange;
    	
    	/**
    	 * 发送消息
    	 * @param msg
    	 */
    	public void sendMsg(String msg) {
    		/**
    		 * 第一个参数表示 交换器的名称
    		 * 第二个参数表示  路由键
    		 * 第三个参数表示 消息,将要发送的消息
    		 */
    		this.rabbiteTemplate.convertAndSend(exchange,"order.log.debug","order.log.Debgu"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"order.log.info","order.log.Info"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"order.log.warn","order.log.Warn"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"order.log.error","order.log.Error"+msg);
    	}
    	
    }
    
    
    @Component
    public class ProductSender {
    
    	@Autowired
    	private RabbitTemplate rabbiteTemplate;
    	@Value("${mq.config.exchange}")
    	private String exchange;
    	
    	/**
    	 * 发送消息
    	 * @param msg
    	 */
    	public void sendMsg(String msg) {
    		/**
    		 * 第一个参数表示 交换器的名称
    		 * 第二个参数表示  路由键
    		 * 第三个参数表示 消息,将要发送的消息
    		 */
    		this.rabbiteTemplate.convertAndSend(exchange,"product.log.debug","product.log.Debgu"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"product.log.info","product.log.Info"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"product.log.warn","product.log.Warn"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"product.log.error","product.log.Error"+msg);
    	}
    	
    }
    
    
    @Component
    public class UserSender {
    
    	@Autowired
    	private RabbitTemplate rabbiteTemplate;
    	@Value("${mq.config.exchange}")
    	private String exchange;
    	
    	/**
    	 * 发送消息
    	 * @param msg
    	 */
    	public void sendMsg(String msg) {
    		/**
    		 * 第一个参数表示 交换器的名称
    		 * 第二个参数表示  路由键
    		 * 第三个参数表示 消息,将要发送的消息
    		 */
    		this.rabbiteTemplate.convertAndSend(exchange,"user.log.debug","user.log.Debgu"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"user.log.info","user.log.Info"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"user.log.warn","user.log.Warn"+msg);
    		this.rabbiteTemplate.convertAndSend(exchange,"user.log.error","user.log.Error"+msg);
    	}
    	
    }
    
    

    3.启动类

    @SpringBootApplication
    @EnableRabbit
    public class App {
    
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    	
    }
    

    4.测试类(生产者执行的入口,会默认启动上面的启动类)

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringBootTest
    public class QueueTest {
    
    	@Autowired
    	private OrderSender orderSender;
    	@Autowired
    	private ProductSender productSender;
    	@Autowired
    	private UserSender userSender;
    	
    	@Test
    	public void test() throws Exception{
    		  this.orderSender.sendMsg("UserSender----");
    		  this.productSender.sendMsg("Producer----");
    		  this.userSender.sendMsg("UserSnder------");
    	}
    	
    	
    }
    
    

    消息消费者
    1.配置文件

    ##项目名/ RabbitMQ的ip/端口
    #spring.application.name=springcloud-consumer
    #spring.rabbitmq.host=192.168.179.136
    #spring.rabbitmq.port=5672
    ##RabbitMQ的用户名与密码
    #spring.rabbitmq.username=mquser
    #spring.rabbitmq.password=mquser
    
    #设置交换器的名称
    mq.config.exchange=log.topic
    #info 队列名称
    mq.config.queue.info=log.info
    #error 队列名称
    mq.config.queue.error=log.error
    #log 队列名称
    mq.config.queue.logs=log.all
    
    

    2.接收者类(3个)

    /**
     * 消息接收者
     * 
     * @RabbitListener bindings:绑定队列
     * 
     * @QueueBinding value:绑定队列的名称 
     *               exchange:配置交换器 
     *               key:路由键  :接收以el表达式内部代码格式结尾的消息
     * @Queue value:配置队列名称 
     *        autoDelete:是否是一个可删除的临时队列 
     * 
     * @Exchange value:为交换器起个名称  
     *           type:指定具体的交换器类型
     *
     */
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.TOPIC),
    		value = @Queue(value = "${mq.config.queue.error}", autoDelete = "true"),
    		key = "*.log.error")
    )
    public class ErrorReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 * @throws Exception 
    	 */
    	@RabbitHandler
    	public void Receiver(String msg){
    		System.out.println("......Error........receiver:"+msg);
    	
    	}
    }
    
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.TOPIC),
    		value = @Queue(value = "${mq.config.queue.info}", autoDelete = "true"),
    		key = "*.log.info")
    )
    public class InfoReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 */
    	@RabbitHandler
    	public void Receiver(String msg) {
    		System.out.println("......Info........receiver:"+msg);
    	}
    }
    
    
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.TOPIC),
    		value = @Queue(value = "${mq.config.queue.logs}", autoDelete = "true"),
    		key = "*.log.*")
    )
    public class LogsReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 * @throws Exception 
    	 */
    	@RabbitHandler
    	public void Receiver(String msg) throws Exception {
    		System.out.println("......All........receiver:"+msg);
    	
    	}
    }
    
    

    3.启动类

    @SpringBootApplication
    @EnableRabbit
    public class App {
    
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    	
    }
    
    

    Fanout交换器(广播)

    在这里插入图片描述
    消息生产者

    1.配置文件

    #项目名/ RabbitMQ的ip/端口
    #spring.application.name=springcloud-provider
    #spring.rabbitmq.host=192.168.179.136
    #spring.rabbitmq.port=5672
    #RabbitMQ的用户名与密码
    #spring.rabbitmq.username=mquser
    #spring.rabbitmq.password=mquser
    
    # 交换器
    mq.config.exchange=order.fanout
    

    2.发送类

    @Component
    public class Sender {
    
    	@Autowired
    	private RabbitTemplate rabbiteTemplate;
    	@Value("${mq.config.exchange}")
    	private String exchange;
    	/**
    	 * 发送消息
    	 * @param msg
    	 */
    	public void sendMsg(String msg) {
    		/**
    		 * 第一个参数表示 交换器的名称
    		 * 第二个参数表示  路由键
    		 * 第三个参数表示 消息,将要发送的消息
    		 */
    		this.rabbiteTemplate.convertAndSend(exchange,"",msg);
    	}
    	
    }
    
    

    消息消费者
    1.配置文件

    #项目名/ RabbitMQ的ip/端口
    #spring.application.name=springcloud-consumer
    #spring.rabbitmq.host=192.168.179.136
    #spring.rabbitmq.port=5672
    #RabbitMQ的用户名与密码
    #spring.rabbitmq.username=mquser
    #spring.rabbitmq.password=mquser
    
    # 交换器
    mq.config.exchange=order.fanout
    # 设置短信服务队列名称
    mq.config.queue.sms=order.sms 
    # push服务队列名称
    mq.config.queue.push=order.push
    

    2.接收者类(两个)

    /**
     * 消息接收者 消息接收者
     * @RabbitListener bindings:绑定队列
     * @QueueBinding value:绑定队列的名称 
     *               exchange:配置交换器 
     * @Queue value:配置队列名称 
     *        autoDelete:是否是一个可删除的临时队列 
     * 
     * @Exchange value:为交换器起个名称  
     *           type:指定具体的交换器类型
     */
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.FANOUT),
    		value = @Queue(value = "${mq.config.queue.push}", autoDelete = "true")
    		)
    )
    public class PushReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 */
    	@RabbitHandler
    	public void receiveMsg(String msg) {
    		System.out.println("push消费者消费信息:------"+msg);
    	}
    }
    
    
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.FANOUT),
    		value = @Queue(value = "${mq.config.queue.sms}", autoDelete = "false")
    		)
    )
    public class SmsReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 * @throws Exception 
    	 */
    	@RabbitHandler
    	public void receiveMsg(String msg) throws Exception {
    		System.out.println("sms消费者消费信息------"+msg);
    		// 手动抛出异常
    		//throw new Exception();
    	}
    }
    
    

    3.启动类(启动消费者项目)

    @SpringBootApplication
    @EnableRabbit
    public class App {
    
    	public static void main(String[] args) {
    		SpringApplication.run(App.class, args);
    	}
    	
    }
    

    测试结果
    在这里插入图片描述

    七. 实现服务的松耦合设计

    在Fanout交换器演示案例中添加如下代码,实现松耦合功能

    1.在消息消费者的配置文件中

    # 新增红包队列, 作为松耦合实例
    mq.config.queue.red=order.red
    
    1. 新增一个接收类
    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.FANOUT),
    		value = @Queue(value = "${mq.config.queue.red}", autoDelete = "true")
    		)
    )
    public class RedReceiver {
    
    	/**
    	 * 监听指定的消息队列
    	 * 
    	 * @param msg
    	 * @throws Exception 
    	 */
    	@RabbitHandler
    	public void receiveMsg(String msg) throws Exception {
    		System.out.println("我自愿给chy发送888红包------"+msg);
    	}
    }
    
    

    测试结果
    在这里插入图片描述

    通过上述案例可知 ,对新队列的添加不仅实现了松耦合的功能 ,而且实现开闭原则 ,即对拓展开放, 对修改关闭

    八. 消息处理

    1 RabbitMQ 的消息持久化处理

    消息的可靠性是RabbitMQ 的一大特色,那么RabbitMQ 是如何保证消息可靠性的呢——消息持久化。

    autoDelete 属性
    @Queue: 当所有消费客户端连接断开后,是否自动删除
    队列true:删除false:不删除
    设置删除 ,会将系统宕机时发送给消息队列的消息删除 ; 反之则不会删除 ,待重启后还能读取消息队列中的消息

    @Exchange:当所有绑定队列都不在使用时,是否自动
    删除交换器true:删除false:不删除

    定位到消息消费者项目的一个接收者类中

    @Component
    @RabbitListener(bindings = @QueueBinding(
    		exchange = @Exchange(value = "${mq.config.exchange}", type = ExchangeTypes.DIRECT),
    		value = @Queue(value = "${mq.config.queue.error}", autoDelete = "false"),
    		key = "${mq.config.queue.error.routing.key}")
    )
    public class ErrorReceiver {
    	@RabbitHandler
    	public void receiveMsg(String msg) throws Exception {
    		System.out.println("Error------"+msg);
    		// 手动抛出异常
    		throw new Exception();
    	}
    }
    

    Features中带有AD表示设置了自动删除
    在这里插入图片描述
    关闭自动删除 ,项目宕机 ,仍会将消息发送到队列中 ,可通过Ready或total查看
    在这里插入图片描述

    重启后, 读取消息队列中保存的消息

    在这里插入图片描述

    2. RabbitMQ 中的消息确认ACK 机制

    相关概念
    在这里插入图片描述

    解决方法

    1. 使用try-catch块捕获消费者中的异常
    2. 设置重试次数(在全局配置文件application.properties添加如下代码)
    #开启重试
    spring.rabbitmq.listener.simple.retry.enabled=true 
    #最大重试次数
    spring.rabbitmq.listener.simple.retry. max-attempts=5
    

    第五章 Eureka注册中心

    一. 服务的注册中心

    服务注册中心就是将微服务实现服务化管理的核心组件,类似于目录服务的作用,主要用来存储服务信息,譬如提供者 url 串、路由信息等。服务注册中心是SOA 架构中最基础的设施之一。

    1. 服务注册中心的作用

      • 服务的注册
      • 服务的发现
    2. 常见的注册中心有哪些

      • Dubbo 的注册中心 Zookeeper
      • Sringcloud 的注册中心 Eureka
    3. 服务注册中心解决了什么问题

      • 服务管理
      • 服务的依赖关系管理
    4. 什么是 Eureka 注册中心
      Eureka 是 Netflix 开发的服务发现组件,本身是一个基于 REST 的服务。Spring Cloud 将它集成在其子项目 spring-cloud-netflix 中,以实现 Spring Cloud 的服务注册于发现,同时 还提供了负载均衡、故障转移等能力。

    5. Eureka 注册中心三种角色

    角色作用
    Eureka Server通过 Register、Get、Renew 等接口提供服务的注册和发现。
    Application Service (Service Provider)服务提供方 , 把自身的服务实例注册到 Eureka Server 中
    Application Client (Service Consumer)服务调用方 , 通过 Eureka Server 获取服务列表,消费服务。

    二. 搭建Eureka注册中心

    1. 搭建服务注册中心

    pom.xml文件

    <dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>${spring-cloud.version}</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
      <!--SpringBoot项目打包插件 -->
       <build>
    		<plugins>
    			<plugin>
    				<groupId>org.springframework.boot</groupId>
    				<artifactId>spring-boot-maven-plugin</artifactId>
    			</plugin>
    		</plugins>
    	</build>
    

    在启动类上添加注解@EnableEurekaServer

    @SpringBootApplication
    @EnableEurekaServer
    public class SpringCloudEurekaApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(SpringCloudEurekaApplication.class, args);
    	}
    
    }
    
    

    修改配置文件 application.yml

    server:
      port: 8888
    spring:
      application:
        name: Eureka-Server
    
    eureka:
      client:
        register-with-eureka: false  #是否将自己注册到 Eureka-Server 中,默认的为 true
        fetch-registry: false   #是否冲 Eureka-Server 中获取服务注册信息,默认为 true 
    

    2. 搭建高可用 Eureka 注册中心(Eureka 集群)

    1. 创建项目,同上

    2. 配置文件 ,在上面基础上额外添加

      #设置 eureka 实例名称,与配置文件的变量为主 
      eureka.instance.hostname=服务名 
      #设置服务注册中心地址,指向另一个注册中心
      eureka.client.serviceUrl.defaultZone=http://ip+port/eu reka/ 
      

      在搭建 Eureka 集群时,需要添加多个配置文件,并且使用 SpringBoot 的多环境配置方式。集群中需要多少节点就添加多少个配置文件。

    3. 配置节点信息
      application-eureka1.properties(properties可以和yml文件相互转换)

      spring.application.name=eureka-server
      server.port=8761
      #设置eureka 实例名称,与配置文件的变量为主
      eureka.instance.hostname=eureka1
      #设置服务注册中心地址,指向另一个注册中心,如果是两个以上,只需要在下面加上逗号 ,然后加上完整连接即可
      eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka2:8761/eureka/
      spring.security.user.name=admin
      spring.security.user.password=admin
      
      #Eureka注册中心
      #是否将自己注册到 Eureka-Server 中,默认的为 true,使用单机的时候需要改为false
      #eureka.client.register-with-eureka=false
      #是否从 Eureka-Server 中获取服务注册信息,默认为 true
      eureka.client.fetch-registry=false
      
      

      application-eureka2.properties

      spring.application.name=eureka-server
      server.port=8761
      #设置eureka 实例名称,与配置文件的变量为主
      eureka.instance.hostname=eureka2
      #设置服务注册中心地址,指向另一个注册中心,如果是两个以上,只需要在下面加上逗号 ,然后加上完整连接即可
      eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka1:8761/eureka/
      
      spring.security.user.name=admin
      spring.security.user.password=admin
      
      #Eureka注册中心
      #是否将自己注册到 Eureka-Server 中,默认的为 true
      #eureka.client.register-with-eureka=false
      #是否从 Eureka-Server 中获取服务注册信息,默认为 true
      eureka.client.fetch-registry=false
      
      

      修改本地主机映射
      在这里插入图片描述
      修改虚拟机集群主机映射(集群中的每个主机都要配置)

      在这里插入图片描述

    4. 添加日志
      将logback.xml放入resources目录下

      <?xml version="1.0" encoding="UTF-8" ?>
       <configuration>
      <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->  
          <property name="LOG_HOME" value="${catalina.base}/logs/" />  
          <!-- 控制台输出 -->   
          <appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
             <!-- 日志输出编码 -->  
              <layout class="ch.qos.logback.classic.PatternLayout">   
                   <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> 
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n   
                  </pattern>   
              </layout>   
          </appender>   
          <!-- 按照每天生成日志文件 -->   
          <appender name="RollingFile"  class="ch.qos.logback.core.rolling.RollingFileAppender">   
              <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                  <!--日志文件输出的文件名-->
                  <FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>   
                  <MaxHistory>30</MaxHistory>
              </rollingPolicy>   
              <layout class="ch.qos.logback.classic.PatternLayout">  
                  <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> 
                  <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n   
                  </pattern>   
             </layout> 
              <!--日志文件最大的大小-->
             <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
               <MaxFileSize>10MB</MaxFileSize>
             </triggeringPolicy>
          </appender>     
      
          <!-- 日志输出级别 -->
          <root level="info">   
              <appender-ref ref="Stdout" />   
              <appender-ref ref="RollingFile" />   
          </root> 
      
      
      
      <!--日志异步到数据库 -->  
      <!--     <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
              日志异步到数据库 
              <connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
                 连接池 
                 <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
                    <driverClass>com.mysql.jdbc.Driver</driverClass>
                    <url>jdbc:mysql://127.0.0.1:3306/databaseName</url>
                    <user>root</user>
                    <password>root</password>
                  </dataSource>
              </connectionSource>
        </appender> -->
      
      </configuration>
      
    5. 部署项目
      使用maven打包项目, 将项目上传到linux下 , 编写并使用脚本启动程序 server.sh

      #!/bin/bash
       
      cd `dirname $0`
       
      CUR_SHELL_DIR=`pwd`
      CUR_SHELL_NAME=`basename ${BASH_SOURCE}`
       
      JAR_NAME="项目名称"
      JAR_PATH=$CUR_SHELL_DIR/$JAR_NAME
       
      #JAVA_MEM_OPTS=" -server -Xms1024m -Xmx1024m -XX:PermSize=128m"
      JAVA_MEM_OPTS=""
       
       #配置集群时使用
      SPRING_PROFILES_ACTIV="-Dspring.profiles.active=配置文件变量名称"
      #配置单机Eureka使用
      #SPRING_PROFILES_ACTIV=""
      LOG_DIR=$CUR_SHELL_DIR/logs
      LOG_PATH=$LOG_DIR/${JAR_NAME%..log
       
      echo_help()
      {
          echo -e "syntax: sh $CUR_SHELL_NAME start|stop"
      }
       
      if [ -z $1 ];then
          echo_help
          exit 1
      fi
       
      if [ ! -d "$LOG_DIR" ];then
          mkdir "$LOG_DIR"
      fi
       
      if [ ! -f "$LOG_PATH" ];then
          touch "$LOG_DIR"
      fi
       
      if [ "$1" == "start" ];then
       
          # check server
          PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
          if [ -n "$PIDS" ]; then
              echo -e "ERROR: The $JAR_NAME already started and the PID is ${PIDS}."
              exit 1
          fi
       
          echo "Starting the $JAR_NAME..."
       
          # start
          nohup java $JAVA_MEM_OPTS -jar $SPRING_PROFILES_ACTIV $JAR_PATH >> $LOG_PATH 2>&1 &
       
          COUNT=0
          while [ $COUNT -lt 1 ]; do
              sleep 1
              COUNT=`ps  --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l`
              if [ $COUNT -gt 0 ]; then
                  break
              fi
          done
          PIDS=`ps  --no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'`
          echo "${JAR_NAME} Started and the PID is ${PIDS}."
          echo "You can check the log file in ${LOG_PATH} for details."
       
      elif [ "$1" == "stop" ];then
       
          PIDS=`ps --no-heading -C java -f --width 1000 | grep $JAR_NAME | awk '{print $2}'`
          if [ -z "$PIDS" ]; then
              echo "ERROR:The $JAR_NAME does not started!"
              exit 1
          fi
       
          echo -e "Stopping the $JAR_NAME..."
       
          for PID in $PIDS; do
              kill $PID > /dev/null 2>&1
          done
       
          COUNT=0
          while [ $COUNT -lt 1 ]; do
              sleep 1
              COUNT=1
              for PID in $PIDS ; do
                  PID_EXIST=`ps --no-heading -p $PID`
                  if [ -n "$PID_EXIST" ]; then
                      COUNT=0
                      break
                  fi
              done
          done
       
          echo -e "${JAR_NAME} Stopped and the PID is ${PIDS}."
      else
          echo_help
          exit 1
      fi
      

      注意:
      windows下文件格式可能在linux下无法使用,使用notepad++转换成unix:编辑–>文档格式转换–>转为unix。

      赋予脚本执行权限  chmod +R 755 server.sh
      启动脚本 server.sh -start
      关闭脚本 server.sh -stop
      查看日志:在catalina/logs/server.log中
      测试访问http://192.168.179.136:8761
      http://192.168.179.137:8761
      

    3. 在高可用的 Eureka 注册中心中构建 provider 服务

    1. 创建jar项目

    2. 修改pom文件

    <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.1.7.RELEASE</version>
    	</parent>
    
    <properties>
    		<java.version>1.8</java.version>
    		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
    		<maven-jar-plugin.version>2.6</maven-jar-plugin.version>
    	</properties>
    
    <dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>${spring-cloud.version}</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    
    1. 修改启动类
      添加@EnableEurekaClient,表示可以被Eureka注册中心发现
    @SpringBootApplication
    @EnableEurekaClient
    public class SpringCloudEurekaApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(SpringCloudEurekaApplication.class, args);
    	}
    
    }
    
    1. 修改配置文件
    server:
      port: 8081
    spring:
      application:
        name: Eureka-Provider
    
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
    
    
    
    
        
    
    
    1. 控制层:
    @RestController
    public class UserController {
    
    	@RequestMapping("/user")
    	public List<User> user() {
    		List<User> list=new ArrayList<User>();
    		
    		list.add(new User(1, "张三", 21));
    		list.add(new User(2, "李四", 22));
    		list.add(new User(3, "王五", 23));
    		list.add(new User(4, "赵六", 24));
    		return list;
    		
    	}
    }
    

    在这里插入图片描述

    4. 在高可用的 Eureka 注册中心中构建 consumer 服务

    1. 创建jar项目

    2. 修改pom文件

    <parent>
    		<groupId>org.springframework.boot</groupId>
    		<artifactId>spring-boot-starter-parent</artifactId>
    		<version>2.1.7.RELEASE</version>
    	</parent>
    
    <properties>
    		<java.version>1.8</java.version>
    		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
    		<maven-jar-plugin.version>2.6</maven-jar-plugin.version>
    	</properties>
    
    <dependencies>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter</artifactId>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    	</dependencies>
    
    	<dependencyManagement>
    		<dependencies>
    			<dependency>
    				<groupId>org.springframework.cloud</groupId>
    				<artifactId>spring-cloud-dependencies</artifactId>
    				<version>${spring-cloud.version}</version>
    				<type>pom</type>
    				<scope>import</scope>
    			</dependency>
    		</dependencies>
    	</dependencyManagement>
    
    1. 修改启动类(添加@EnableEurekaClient)
    @SpringBootApplication
    @EnableEurekaClient
    public class SpringCloudEurekaApplication {
    
    	public static void main(String[] args) {
    		SpringApplication.run(SpringCloudEurekaApplication.class, args);
    	}
    
    }
    
    1. 修改配置文件
    server:
      port: 8091
    spring:
      application:
        name: Eureka-Consumer
    
    
    eureka:
      client:
        serviceUrl:
          defaultZone: http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
    
    1. 服务层(重点)
    @Service
    public class UserService {
    
    	
    	@Autowired
    	//ribbon 负载均衡器 
    	private LoadBalancerClient balancerClient;
    	
    	public List<User> getUsers(){
    		//选择调用的服务的名称   
    		//ServiceInstance 封装了服务的基本信息,如 IP,端口 
    		ServiceInstance si = this.balancerClient.choose("Eureka-Provider");
    		String url="http://"+si.getHost()+":"+si.getPort()+"/user";
    		
    		//springMVC RestTemplate 
    		//发送http请求
    		RestTemplate restTemplate=new RestTemplate();
    		
    		//参数化类型引用
    		ParameterizedTypeReference<List<User>> responseType=new ParameterizedTypeReference<List<User>>() { };
    		
    		//ResponseEntity:封装了返回值信息                       exchange发送请求         路径    方式                   参数       
    		ResponseEntity<List<User>> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, responseType);
    		
    		//获取响应体
    		List<User> list=responseEntity.getBody();
    		return list;
    	}
    }
    
    1. controller

    在consumer端访问http://localhost:8091/consumer.
    会获取到provider的控制层产生的数据。

    三. 理解Eureka注册中心

    1. Eureka 注册中心架构原理

    1 Eureka 架构图

    在这里插入图片描述

    Register(服务注册):把自己的 IP 和端口注册给 Eureka。
    Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。
    Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防止consumer 调用到不存在的服务。
    Get Registry(获取服务注册列表):获取其他服务列表。
    Replicate(集群中数据同步):eureka 集群中的数据复制与同步。
    Make Remote Call(远程调用):完成服务的远程调用。

    2. 基于分布式 CAP 定理,分析注册中心两大主流框架:Eureka与 Zookeeper 的区别

    1. 什么是 CAP 原则
    CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。
    CAP 由 Eric Brewer 在 2000 年 PODC 会议上提出。该猜想在提出两年后被证明成 立,成为我们熟知的 CAP 定理
    在这里插入图片描述
    cap定理研究策略
    在这里插入图片描述
    2. Zookeeper 与 Eureka 的区别

    在这里插入图片描述

    四. 优雅停服

    1. 在什么条件下,Eureka 会启动自我保护
    在这里插入图片描述

    2 为什么要启动自我保护
    在这里插入图片描述

    3 如何关闭自我保护
    修改 Eureka Server 配置文件

    #关闭自我保护:true 为开启自我保护,false 为关闭自我保护
    eureka.server.enableSelfPreservation=false
    #清理间隔(单位:毫秒,默认是60*1000)
    eureka.server.eviction.interval-timer-in-ms=60000
    

    4 如何优雅停服

    1. 不需要在 Eureka Server 中配置关闭自我保护

    2. 需要在服务中添加 actuator.jar 包

      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      
    3. 修改配置文件

      #启用 shutdown 
      endpoints.shutdown.enabled=true 
      #禁用密码验证 
      endpoints.shutdown.sensitive=false 
      #2.0版本以上
      management.endpoints.web.exposure.include: "*"
      
    4. 使用httpclient或者curl发送请求
      http://ip:port/acyuator/shutdown
      (curl:使用windows的cmd或者linux的终端:curl -X POST http://ip:port/shutdown)

    五. 加强Eureka注册中心的安全认证

    1. 在 Eureka Server 中添加 security 包

    <dependency>
    	<groupId>org.springframework.boot</groupId>
    	<artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    

    2.修改 Eureka Server 配置文件( 添加账户密码 )

    spring.application.name=Eureka-Server
    
    server.port=8080
    
    spring.security.basic.enabled=true
    spring.security.user.name=admin
    spring.security.user.password=admin
    
    eureka.client.serviceUrl.defaultZone=http://admin:admin@eureka1:8761/eureka/,http://admin:admin@eureka2:8761/eureka/
    
    

    3.添加配置类 关闭csrf防护(2.0以上)

    @Configuration
    public class Config extends WebSecurityConfigurerAdapter{
    
    	@Override
    	protected void configure(HttpSecurity http) throws Exception {
    		http.csrf().disable();
    		super.configure(http);
    	}
    }
    

    4. 修改微服务的配置文件添加访问注册中心的用户名与密码
    用户名 admin
    密码 admin

    eureka:
      client:
        serviceUrl:
          defaultZone: http://admin:admin@192.168.40.128:8080/eureka,http://admin:admin@192.168.40.129:8080/eureka
    

    传送门时间到~~~
    SpringCloud高级阶段上 传送门
    第一章 负载均衡Ribbon
    第二章 声明式调用Feign
    第三章 服务容错保护Hystrix

    SpringCloud高级阶段中 传送门
    第四章 如何设计微服务
    第五章 服务网关Zuul
    第六章 分布式配置中心

    SpringCloud高级阶段下 传送门
    第七章 消息总线Bus
    第八章 消息驱动Stream
    第九章 分布式服务跟踪Sleuth

    展开全文
  • 企业的每岗位都有其标准的晋升机制和等级要求,只不过大企业把它标准化和书面化,而一些小企业则没有这样的要求。但是不可否认的是,每职责岗位都有其晋升通道,岗位不同,其要求的能力素质也不太一样。 就比如...

    企业的每个岗位都有其标准的晋升机制和等级要求,只不过大企业把它标准化和书面化,而一些小企业则没有这样的要求。但是不可否认的是,每个职责岗位都有其晋升通道,岗位不同,其要求的能力素质也不太一样。

    就比如,腾讯的产品项目(内部成为P族)的内部晋升模型,从P1—P6,分为初作者、有经验者、骨干、专家、资深专家,然后每个等级又分为3个子等级,所以腾讯的分级也是极为严谨。晋升厚度很大,想要从初学者到资深专家,估计也得花比较长的时间。这是较为成熟的晋升体系,这也是建立在企业文化深厚底蕴,最重要的是得益于本身职位在历史长河中的发展较为充分,所以这个晋升体系也是比较完善。

    腾讯P族的等级能力要求

    但是作为产品运营,一个还在拓荒期的岗位,其晋升也是来的较为野蛮。小宇哥就是个典型的例子,从进入运营行业,就在没人带没人教的情况下,硬是闯出了一条血路。那时候运营也是叫的比较少,多是叫策划或者网络编辑、推广等称呼。近几年,运营这个称呼也开始慢慢被大众所接受,特别是近两年,运营多次被提起并重视,也是作为运营人的福音。

    在这种情况下,很多刚进入运营行业的小伙伴,比较关心的一个话题就是关于晋升渠道,现在网络上也贴出关于运营具体的晋升渠道:

    新媒体编辑——新媒体运营——资深 / 高级新媒体运营——新媒体运营主管 / 经理——新媒体运营总监——运营总监——CXO/CMO/COO

    这里对应的每一个岗位等级,能力要求也不一样。但是有些人做了3-5年,还是中级运营或者处在运营主管的位置就不会再上升了,而有些人年纪轻轻就坐上了运营总监甚至CMO/COO的位置了。

    为什么呢?这也是在网上被问得很火的一个问题:

    高级运营和初级运营的区别在哪里?

    但是我今天把运营等级分为了初级、中级、高级、顶级四个等级,而每一个等级对于的岗位要求能力以及现在从事的人数也不尽相同。今天我们就来说说这四者之间有什么不同?进而导致“同人不同命”的。

    运营的四个等级

    • 第一层,初级运营,现在的技能磨练是以后的根本
    • 第二层,中级运营,以方法论作指导,do right thing
    • 第三层,高级运营,运用丰富的资源整合把事情做得更好
    • 第四层,顶级运营,运筹帷幄之中 决胜千里之外

    第一层,初级运营,现在的技能磨练是以后的根本

    企业的一个岗位都有其要求,讲得就是技能,或高或低不同,产品运营岗位也不列外。我们都知道,现在进入运营行业的小伙伴也是从学会最基本的技能开始,如果里面的技能素质都不懂,你连初级运营都不是。

    在我刚进入运营行业时,从最底层的运营推广开始。产品推广中的SEO、SEM、软文写作、编辑器使用,排版等等,这是现在新媒体运营编辑的每天都会接触,可能现在在大企业里运营岗位分得较为垂直,编辑只是做“双微”的推送,并不会涉及SEO等推广的技能。但是这都算是运营的必备硬技能之一。

    我们这里又会把技能分为:硬技能和软技能

    硬技能指的是外在的技能,就是大家都能看见的,比如我们使用某个数据分析的工具、编辑使用的编辑器、又或者你使用Photoshop、excel等工具,这些都是大家可以看得到的,也是一个人别伯乐发现最好展现途径。

    软技能主要指的就是一个人的内在涵养,比如的谈吐、沟通技能、人际交往能力、团队协作能力等等都是你的软技能,虽然看不见,但是却又不得不修炼的技能。

    为什么这些技能显得重要?

    应届毕业生或者从产品、销售、技术转岗过来的新人,基本上对运营只是停留在概念上,而没有过多关于如何做或者为什么这些较为深层次的问题,唯有的就是先练好自己的技能,就是懂得这些好的爆品服务都是通过某些工具去实现的,这时我就得先去熟悉这些技能。而且这些技能也是运营入门必备的能力。

    拿我们新媒体编辑,如果你作为运营负责人,这时你要招一个新媒体编辑,这个新媒体编辑要么就是在校大学生,要么就是从别的岗位转过来的小伙伴。你招进来之后,你得培训他,这个培训不是特指的是公司业务层面的培训,而是主要在在技能上的培训。因为他要做一些推广运营的工作,比如微信公众号的运营、微博的推广或者APP内容的运营等等,这时你就得培养他们后台的使用、编辑器的使用、排版等等。

    很多人可能会觉得说,APP后台的使用、编辑的排版这些基本很多人都会,即使没接触过,培训不用很长时间也很快就能上手。说这句话的人可能只是看到的只是眼前的或者是狭隘的,而没有想到更久以后的或者更广的。我敢说产品运营的技能提升是一个贯穿于运营人整个职业生涯的,古人都有说:活到老学到老。我们要的不只是眼前的浅技能,产品运营要学的东西也不只是停留运营领域的,而是会设计市场、商务、技术、产品等等方面的技能。

    因此,在刚进入运营这个行业的时候,切莫着急一步登天,先静下心来磨练好你的运营技能,知道什么是运营的核心技能,什么是自己不擅长或者不会的,这将会对你接下来的运营生涯有非常大的帮助。

    第二层,中级运营,以方法论作指导,do right thing

    初级运营只是会用这些技能工具来执行很多事情,最起码对于编辑来说就这样,比如我看到很多小编就是为了做而做,我只是会去找授权文章,然后在编辑器里面修修改改,上司审核通过就可以推送了,心想着轻轻松松又一天,日复一日,这样做对自身并没有太多的帮助,而只是一个文字的搬运工。

    在我们掌握了一些基本的技能之后,接下来你要做的就是运用方法论去使自己会的东西发挥更大的作用。我们听说过这样的一句话:事半功倍,指的就是在做任何事情,方法是无比重要,如果方法不对,就会变成“事倍功半”。可见,有方法比胡做更有效率。

    我们拿活动运营举例子,活动运营是运营的三架马车之一,对产品拉新促活起着关键的作用。

    比如我们在做一个活动时第一步,应该紧紧围绕目标出发

    为什么要做这个活动,目的是什么,这是最重要的。

    大家接到活动运营任务的时候,往往就想着怎么去完成,往往忽略去问为什么?大家可能觉得问为什么没有必要,其实不然。多为什么帮助你去思考做好活动的必然过程。为什么要做这次活动?活动目的是为了什么?你知道了活动的目的之后,便于帮助你更好确定活动运营内容。比如,你现在手上接到要做一个活动任务,这个任务希望促销某种商品。当你了解到其实促销这种商品的主要目的是为了提升用户活跃量,那你会更加把活动内容和方向朝活跃老用户靠拢。那么这次活动效果会更好。

    而最理想的状况是把目的转化成一项数据,比如app希望提升用户规模,这是目的,转化为数据就是提升DAU。所以提升DAU就是活动目的。活动目的是策划活动的起点,是思考的源头。把握好这点,我们在后续的步骤中就不会偏离初衷。

    第二步,活动时间很重要

    很多时间节点不光非常有利活动借势宣传,而且经常能成为做活动的理由。传统的线下活动喜欢选择店庆、节假日、换季开展;而移动互联网优势给了线上活动更多的玩法和选择,不光是店庆、节假日、换季,一次热点事件都能成为很好的选择。另外,活动的起止时间也要有大概的范围。急于拿到活动效果的情况,活动就必须尽快上线;如果不是,那就可以有选择的余地。因为活动如果可以借势,就会事半功倍,这个“势”就是时间点。

    那我们除了在选择好的时间除外,还应该避开某些时间,就比如阿里巴巴的双十一,如果叫你跟阿里巴巴的双十一碰上,那你还不是死路一条,所以这些势头太过于凶猛都已经成为全民节日的,尽量避开,否则做了也是浪费钱。类似这些节日的还有京东的618、阿里的双十二等等。

    第三步,你要确定这个活动怎么玩才能吸引到用户的眼球!

    好玩能吸引到用户眼球的活动必定尊徐2个原则:简单和有创意的!

    1.简单,很多时候我们在活动策划时如果没注意就会陷入自我思维,源于对自己的产品过于熟悉,使得所有动作都变得理所当然,从而忽视一个活动初次参与者的心理。个人的思维怪圈是很难跳出的,所以在活动设计时要经常和大家交流,站在一个小白用户角度考虑问题。

    2.有创意能够抓住用户心理,活动本身有趣给予用户参与的一个很好的理由,是刺激用户参与并分享有利因素。抓住用户的兴趣点,满足不同用户个性化需求,满足用户虚荣心。很多时候运营人员要设计一个全新的活动是比较困难的,所以大部分活动策划都是站在成功活动基础上去微创新。微创新指借鉴以往成功活动模式,并寻求部分变化,这里变化可以是主题,可以是参与规则,也可以是参与平台等等,但活动主体模式基本一致。

    下面补充3个比较受3用户欢迎的活动类型:

    1.互动型活动

    互动型活动特征在于该活动形式大众,潜在参与对象多数有关相关经历,而且需要和活动发起方进行相关的沟通交流,比如微博段子手经常发起的话题“你吃过最难吃的食物是什么“、“你请假用过的奇葩理由”等。

    2.猎奇型活动

    猎奇型活动的特征在于独特、新奇,甚至怪诞。比如看到这样的新闻大家一般都会感兴趣的点击进去,“高三学生一年交往335个女友,食堂大妈都是他对象”、“武汉大学惊现女学霸,连续通宵自习3晚”。

    3.隐私型活动

    隐私型活动是目前被诟病比较多的一种活动形式,当然也是传播效果比较明显的一类活动,因为他们往往和性、不好意思公开的话题甚至内心的阴暗面相关。比如“在公告场所放屁是一种什么样的体验”、“早上下楼取快递你有穿内衣吗”、“第一次XXOO是什么时候”、“接吻大赛”、“BOSS吐槽大赛”等。

    第四步,做好活动执行时方案优化

    在活动执行上线时,这时监控效果就变得尤为重要,看活动是否在预期轨道上。如果在可调整的范围内,就尽快行动。这时你需要监控的就是数据和突发状况:

    数据,分为按实时的和单位时间的(一般按天)两种情况,要对数据波动情况有心理预期,认为上下多少算是正常的。如果超出波动范围,就被认为是异常情况,需要追查一下具体原因。可能性等同于上文的「风险点」。紧密关注会出现用户反馈的所有平台,比如用户群、贴吧、微博、朋友圈等。

    针对紧急事件和特殊事件,必须要有方案。特别是碰到大型活动的时候,根本不会让你有大量时候去思考补救措施。比如,服务器档机、系统异常等等。这些特殊情况出现时,我们活动应该继续,还是暂定。这些都是我们需要考虑到的。

    第五步,活动复盘对运营的价值尤为重要

    复盘,就是对过去做的事情重新演绎一次,从而获取对这件事更深的理解。没有复盘性的活动总结,活动只会越做越差。只有对活动整个过程进行细致的梳理、分析、总结才会凝聚成你的经验,而不仅仅是经历。

    活动复盘主要是可以分四步走:

    1、收集数据

    关于本次活动的所有数据都需要做收集、整理和分析。首先拿出当初做活动时定下的运营目标,计划新增多少用户,次日留存率是多少,有多少新增付费用户,Arup值是多少。客观地把当初的活动目标再拿出来看一遍,这时候也可,包也可以试着让团队成员来回顾,看是否每个人都会活动目标都足够清晰。

    2、同比和环比

    通过活动数据进行往期的同比和环比,评估这次活动的效果。对获取的数据结果进行一个简单处理,比如说获取到用户增长为5000人,那么用户增长的时间段分布式怎样的,增长最多的那个时间点有那些事件发生;反过来,也可以把宣传资源投放的时刻对应的增长变化提前呈现出来,看是否有哪部分是没有执行到位的。

    3、原因和现状分析

    分析这次活动带来的收益和不足,分析原因,以及下一阶段可以采取的措施。团队讨论深入地分析产生结果的原因,根据现有目标和结果的差异,提出部分假设,比如:是否高估了某个渠道的转化?或者是投放的时间点没有选择到最佳?还是宣传文案出了问题,没能打动用户?是不是Landing Page的某些功能不够顺畅?

    4、自我反思和总结

    完成这次活动之后,活动运营需要进行自我总结,分析活动成功与不足之处,便于下次做好活动。当你开始复盘之后,就会逐步体会到活动复盘,对于提升活动运营效果的巨大好处的。

    第三层,高级运营,运用丰富的资源把事情做得更好

    资源对于产品运营来说,那可谓是至关重要,不论你是做活动运营还是做BD或者做其他,拥有相对丰富的资源,将对你的项目整体进度起着很大的重要。

    如果说中级运营做活动运营可以打75分,那么高级运营就是那25分的补充者。我们都知道,做活动策划是很耗人力物力财力的,资金预算也是做活动前要考虑的头等大事,预算的多少将直接这场活动的效果。

    而如果拥有相对丰富的资源以及将它整合起来,不但省时省力还省钱。在我之前效力过的一家活动策划公司,当时帮某汽车品牌做一个圣诞节营销事件。我们当时遇到的最大的问题就是遇资金不够(其实就是甲方给的钱不够)不能专门定制一个线下活动,但是我们又不能放弃这么好的机会,帮一个世界500强做一个营销事件,如果做成功这对公司的整体实力以及对外的谈资都有会很大的提升。

    思前想后,刚好跟我们合作的一个俱乐部主办方在圣诞节将会有一个比较大型的活动要举办,这个主办方刚好跟我们的VP兼运营总监,也是我们的运营总监是合作伙伴。这时该发挥我们总监的作用了。我们的vp跟主办方接洽后,将通过整合,将这个活动包装成这个汽车品牌的送福利专场,就是我们提前准备的方案中提到,先在线上搞个类似粉丝抽奖活动,礼品就是这场高档次的活动入门券,然后通过这部汽车(仪式感:装扮成圣诞送福利专车)将这些中奖用户送到现场。

    这次线上线下的结合,很好地将活动的亮点凸现出来,品牌主满意,用户的体验也很好。最重要的是本身需要30万才能完成的活动,结果我们就以三分一的费用就办的漂漂亮亮。

    在这场成功的线上线下活动中,我们的vp扮演着重要的角色,将两个互不相干的两发拉拢在一起,策划了一场漂亮的活动,这就是资源整合的作用,也是我们所说的资源跨界。

    当去到高级运营的级别时,在很多人看来是个大事情,但是在他们的眼里将变成小事一桩,小事情就根本不要烦他。如果你有细心留意,你会发现现在很多企业的高级运营,他们很多时候都是充当着一个商务的角色,落实到执行上的 事情不会涉及过多,更多的是指个大方向,或者将精力聚焦在更重要的事,那就是资源的整合以及谈判。

    第四层,顶级运营,运筹帷幄之中 决胜千里之外

    如果说上面的人都在执行或者半执行布置下去的战术,那么后面就一定得有战略的部署者,就好似诸葛亮。而这诸葛亮者往往都是运筹帷幄之中,这个就是顶级运营(顶级运营一般是公司的COO或者VP)

    这个顶级运营已经不是狭义的运营,而是广义上的运营,就是上升到公司宏观的管理层面,包括技术、市场、产品上的战略性思维。

    什么是战略性思维?我觉得这种战略性思维主要可以表现为:大局观、细算力。大局观指的是长远的计划,细算力就是日常具体工作的力度。

    当年在第二次大战期间,丘吉尔以花甲高龄临危受命,力挽狂澜,处境险恶,压力巨大。然而,丘吉尔的思考模式取向仍然是离不开“大局观”及“细算力”的。

    坐上这个位置的人,是具有大局观和细算力的人。他们对企业的绩效直接负责,以企业竞争为导向,制定公司正常运作的战略保证公司的正常运转。他们在制定运营竞争策略时,我觉得他们往往都会遵循竞争战略之父迈克尔·波特的竞争战略理论观点:

    1.总成本领先战略。这是在某一产业领域内使成本低于竞争对手而取得领先地位的战略,其着眼点是取得价格竞争优势。在这种战略下,一般是运营系统具有一定的规模优势和技术高、产量大等优势。成本领先战略要求企业加强对费用的控制,以及最大限度地减小研究开发、服务、推销、广告等方面的成本。为了达到这些目标,有必要在管理方面对成本控制给予高度重视。尽管质量、服务以及其它方面也不容忽视,但贯穿于整个战略中的主题是使成本低于竞争对手。显然,处于低成本地位的企业可以获得高于行业平均水平的收益。因为它的低成本意味着当别的企业在竞争过程中已失去利润时,这个公司仍然可以获取利润。

    2.差异化战略。这种战略要求运营系统与其竞争特色的优势相适应,但也要注意成本因素。这种战略是通过公司所有部门的努力,对公司产品在一个或几个方面与竞争对手的产品有所不同。如产品特殊的功能、高超的质量、优质的服务、独特的品牌等等。这种战略将增加公司在产品设计、研发等方面的投入,使产品的成本上升。但是,顾客对产品的偏爱而愿意接受较高的价格,这将弥补公司采用差别化战略而带来的成本上升。但在很多公司里,管理者能够把成本控制在比竞争对手低的同时,将其产品进行差别化。

    3.目标集聚战略。实际上是一种市场细分市场战略,这种战略的前提是企业能够以更高的效率、更好的效果为某一细分市场中的特殊顾客群服务,采用目标集聚战略的企业也具有赢得超过行业平均收益水平的潜力。采用目标集聚战略的公司通常将全力集中在某一特定区域的市场或顾客群。这类公司要么采用低成本战略,要么采用差别化战略,但仅关注于特定的目标市场。采用低成本战略的公司,将资源集中在整个市场的一个或几个细分市场,旨在成为服务于该细分市场的最低成本的公司。

    其实我们在一些基本的运营推广工作的时候,往往也会涉及到一个人是否有大局观的表现。比如我们在做推广及线下的策划上,有经验的推广人员知道很多外链的方式,但是该在哪里增加,增加多少,增加后要达到什么效果都需要网站运营人员进行规划。不能为了留外链而不加限制地发布外链,需要从网站权重、流量、相关性、品牌效应、转化率等方面进行思考。优秀的网络推广,不仅是外链,更重要的是品牌效应,品牌能够遍布在网络各个角落,并且曝光率高,建立网站品牌是网络推广的最终目的。有品牌,有信任,有流量,也就有转化率。所以在我们的微观工作中也时时体现着全局观。

    因此我们又会说,其实初级运营和高级运营没有严格的分水岭,它就体现在我们工作、生活中、只不过其所表现的工作阅历、环境、位置、情况、和职责大小程度不尽相同而已。

    展开全文
  • 合格的初级前端工程师需要掌握的模块笔记

    千次阅读 多人点赞 2021-02-04 09:43:23
    文章目录一合格的初级前端工程师需要掌握的模块笔记前言Web模块html基本结构标签属性事件属性文本标签多媒体标签列表表格表单标签其他语义化标签网页结构模块划分CSS代码语法CSS 放置位置CSS的继承选择器的种类...

    一个合格的初级前端工程师需要掌握的模块笔记

    文章目录


    Github来源:一个合格的初级前端工程师需要掌握的模块笔记 | 求星星 ✨ | 给个❤️关注,❤️点赞,❤️鼓励一下作者

    大家好,我是魔王哪吒,很高兴认识你~~

    哪吒人生信条:如果你所学的东西 处于喜欢 才会有强大的动力支撑

    每天学习编程,让你离梦想更新一步,感谢不负每一份热爱编程的程序员,不论知识点多么奇葩,和我一起,让那一颗四处流荡的心定下来,一直走下去,加油,2021加油!欢迎关注加我vx:xiaoda0423,欢迎点赞、收藏和评论

    不要害怕做梦,但是呢,也不要光做梦,要做一个实干家,而不是空谈家,求真力行。

    前言

    如果这篇文章有帮助到你,给个❤️关注,❤️点赞,❤️鼓励一下作者,接收好挑战了吗?文章公众号首发,关注 程序员哆啦A梦 第一时间获取最新的文章

    ❤️笔芯❤️~

    Web模块

    html基本结构

    1. html标签是由<>包围的关键词。
    2. html标签通常成对出现,分为标签开头和标签结尾。
    3. 有部分标签是没有结束标签的,为单标签,单标签必须使用/结尾。
    4. 页面所有的内容,都在html标签中。
    5. html标签分为三部分:标签名称,标签内容,标签属性。
    6. html标签具有语义化,可通过标签名能够判断出该标签的内容,语义化的作用是网页结构层次更清晰,更容易被搜索引擎收录,更容易让屏幕阅读器读出网页内容。
    7. 标签的内容是在一对标签内部的内容。
    8. 标签的内容可以是其他标签。

    标签属性

    1. class属性:用于定义元素的类名
    2. id属性:用于指定元素的唯一id,该属性的值在整个html文档中具有唯一性
    3. style属性:用于指定元素的行内样式,使用该属性后将会覆盖任何全局的样式设定
    4. title属性:用于指定元素的额外信息
    5. accesskey属性:用于指定激活元素的快捷键
    6. tabindex属性:用于指定元素在tab键下的次序
    7. dir属性:用于指定元素中内容的文本方向,属性只有ltrrtl两种
    8. lang属性:用于指定元素内容的语言

    事件属性

    1. window窗口事件,onload,在网页加载结束之后触发,onunload,在用户从网页离开时发生(点击跳转,页面重载,关闭浏览器窗口等)
    2. form表单事件,onblur,当元素失去焦点时触发,onchange,在元素的值被改变时触发,onfocus,当元素获得焦点时触发,onreset,当表单中的重置按钮被点击时触发,onselect,在元素中文本被选中后触发,onsubmit,在提交表单时触发
    3. keyboard键盘事件,onkeydown,在用户按下按键时触发,onkeypress,在用户按下按键后,按着按键时触发。该属性不会对所有按键生效,不生效的有,alt,ctrl,shift,esc
    4. mouse鼠标事件,onclick,当在元素上发生鼠标点击时触发,onblclick,当在元素上发生鼠标双击时触发,onmousedown,当元素上按下鼠标按钮时触发,onmousemove,当鼠标指针移动到元素上时触发,onmouseout,当元素指针移出元素时触发,onmouseup,当元素上释放鼠标按钮时触发。Media媒体事件,onabort,当退出时触发,onwaiting,当媒体已停止播放但打算继续播放时触发。

    文本标签

    1. 段落标签<p></p>,段落标签用来描述一段文字
    2. 标题标签<hx></hx>,标题标签用来描述一个标题,标题标签总共有六个级别,<h1></h1>标签在每个页面中通常只出现一次
    3. 强调语句标签,<em></em>,用于强调某些文字的重要性
    4. 更加强调标签,<strong></strong><em>标签一样,用于强调文本,但它强调的程度更强一些
    5. 无语义标签<span></span>,标签是没有语义的
    6. 短文本引用标签<q></q>,简短文字的引用
    7. 长文本引用标签<blockquote></blockquote>,定义长的文本引用
    8. 换行标签<br/>

    多媒体标签

    1. 链接标签,<a></a>
    2. 图片标签,<img/>
    3. 视频标签,<video></video>
    4. 音频标签,<audio></audio>

    列表

    1. 无序列表标签,ul,li<ul></ul>列表定义一个无序列表,<li></li>代表无需列表中的每一个元素
    2. 有序列表,ol,li
    3. 定义列表,<dl></dl>,定义列表通常和<dt></dt><dd></dd>标签一起使用

    表格

    1. 表格标签<table></table>
    2. 表格的一行<tr></tr>
    3. 表格的表头<th></th>
    4. 单元格<td></td>
    5. 表格合并,同一行内,合并几列colspan="2",同一列内,合并几行rowspan="3"

    表单标签

    1. 表单标签<form>

    <form></form>表单是可以把浏览者输入的数据传送到服务器端,这样服务器端程序就可以处理表单传过来的数据。

    <form method="传送方式" action="服务器文件">

    action,浏览者输入的数据被传送到的地方

    method,数据传送的方式

    1. 输入标签<input/>

    name:为文本框命名,用于提交表单,后台接收数据用。

    value:为文本输入框设置默认值。

    type:通过定义不同的type类型,input的功能有所不同。

    text	单行文本输入框
    password	密码输入框(密码显示为***)
    radio	单选框 (checked属性用于显示选中状态)
    checkbox	复选框(checked属性用于显示选中状态)
    file	上传文件
    button	普通按钮
    reset	重置按钮(点击按钮,会触发form表单的reset事件)
    submit	提交按钮(点击按钮,会吃饭form表单的submit事件)
    email	专门用于输入 e-mail
    url	专门用于输入 url
    number	专门用于number
    range	显示为滑动条,用于输入一定范围内的值
    date	选取日期和时间(还包含:month、week、time、datetime、datetime-local)
    color	选取颜色
    

    button按钮,下拉选择框<select></select>

    <option value="提交值">选项</option>是下拉选择框里面的每一个选项
    
    1. 文本域:<textarea></textarea>,当用户想输入大量文字的时候,使用文本域。cols,多行输入域的列数,rows,多行输入域的行数。

    其他语义化标签

    1. 盒子<div></div>
    2. 网页头部<header></header>html5新增语义化标签,定义网页的头部,主要用于布局,分割页面的结构
    3. 底部信息<footer></footer>html5新增语义化标签,定义网页的底部,主要用于布局,分割页面的结构
    4. 导航<nav></nav>html5新增语义化标签,定义一个导航,主要用于布局,分割页面的结构
    5. 文章<article></article>html5新增语义化标签,定义一篇文章,主要用于布局,分割页面的结构
    6. 侧边栏<aside></aside>,语义化标签,定义主题内容外的信息,主要用于布局,分割页面的结构。
    7. 时间标签<time></time>,语义化标签,定义一个时间

    网页结构

    1. <!DOCTYPE html> 定义文档类型,告知浏览器用哪一种标准解释HTML
    2. <html></html>可告知浏览器其自身是一个 HTML 文档
    3. <body></body>标签之间的内容是网页的主要内容
    4. <head></head>标签用于定义文档的头部,它是所有头部元素的容器
    5. <title></title>元素可定义文档的标题
    6. <link>标签将css样式文件链接到HTML文件内
    7. <meta>定义文档的元数据

    模块划分

    1. 常见的企业网站,多由头部区,展示图片区域,主题区域,底部信息区域组成
    2. 网页拆分原则: – 由上到下 - 由内到外

    CSS代码语法

    1. CSS全称为层叠样式表(Cascading Style Sheets),它主要是用于定义HTML内容在浏览器内的显示样式,如文字大小、颜色、字体加粗等。
    2. css代码通常存放在<style></style>标签内
    3. css 样式由选择符和声明组成,而声明又由属性和值组成
    4. 选择符{属性:值}
    5. 选择符:又称选择器,指明网页中要应用样式规则的元素

    CSS 放置位置

    1. 行内样式,不建议使用
    2. 内联式样式表
    3. 外联样式表

    CSS的继承

    1. CSS的某些样式是具有继承性的,继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代。
    2. 不可继承样式:display、margin、border、padding、background、height、min-height、max-height、width、min-width、max-width、overflow、position、left、right、top、bottom、z-index、float、clear
    3. 可以继承的样式:letter-spacing、word-spacing、white-space、line-height、color、font、font-family、font-size、font-style、font-variant、font-weight、text-decoration、text-transform、direction、visibility、cursor

    选择器的种类

    1. 标签选择器:通过标签的名字,修改css样式
    2. 通配符选择器:选择页面中所有的元素
    3. 属性选择器
    4. 后代选择器:选择某个父元素下面所有的元素
    5. 一级子元素选则器:选择某个父元素的直接子元素,后代选择器是选择父元素的所有子孙元素,一级子元素原则器只选择第一级子元素,不会再向下查找元素
    6. id选择器:通过id查找页面中唯一的标签
    7. class选择器:通过特定的class(类)来查找页面中对应的标签,以 .class名称
    8. 伪类选择器::hover鼠标移入某个元素;:before在某个元素的前面插入内容;:after在某个元素的后面插入内容
    9. 群组选择器:可以对多个不同的选择器设置相同的样式

    选择器的优先级

    1. 当有不同的选择器对同一个对象进行样式指定时,并且两个选择器有相同的属性被赋予不同的值时。
    2. 通过测算那个选择器的权重值最高,应用哪一个选择器的样式
    3. 权重计算方式:
    标签选择器:1
    
    class选择器:10
    
    id选择器:100
    
    行内样式:1000
    
    !important 最高级别,提高样式权重,拥有最高级别
    

    背景样式

    1. 背景颜色background-color
    2. 背景图片background-image
    background-image:url(bg01.jpg);
    
    1. 背景图片位置background-position
    background-position:10px 100px;
    
    // 代表坐标x,y轴
    
    1. 背景图片重复background-repeat
    background-repeat:no-repeat
    
    // no-repeat 设置图像不重复,常用
    
    // round 自动缩放直到适应并填充满整个容器
    
    // space 以相同的间距平铺且填充满整个容器
    
    1. 背景图片定位background-attachment
    background-attachment:fixed
    
    // 背景图像是否固定或者随着页面的其余部分滚动
    
    // background-attachment的值可以是scroll(跟随滚动),fixed(固定)
    
    1. background缩写
    background:#ff0000 url(bg01.jpg) no-repeat fixed center
    

    字体样式

    1. 字体族font-family
    font-family:"微软雅黑","黑体";
    
    1. 字体大小font-size
    font-size:12px;
    

    网页默认字体大小是16px

    1. 字体粗细font-weight
    font-weight:400;
    
    normal(默认)
    bold(加粗)
    bolder(相当于<strong>和<b>标签)
    lighter (常规)
    100 ~ 900 整百(400=normal,700=bold)
    
    1. 字体颜色color
    颜色的英文单词color:red;
    
    十六进制色:color: #FFFF00;
    
    RGB(红绿蓝)color:rgb(255,255,0)
    
    RGBA(红绿蓝透明度)A是透明度在0~1之间取值。color:rgba(255,255,0,0.5)
    
    1. 字体斜体font-style
    font-style:italic
    
    normal 文本正常显示
    
    italic 文本斜体显示
    
    oblique 文本倾斜显示
    

    文本属性

    1. 行高line-height
    line-height:50px;
    

    可以将父元素的高度撑起来

    1. 文本水平对齐方式text-align
    left 左对齐
    center 文字居中
    right 右对齐
    
    1. 文本所在行高的垂直对齐方式vertical-align
    baseline 默认
    
    sub 垂直对齐文本的下标,和<sub>标签一样的效果
    
    super 垂直对齐文本的上标,和<sup>标签一样的效果
    
    top 对象的顶端与所在容器的顶端对齐
    
    text-top 对象的顶端与所在行文字顶端对齐
    
    middle 元素对象基于基线垂直对齐
    
    bottom 对象的底端与所在行的文字底部对齐
    
    text-bottom 对象的底端与所在行文字的底端对齐
    
    1. 文本缩进text-indent
    text-indent:2em;
    

    通常用在段落开始位置的首行缩进

    1. 字母之间的间距letter-spacing
    2. 单词之间间距word-spacing
    3. 文本的大小写text-transform
    capitalize 文本中的每个单词以大写字母开头。
    
    uppercase 定义仅有大写字母。
    
    lowercase 定义仅有小写字母。
    
    1. 文本的装饰text-decoration
    none 默认。
    underline 下划线。
    overline 上划线。
    line-through 中线。
    
    1. 自动换行word-wrap
    word-wrap: break-word;
    

    基本样式

    1. 宽度width
    width:200px;
    定义元素的宽度
    
    1. 高度height
    height:300px
    元素默认没有高度
    需要设置高度
    可以不定义高度,让元素的内容将元素撑高
    
    1. 鼠标样式cursor

    定义鼠标的样式cursor:pointer

    default默认
    pointer小手形状
    move移动形状
    
    1. 透明度opacity
    opacity:0.3
    
    透明度的值0~1之间的数字,0代表透明,1代表完全不透明
    
    透明的元素,只是看不到了,但是还占据着文档流
    
    1. 可见性visibility
    visibility:hidden;
    
    visible 元素可见
    
    hidden 元素不可见
    
    collapse 当在表格元素中使用时,此值可删除一行或一列,不会影响表格的布局。
    
    1. 溢出隐藏 overflow

    设置当对象的内容超过其指定高度及宽度时如何显示内容

    visible 默认值,内容不会被修剪,会呈现在元素框之外。
    hidden 内容会被修剪,并且其余内容是不可见的。
    scroll 内容会被修剪,但是浏览器会显示滚动条以便查看其余的内容。
    auto 如果内容被修剪,则浏览器会显示滚动条以便查看其余的内容。
    
    1. 边框颜色outline

    input文本输入框自带边框,且样式丑陋,我们可以通过outline修改边框

    outline:1px solid #ccc;
    
    outline:none清除边框
    

    样式重置

    早期的网页没有css样式,为了界面美观,很多元素自带margin、padding等样式,但这些样式在不同浏览器解析的值都不一样,需要将css样式重置,保证在不同浏览器显示一致。

    清除元素的margin和padding
    去掉自带的列表符
    去掉自带的下划线
    

    盒模型样式

    1. 块状元素、内联元素和内联块状元素。

    块级元素:

    • 每个块级元素都从新的一行开始,并且其后的元素也另起一行。
    • 元素的高度、宽度、行高以及顶和底边距都可设置。
    • 元素宽度在不设置的情况下,是它本身父容器的100%,除非设定一个宽度。

    行内元素:

    • 和其他元素都在一行上
    • 元素的高度、宽度、行高及顶部和底部边距不可设置
    • 元素的宽度就是它包含的文字或图片的宽度,不可改变。

    行内块状元素:

    • 和其他元素都在一行上
    • 元素的高度、宽度、行高以及顶和底边距都可设置。
    1. 元素分类转换display

    block:将元素转换为块级元素

    inline:将元素装换为行级元素

    inline-block:将元素转换为内联块元素

    none: 将元素隐藏

    1. 描边border
    border:2px solid #f00;
    

    线条的样式:

    dashed(虚线)| dotted(点线)| solid(实线)。
    

    css样式中允许只为一个方向的边框设置样式:

    下描边border-bottom:1px solid red;
    
    上描边border-top:1px solid red;
    
    右描边border-right:1px solid red;
    
    左描边border-left:1px solid red;
    
    1. 间距margin
    div{margin:20px 10px 15px 30px;}
    
    1. 内填充padding
    padding:10px
    

    浮动float

    1. 浮动原理
    • 浮动使元素脱离文档普通流,漂浮在普通流之上的。

    • 浮动元素依然按照其在普通流的位置上出现,然后尽可能的根据设置的浮动方向向左或者向右浮动,直到浮动元素的外边缘遇到包含框或者另一个浮动元素为止,且允许文本和内联元素环绕它。

    • 浮动会产生块级框(相当于设置了display:block),而不管该元素本身是什么。

    1. 清除浮动带来的影响

    clear 清除浮动:

    none : 不清除(默认值)。
    
    left : 不允许左边有浮动对象
    right : 不允许右边有浮动对象
    both : 不允许两边有浮动对象
    
    1. 利用伪类实现清除浮动
    .clearFix {
      	content="";
      	display:block;
      	width:0;
      	height:0;
      	clear:both;
    }
    

    定位position

    1. 定位功能可以让布局变的更加自由。
    2. 层模型–绝对定位(相对于父类)

    绝对定位使元素的位置与文档流无关,因此不占据空间。这一点与相对定位不同,相对定位实际上被看作普通流定位模型的一部分,因为元素的位置相对于它在普通流中的位置。

    #box_relative {
      position: absolute;
      left: 30px;
      top: 20px;
    }
    

    如下图所示:

    如果想为元素设置层模型中的绝对定位,需要设置position:absolute(绝对定位),这条语句的作用将元素从文档流中拖出来,然后使用left、right、top、bottom属性相对于其最接近的一个具有定位属性的父包含块进行绝对定位。如果不存在这样的包含块,则相对于body元素,即相对于浏览器窗口。

    1. 层模型–相对定位(相对于原位置)

    相对定位是一个非常容易掌握的概念。如果对一个元素进行相对定位,它将出现在它所在的位置上。然后,可以通过设置垂直或水平位置,让这个元素“相对于”它的起点进行移动。

    #box_relative {
      position: relative;
      left: 30px;
      top: 20px;
    }
    

    如下图所示:

    如果想为元素设置层模型中的相对定位,需要设置position:relative(相对定位),它通过left、right、top、bottom属性确定元素在正常文档流中的偏移位置。相对定位完成的过程是首先按static(float)方式生成一个元素(并且元素像层一样浮动了起来),然后相对于原位置移动,移动的方向和幅度由left、right、top、bottom属性确定,偏移前的位置保留不动。

    1. 层模型–固定定位(相对于网页窗口)
    position:fixed
    

    absolute定位类型类似,但它的相对移动的坐标是视图(屏幕内的网页窗口)本身。由于视图本身是固定的,它不会随浏览器窗口的滚动条滚动而变化,除非你在屏幕中移动浏览器窗口的屏幕位置,或改变浏览器窗口的显示大小,因此固定定位的元素会始终位于浏览器窗口内视图的某个位置,不会受文档流动影响,这与background-attachment:fixed属性功能相同。

    浏览器默认样式

    1. 页边距

    IE默认为10px,通过bodymargin属性设置

    要清除页边距一定要清除这两个属性值

    body { margin:0; padding:0;}
    
    1. 段间距

    IE默认为19px,通过pmargin-top属性设置

    p默认为块状显示,要清除段间距,一般可以设置

    p { margin-top:0; margin-bottom:0;}
    

    html5

    HTML5 的优势

    1. 解决跨浏览器,跨平台问题
    2. 增强了 web 的应用程序

    HTML5 废弃元素

    frame frameset noframes
    acronym applet dir
    basefont big center font strike tt
    

    HTML5 新增元素

    <header>	用于定义文档或节的页眉
    
    <footer>	用于定义文档或节的页脚
    
    <article>	用于定义文档内的文章
    
    <section>	用于定义文档中的一个区域(或节)
    
    <aside>	用于定义与当前页面或当前文章的内容几乎无关的附属信息
    
    <figure>	用于定义一段独立的引用,经常与说明(caption)<figcaption>配合使用,通常用在主文中的图片,代码,表格等。
    
    <figcaption>	用于表示是与其相关联的引用的说明/标题,用于描述其父节点<figure>元素里的其他数据。
    
    <hgroup>	用于对多个<h1>~<h6>元素进行组合
    
    <nav>	用于定义页面上的导航链接部分
    
    <mark>	用于定义高亮文本
    
    <time>	用于显示被标注的内容是日期或时间(24小时制)
    
    <meter>	用于表示一个已知最大值和最小值的计数器
    
    <progress>	用于表示一个进度条
    
    <audio>	定义声音,比如音乐或其他音频流
    
    <video>	定义视频,比如电影片段或其他视频流
    

    HTML5 表单相关元素和属性

    input新增type类型
    
    color	用来创建一个允 许用户使用颜色选择器,或输入兼容 CSS 语法的颜色代码的区域
    
    time	生成一个时间选择器
    
    datetime	生成一个 UTC 的日期时间选择器
    
    datetime-local	生成一个本地化的日期时间选择器
    
    date	显示一个日期输入区域,可同时使用日期选择器,结果值包括年、月、日,不包括时间。
    
    month	生成一个月份选择器,它结果值包括年份和月份, 但不包括日期
    
    week	生成一个选择的几周的选择器
    
    email	生成一个 E-mail 输入框
    
    number	生成一个只能输入数字的输入框
    
    range	生成一个拖动条,通过拖动条,使得用户只能输入指定范围,指定步长的值
    
    search	生成一个专门用于输入搜索关键字的文本框
    
    tel	生成一个只能输入电话号码的文本框
    
    url	生成一个 URL 输入框
    
    HTML5 input新增属性
    
    placeholder	主要用在文本框,规定可描述输入字段预期值的简短的提示信息
    
    autocomplete	为了完成表单的快速输入,一般浏览器提供了自动补全的功能选择
    
    autofocus	当为某个表单控件增加该属性后,当浏览器打开这个页面, 这个表单控件会自动获得焦点
    
    list	为文本框指定一个可用的选项列表,当用户在文本框中输 入信息时,会根据输入的字符,自动显示下拉列表提示,供用户从中选择
    
    pattern	用于验证表单输入的内容,通常 HTML5 的 type 属性,比如 email、tel、 number、url 等,已经自带了简单的数据格式验证功能了,加上 pattern 属性后, 验证会更加高效
    
    novalidate	当提交表单时不对其进行验证
    
    required	必需在提交之前填写输入字段
    
    spellcheck	拼写检查,为<input>、<textarea>等元素新增属性
    
    formenctype	规定在发送到服务器之前应该如何对表单数据进行编码
    
    formtarget	带有两个提交按钮的表单,会提交到不同的目标窗口
    
    multiple	一次上传多个文件
    
    maxlength wrap	<textarea>新增<br />maxlength:用于规定文本区域最大字符数。<br />wrap:是否包含换号符(soft/ hard)
    

    css3

    CSS3 新增选择器

    1. 兄弟选择器

    元素 1 ~ 元素 2 第1个元素之后,所有的元素2都会被选择,且这些元素和第一个元素拥有同一个父元素(两个元素之间不一定要相邻)。

    1. 属性选择器
    • E[attribute^=value] 用于选取带有以指定值开头的属性值的元素
    • E[attribute$=value] 用于选取属性值以指定值结尾的元素
    • E[attribute*=value] 用于选取属性值中包含指定值的元素,位置不限,也不限制整个单词
    1. 伪类选择器
    • :root 选择文档的根元素,HTML 里,永远是<html>元素
    • :last-child 向元素添加样式,且该元素是它的父元素的最后一个子元素
    • :nth-child(n) 向元素添加样式,且该元素是它的父元素的第 n 个子元素
    • :nth-last-child(n) 向元素添加样式,且该元素是它的父元素的倒数第 n 个子 元素
    • :only-child 向元素添加样式,且该元素是它的父元素的唯一子元素
    • :first-of-type 向元素添加样式,且该元素是同级同类型元素中第一个元 素
    • :last-of-type 向元素添加样式,且该元素是同级同类型元素中最后一个 元素
    • :nth-of-type(n) 向元素添加样式,且该元素是同级同类型元素中第 n 个元 素
    • :nth-last-of-type(n) 向元素添加样式,且该元素是同级同类型元素中倒数第 n 个元素
    • :only-of-type 向元素添加样式,且该元素是同级同类型元素中唯一的元素
    • :empty 向没有子元素(包括文本内容)的元素添加样式
    1. 伪元素选择器
    • :enabled 向当前处于可用状态的元素添加样式,通常用于定义表单的样式或者超链接的样式
    • :disabled 向当前处于不可用状态的元素添加样式,通常用于定义表单的 样式或者超链接的样式
    • :checked 向当前处于选中状态的元素添加样式
    • :not(selector) 向不是 selector 元素的元素添加样式
    • :target 向正在访问的锚点目标元素添加样式
    • ::selection 向用户当前选取内容所在的元素添加样式

    CSS3 新增属性

    1. 新增背景属性
    • background-clip 设置背景覆盖范围 border-box/paddingbox/content-box
    • background-origin 设置背景覆盖的起点 border-box/paddingbox/content-box
    • background-size 设置背景的大小 cover/contain/长度/百分比
    1. 新增的字体文本相关属性
    • text-overflow 设置当文本溢出元素框时处理方式 clip/ellipsis
    • word-wrap 规定单词的换行方式 normal/break-word
    • word-break 规定自动换行的方式 normal/break-all/keep-all
    1. 新增盒模型属性
    • box-shadow 阴影 h-shadow v-shadow <br />blur spread color inset
    • resize 调整尺寸 none/both/horizontal
    • outline-offset 轮廓的偏移量 length/inherit

    新增变形动画属性

    1. transform
    • translate(x,y)
    • rotate(angle)
    • scale(x,y)
    • skew(angleX ,angleY)
    1. transform-origin

    表示元素旋转的中心点,默认值为 50% 50%。

    • 第一个值表示元素旋转中心点的水平位置,它还可以赋值 left、right、center、长度、百分比

    • 第二个值表示元素旋转中心点的垂直位置,它还可以赋值 top、bottom、 center、长度、百分比。

    3D 变形属性

    1. transform 3D函数

    transform 增加了三个变形函数:

    • rotateX:表示元素沿着 x 轴旋转。
    • rotateY:表示元素沿着 y 轴旋转。
    • rotateZ:表示元素沿着 z 轴旋转。
    1. transform-style用来设置嵌套的子元素在 3D 空间中显示效果。

    2. perspective设置成透视效果,透视效果为近大远小。

    3. perspective-origin设置 3D 元素所基于的 x 轴和 y 轴,改变 3D 元素的底部位置,该属性取值同 transform-origin,默认值为 50% 50%。

    4. backface-visibility用来设置当元素背面面向屏幕时是否可见,通常用于设置 不希望用户看到旋转元素的背面。

    它的属性值有visible(背面可见,默认值)、 hidden(背面不可见)两个。

    CSS3 的过渡属性

    • transition-delay 设置过渡的延迟时间
    • transition-duration 设置过渡的过渡时间
    • transition-timing-function 设置过渡的时间曲线
    • transition-property 设置哪条 CSS 使用过渡
    • transition 一条声明设置 所有过渡属性

    CSS3 的动画属性

    1. animation
    • @keyframes 定义动画选择器
    • animation-name 使用@keyframes 定义的动画
    • animation-delay 设置动画的持续动画时间
    • animation-timing-function 设置动画的时间曲线
    • animation-iteration-count 设置动画播放次数
    • animation-direction 设置动画反向播放
    • animation-play-state 设置动画播放状态
    • transition 一条声明设置所有动画属性

    CSS3 新增多列属性

    • column-count 设置元素应该被分隔的列数
    • column-width 设置列的宽度
    • columns 一条声明设置列宽和列数 column
    • column-gap 设置列之间的间隔
    • column-span 设置元素应该横跨的列数
    • column-rule-style 设置列之间间隔的样式
    • column-rule-color 设置列之间间隔的颜色
    • column-rule-width 设置列之间间隔的宽度
    • column-rule 一条声明设置列之间间 隔所有属性

    CSS3新增单位

    px、em、rem、vh、 vw和% 移动端长度单位

    使用CSS单位px、em、rem、vh、 vw等实现页面布局。

    • px:绝对单位,页面按精确像素展示
    • em:相对单位,基准点为父节点字体的大小,如果自身定义了font-size按自身来计算(浏览器默认字体是16px),整个页面内1em不是一个固定的值。

    em会根据父级元素的大小而变化,但是如果嵌套了多个元素,要计算它的大小,是很容易出错的,这样就引申出了rem

    • rem:相对单位,可理解为”root em”, 相对根节点html的字体大小来计算,CSS3新加属性。
    • %% 百分比,相对长度单位,相对于父元素的百分比值
    • vw、vh、vmin、vmax 主要用于页面视口大小布局

    vw:viewpoint width,视窗宽度,1vw等于视窗宽度的1%。

    vh:viewpoint height,视窗高度,1vh等于视窗高度的1%。

    • vmin:vwvh中较小的那个。
    • vmax:vwvh中较大的那个。

    弹性盒模型

    弹性盒模型的语法基础概念

    任何一个容器都可以指定弹性布局

    JavaScript

    JavaScript基础

    1. 外部引入js文件:通过<script src="main.js"></script>
    2. 关键词

    1. 变量名大小写敏感
    2. 命名规范

    JavaScript数据类型

    1. 字符串(String)
    2. 数字(Number)
    3. 布尔值(Boolean)
    4. 未定义(Undefined)
    //undefined有两种结果
    //1、真的没定义
    alert(typeof dada); //undefined
    
    //2、定义了,但是没有放东西进去
    var dada;
    alert(dada); //undefined
    

    undefined,表示未定义或只声明未给值的变量

    1. 对象(Object)

    js中内置了如下的对象:

    • Object 是所有JS对象的超类(基类),JS中的所有对象都是继承自Object对象的
    • Array 数组对象 定义数组属性和方法
    • Number 数字对象
    • Boolean 布尔对象 布尔值相关
    • Error 错误对象 处理程序错误
    • Function 函数对象 定义函数属性和方法
    • Math 数学对象
    • Date 日期对象
    • RegExp 对象正则表达式对象 定义文本匹配与筛选规则
    • String 字符串对象 定义字符串属性和方法

    算术运算

    var y = 3;
    

    强制转换

    1. 字符串转数字parseInt() parseFloat() isNaN()
    2. 数字转为字符串toString()

    赋值运算

    1. 复合的赋值运算符 += -= *= /= %=

    关系运算

    1. 关系运算:> < <= >= != == === ==和=== !=和!==

    “=”、“==”、“===”有什么区别?

    1. = 是赋值符号
    2. == 忽略数据类型的判断 是否相等
    3. === 数值和数据类型都要相等才判断为相等

    逻辑运算

    • 逻辑与&&
    • 逻辑或 ||
    • 逻辑非 !
    • 复合逻辑表达式

    三元运算

    条件运算符?:
    
    三元运算符:(比较表达式)?结果1:结果2
    

    分支循环

    程序运行的三大结构:顺序结构、选择结构、循环结构

    • 单分支选择:if语句
    • 双分支选择:if-else语句
    • 多分支语句:if-else if-else 语句

    switch

    语法格式

    switch(num){ //表达式
      case 1:
        //执行代码块1
        break;  //中断执行,跳出
        ...
      default:  //默认,其他都不是的情况下执行
      	//执行代码块
      	break;
    }
    
    //强调:break非常重要,如果不加break的话,程序会一直继续往下执行;
    

    while

    语法格式:

    while循环的特点:不知道具体执行的次数时,使用最合适

    while(条件表达式){
         //要重复执行的代码段 - 循环体
    }
    

    do-while

    语法格式:

    do{
      //循环体
    }while(循环条件判断);
    
    • do-while是先执行循环体,再检测循环条件。
    • do-while能保证循环体至少执行一次。
    • 其他循环无法保证循环至少执行一次。

    for

    for(1循环变量初始化;2循环条件判断;4循环变量的修改){
      3循环体
    }
    

    break和continue

    1. break 退出循环
    2. continue 跳过本次循环,继续下一次循环

    数组

    1. 数组定义
    var arr = new Array();
    var arr = [];
    
    1. 字面量方式定义
    var arr = ["1","2"];
    
    1. 向数组赋值
    arr[0] = "1";
    arr[1] = "2";
    alert(arr[0]+","+arr[1]);
    
    1. 数组索引
    arr[0]+","+arr[1]
    
    1. 数组长度
    //语法
    arr.length
    
    //最后一个元素的索引
    arr.length-1
    

    数组方法

    1. indexOf

    数组可以通过indexOf()来搜索一个指定的元素的位置,如未找到返回 -1

    1. concat

    concat()方法把当前的 数组 和 另一个 数组连接起来,并返回一个新的 数组

    var newArr = arr1.concat(arr2,"dada");
    
    1. push和pop

    push()向数组的末尾添加若干元素,pop() 则把 数组的最后一个元素删除掉

    arr.push("a","b");
    console.log(arr);
    
    arr.pop();
    console.log(arr);
    
    //空数组继续pop不会报错,而是返回undefined
    
    1. unshift和shift

    unshift() 向数组前面添加若干元素,shift() 则把数组的第一个元素删除掉

    arr.unshift("a","b");
    arr.shift();
    
    1. slice

    slice() 截取数组的部分元素,然后返回一个新的数组

    console.log(arr.slice(0,3)); //从索引0开始,到索引3结束,但不包括3
    
    console.log(arr.slice(3));  //从索引3开始到结束
    

    如果不给slice()传递任何参数,就会从头到尾截取所有元素。利用这一点,可以很容易的复制一份新的数组

    1. sort

    sort() 可以对当前数组排序

    var arr = ["b","c","a"];
    arr.sort();
    arr;//["a","b","c"]
    
    1. reverse

    reverse() 把整个数组的元素给掉个个

    1. join

    join() 方法把数组的每个元素用指定的字符串连接起来

    var arr = ["a","b","c"];
    arr.join("-"); //"a-b-c"
    
    1. splice

    可以从指定的索引开始删除若干元素,然后再从该位置添加若干元素

    二维数组

    var arr = [[1,2,3],["a","b","c"],"dadaqianduan"];
    var x = arr[1][1]; //b
    

    字符串

    1. 字符串属性length-字符串的长度属性
    2. slice()
    slice(start[,end]),start--开始索引 end--结束索引
    
    1. substr()
    substr(start[,length]),start:开始,取length个字符
    
    1. split()

    split([separator[,limit]]),按条件分割字符串,返回数组

    1. indexOf()

    在父串中首次出现的位置,从0开始!没有返回-1

    1. lastIndexOf()

    倒序查找

    1. charAt(index)

    charAt(index) 指定索引的字符

    1. toLowerCase()

    转小写

    1. toUpperCase()

    转大写

    正则表达式

    创建正则表达式

    var reg = new RegExp("a","i");
    // 将匹配字母a,第二个参数i,表示匹配时不分大小写
    

    元字符

    模式修饰符

    正则方法

    1. test方法

    检索字符串中指定的值。

    1. exec方法

    该方法用于检索字符串中的正则表达式的匹配,该函数返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null

    支持正则的 String方法

    js对象

    定义对象

    //使用new运算符
    var obj = new Object();
    
    //字面量
    var obj={
        name:"dadaqianduan",
        age:12,
        sex:"男"
    }
    

    对象的数据访问

    //用.语法
    obj.name
    
    //用[]语法
    obj["name"]
    

    JSON

    json(JavaScript Object Notation),是一种轻量级的数据交换格式。

    var man = {
      	"name":"dadaqianduan",
      	"age":12,
     	"sex":"男"
    };
    

    内置对象

    • Object 是所有JS对象的超类(基类),JS中的所有对象都是继承自Object对象的
    • Array 数组对象
    • Number 数字对象
    • Boolean 布尔对象
    • Error 错误对象
    • Function 函数对象
    • Math 数学对象
    • Date 日期对象
    • RegExp 对象正则表达式对象
    • String 字符串对象

    Math 方法

    • abs() 绝对值 (去除正负)
    • random() 随机数,0-1之间的随机数,1不会出现
    • round() 四舍五入
    • floor(x) 下舍入(向下取整)
    • ceil(x) 上舍入(向上取整)
    • max(x,y) x 和 y 中的最大值
    • min(x,y) x 和 y 中的最小值
    • cos(x) x的余弦
    • sin(x) x的正弦
    • sqrt(x) 返回x的平方根
    • pow(3,4) 返回34次方

    Date 方法

    • getFullYear() 返回 年(4位)
    • getMouth() 返回 月(0–11)
    • getDate() 返回 日期
    • getDay() 返回 星期 (0-6)
    • getHours() 返回 小时
    • getMinutes() 返回 分钟
    • getSeconds() 返回秒
    • getTime() 返回1970年1月1日午夜到指定日期(字符串)的毫秒数
    • setFullYear() 设置 年份
    • setMouth() 设置 月
    • setDate() 设置 天
    • setHours() 设置小时
    • setMinutes() 设置 分钟
    • setSeconds() 设置 秒
    • setTime() 使用毫秒的形式设置时间对象
    //判断闰年
    function runYear(year){
    	if(year%4==0 && year%100!=0 || year%400==0){
    		return true;
    	}
    };
    

    面向对象是一种编程思想

    1. 类是一个抽象的概念
    2. 对象:具体的事物
    3. 类是对象的抽象,对象是类的具体实例
    4. 类不占用内存,对象占用内存空间
    5. 对象的访问 声明对象
    6. 遍历对象 – for in 循环

    定义对象

    1. 字面量创建
    2. 工厂模式
    // 工厂模式中的函数,首字母大写
    
    function Cat(n,c){
        return {
            name:n,
            color:c,
            say:function(){
              alert("dadaqianduan")
            }
        }
    }
    
    1. 构造函数

    Javascript提供了一个构造函数(Constructor)模式。

    所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。

    对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。

    构造函数首字母大写

    构造函数中的this,指向的 实例化的对象

    function Cat(n,c){
      this.name=n;
      this.color=c;
    }
    

    生成实例对象

    var cat1 = new Cat("dadaqianduan","黄色")
    
    // 自动含有一个constructor属性,指向它们的构造函数
    

    实例:自动含有一个constructor属性,指向它们的构造函数

    alert(cat1.constructor == Cat); //true
    
    1. Javascript还提供了一个instanceof运算符

    验证 原型对象 与 实例对象 之间的关系。

    var txt = 'dadaqianduan';
    alert(txt instanceof String); //false
    
    var age = 123123;
    alert(age instanceof Number); //false
    
    var res = /\d/;
    alert(res instanceof RegExp); //true
    
    var arr = [];
    alert(arr instanceof Array); //true
    

    原型和原型链

    构造函数都有一个prototype属性,指向 另一个对象 。这个对象的 所有属性和方法,都会被构造函数的实例继承。

    所有的函数都是 Function 的实例。

    在构造函数上都有一个 原型 属性prototype,prototype也是一个对象;这个对象上有一个 constructor 属性,该属性指向的就是构造函数。

    实例对象上有一个_proto_属性,该属性也指向原型对象,该属性不是标准属性,不可以用在编程中,该属性用于浏览器内部使用。

    constructor

    1. constructor是构造函数 创建的实例的属性,该属性的作用是 指向 创建当前对象的 构造函数。
    son.constructor == parent; // true
    

    每个原型都有一个constructor属性,指向该关联的构造函数。

    function Person() {
    }
    
    console.log(Person===Person.prototype.constructor)  //true
    

    关系图:

    区分一下普通对象和函数对象

    function f1(){};
    var f2 = function(){};
    var f3 = new function(){};
    
    var o1 = {};
    var o2 = new Object();
    var o3 = new f1();
    
    console.log(typeof Object); //function
    console.log(typeof Function);//function
    console.log(typeof f1) //function
    console.log(typeof f2) // function
    console.log(typeof f3) //function
    console.log(typeof o1) //object
    console.log(typeof o2) //object
    console.log(typeof o3)// object
    
    1. JavaScript中,原型是一个对象,原型的作用是 实现对象的继承。
    2. JavaScript中的所有函数对象中,都存在一个属性,prototype,该属性对应当前对象的原型。
    3. 所有的JavaScript对象,都存在一个_proto_属性,_proto_属性指向实例对象的构造函数的原型。
    var p = new Person(); // 实例对象
    
    console.log(p._proto_ === Person.prototype); // true
    

    p是实例对象, Personp的构造函数。 p_proto_属性 指向 构造函数 Person的原型。

    js是如何通过原型进行继承的:

    var parent = function(name) {
     this.name = name;
    }
    
    parent.prototype.getName = function() {
     return this.name;
    }
    
    var son = new parent("dadaqianduan");
    
    console.log(son.getName()); // dadaqianduan
    

    son继承了parent的原型中的函数属性getName

    原型链

    除了Objectprototype的原型是null外,所有的对象 和 原型 都有自己的原型,对象的原型 指向 原型对象。

    在层级多的关系中,多个原型层层相连 则 构成了 原型链。

    在查找一个对象的属性时,如当前对象找不到该属性,就会沿着原型链一直往上查找,直到找到为止,如果到了原型链顶端,没找到,则返回undefined

    原型

    1. 所有引用类型都有一个__proto__属性
    2. 所有函数都有一个prototype属性
    3. 所有引用类型的__proto__属性指向它构造函数的prototype

    构造函数和实例原型之间的关系:

    Person(构造函数) 的 prototype 指向 Person.prototype

    1. __proto__

    每个对象,除null外,都有的属性叫__proto__,这个属性会指向该对象的原型。

    function Person() {
    }
    var person = new Person();
    console.log(person.__proto__ === Person.prototype); // true
    

    关系图:

    关系图:

    关系图:

    梳理:

    写一个构造函数Person,一般构造函数区别与普通函数要求首字母大写:

    function Person(){}
    

    prototype原型

    原型 是 一个对象,在原型prototype 上定义的属性,通过“继承”,实现 实例 也有这个属性。

    继承 是在 new操作符内部实现的。

    构造函数 内部 有个 prototype的属性,通过这个属性就能访问到 原型。

    Person是构造函数,Person.prototype是原型。

    1. 实例

    有构造函数,可以在原型上创建可继承的属性,通过new操作符创建实例:

    function Person(){}
    
    person = new Person()
    
    da = person instanceof Person // 检查person是否是Person的实例
    
    da // true
    
    // 继承
    
    function Person() {}
    
    Person.prototype.name = 'dadaqianduan.cn'
    
    person = new Person()
    
    da = person.name // 实例继承的属性
    
    da // 'dadaqianduan.cn'
    
    1. proto

    实例通过_proto_访问到原型。

    function Person() {}
    Person.prototype.name = 'dadaqianduan.cn'
    
    person = new Person()
    
    da = person.__proto__ === Person.prototype
    
    da // true
    
    1. constructor构造函数

    原型也可以通过constructor访问到构造函数

    function Person() {}
    Person.prototype.name = 'dadaqianduan.cn'
    person = new Person
    
    da = Person.prototype.constructor === Person
    da // true
    

    小结

    1. 所有引用类型(函数,数组,对象)都拥有__proto__属性。
    2. 所有函数拥有prototype属性。
    3. 每个实例对象(Object)都有一个私有属性,为__proto__指向它的构造函数的原型对象(prototype)。该原型对象也有一个自己的原型对象__proto__,层层向上直到一个对象的原型对象为nullnull 没有原型,并作为这个原型链中的最后一个环节。

    常用的JavaScript设计模式

    百度百科:

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

    使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

    单体模式

    单体是一个用来划分 命名空间并将一批相关的属性和方法组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次。

    特点:

    (1)可以来划分命名空间,从而清除全局变量所带来的危险。

    (2)利用分支技术来来封装浏览器之间的差异。

    (3)可以把代码组织的更为一体,便于阅读和维护。

    工厂模式

    工厂模式的定义:

    提供创建对象的接口,意思就是根据领导(调用者)的指示(参数),生产相应的产品(对象)。

    1. 创建一个对象常常需要复杂的过程,所以不适合在一个复杂的对象中。
    2. 创建对象可能会导致大量的重复代码,也可能提供不了足够级别的抽象。

    工厂就是把成员对象的创建工作转交给一个外部对象,好处在于消除对象之间的耦合(也就是相互影响)。

    分类:

    简单工厂模式:使用一个类,通常为单体,来生成实例。

    复杂工厂模式定义:将其成员对象的实列化推到子类中,子类可以重写父类接口方法以便创建的时候指定自己的对象类型。

    父类只对创建过程中的一般性问题进行处理,这些处理会被子类继承,子类之间是相互独立的,具体的业务逻辑会放在子类中进行编写。

    应用场景:

    以下几种情景下工厂模式特别有用:

    (1)对象的构建十分复杂;

    (2)需要依赖具体环境创建不同实例;

    (3)处理大量具有相同属性的小对象。

    单例模式

    单例模式定义了一个对象的创建过程,此对象只有一个单独的实例,并提供一个访问它的全局访问点。也可以说单例就是保证一个类只有一个实例,实现的方法一般是先判断实例存在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。

    使用闭包方式来实现单例:

    var single = (function(){
        var unique;
    
        function getInstance(){
        // 如果该实例存在,则直接返回,否则就对其实例化
            if( unique === undefined ){
                unique = new Construct();
            }
            return unique;
        }
    
        function Construct(){
            // ... 生成单例的构造函数的代码
        }
    
        return {
            getInstance : getInstance
        }
    })();
    

    unique是返回对象的引用,而 getInstance是静态方法获得实例。Construct 是创建实例的构造函数。

    可以通过 single.getInstance() 来获取到单例,并且每次调用均获取到同一个单例。这就是 单例模式 所实现的效果。

    应用场景:

    1. 单例模式是一种常用的模式,有一些对象我们往往只需要一个,比如全局缓存、浏览器的window对象。
    2. 借助单例模式,可以把代码组织的更为一致,方便阅读与维护。

    函数

    函数的定义

    // 使用关键字function 定义函数
    
    // 定义函数,吃饭
    function dada(){
      console.log("点餐");
      console.log("拿筷子");
      console.log("吃东西");
    }
    

    局部变量和全局变量

    1. 变量的作用域
    • 函数体内的变量:局部变量,仅在函数体内可以使用
    • 函数体外的变量:全局变量,对全局可见
    1. 局部变量
    function da(){
      var dadada = "dada";//局部变量
      alert(dadada);
    }
    da(); //调用
    alert(dadada); //报错访问不到,函数体外对dadada不可见
    
    1. 全局变量
    var da = "我是全局变量";
    function home(){
      var da = "我是局部变量";
      alert(da);
    }
    home();
    
    -----------------------
    
    var a=3;  //全局
    function da(){
      alert(a); //3
      var b=5;
      alert(b);//5
    }
    da();
    console.log(b); //报错,访问不到
    

    典型错误,不使用var声明的变量也是全局变量(不建议这样用)

    function dada(){         
    	da = "123";  //全局变量         
    }         
    dada();         
    alert(da);         
    

    返回值

    return的含义

    //理解返回值
    function getNum(){
     return 2; //return的作用,将函数的结果返回给当前函数名
    }
    var result = getNum(); //如果希望返回值保存,就放在变量中;
    console.log(result);  //2
    

    return使用方法

    • return只能返回一个数据
    • 如果函数中没有return,则返回undefined

    return可以用来结束一个函数

    function Fun(){
      console.log("helloweb");
      return;
      console.log("我还会执行吗?");
    }
    Fun();
    
    function fn(){
      for(var i=0;i<10;i++){
        if(i == 3){ //循环3次就return
        	break;   
        }
        console.log("谁最帅!"); //打印3次
      }
      return "看return会不会执行我"; //return不执行,break执行
    }
    

    匿名函数

    //使用函数表达式定义函数
    
    //say本质上是一个指向函数对象的变量,我们称为函数变量
    var say = function(){
      	console.log("hello");
    };
    say();
    
    var oBtn = document.getElementById("box");
    oBth.onclick = function(){
      alert("你点击我啦");
    }
    

    自运行函数

    示例:

    function fn(){
      //这里是代码
    }
    fn(); //运行fn函数
    
    ----------------------
    
    var fn = function(){
      //这里是代码
    }
    fn(); //运行fn函数
    
    ---------------------
    
    //(fn)() 等价于 fn()
    (function(){
      	//这里是代码
    })()
    

    闭包

    闭包 (closure)有权访问另外一个函数作用域中的变量的函数。

    创建闭包的常见方式有:

    • 在一个函数内部创建另外一个函数,并且把这个函数return出去。
    • 用函数为元素绑定事件,当事件发生时,还可以操作该函数中的变量。

    特性

    1. 可以读取其它函数内部的变量
    2. 让这些变量的值始终保持在内存中

    示例:

    //方式1----函数内部return一个函数
    function run(){
        var a=10;
        return function(){
            a++;
            console.log(a);
        };
    };
    
    var b=run();
    //alert(b);   //b是一个函数
    
    b();//可以访问另外一个作用域中变量的函数
    
    //方式2--函数内部为绑定事件
    function addClick(){
        var txt="abcd";
        document.getElementById('box').onclick=function(){
            alert(txt);
        }
    };
    addClick();
    
    //方式3--函数内部将变量作为回调函数的参数
    function play(num,fn){
        if(num>10){
            return fn && fn(num);
        };
    };
    
    var ss=play(20,function(n){
        return n+1;
    });
    
    console.log(ss);
    

    BOM

    BOM概述

    BOM(browser object model)浏览器对象模型

    BOM提供了独立于内容而与浏览器窗口进行交互的对象、载入页面的相关信息,其核心对象是window对象

    BOM 没有相关标准,每个浏览器都定义了自己的属性,并且,都有其自己对BOM的实现方式

    W3C虽然没有为BOM统一制定标准,但是其中的窗口对象、导航对象等,因功能趋同,实际上已经成为默认的标准

    window方法

    • alert() 提示框
    • confirm() 带有确认 取消 按钮的提示框
    • prompt() 带有可输入内容的文本域的提示框
    • setInterval() 间隔定时器,可按照指定的周期(以毫秒计)来调用函数或计算表达式
    • setTimeout() 超时定时器,在指定的毫秒数后调用函数
    • clearInterval() 清除间隔定时器
    • clearTimeout() 清除超时定时器
    • requestAnimationFrame 帧定时器

    frames [ ] 框架集

    把浏览器窗口分成几个窗框,每个窗框同时取得多个URL地址,显示不同网页内容。

    history 历史记录

    • window.history.go(1) 前进(跳转)
    • window.history.go(-1) 后退(跳转)
    • window.history.forward() 前进
    • window.history.back() 后退

    location 定位

    • window.location.href='http://www.baidu.com/' 页面跳转
    • window.location.reload() 页面重载

    navigator 导航

    window.navigator.userAgent 浏览器类型、版本、操作系统类型、浏览器引擎类型等信息

    screen 屏幕

    window.screen.width 返回当前屏幕宽度(分辨率值)
    window.screen.height 返回当前屏幕高度(分辨率值)

    document 文档

    windowdocument的属性,代表所有html的元素,这部分是js主要操作的部分,因此这部分必须规范,才能进行统一开发。因此,W3C将这部分进行了规范—DOM标准。

    DOM

    DOM(document object model )文档对象模型,定义了表示和修改文档所需的对象、行为和属性,以及这些对象之间的关系。

    DOM对象方法

    • getElementById(id) 通过id获取DOM对象(对象)
    • getElementsByTagName(tag) 通过标签获取DOM对象(“类似数组”对象)
    • getElementsByName(name) 通过name获取DOM对象(“类似数组”对象)
    • getElementsByClassName(class) 通过class获取DOM对象(IE8以下不支持)

    操作DOM间的关系

    • createElement(tag) 创建元素
    • removeChild(对象) 删除元素
    • appendChild(对象) 插入元素
    • replaceChild(替换对象,被替换对象) 替换元素
    • insertBefore(对象,目标对象) 前部插入

    appendChild replaceChild insertBefore 都具有移动对象的功能

    节点属性:

    父节点	parentNode
    第一级所以子节点	childNodes
    第一个子节点	firstChild
    最后一个子节点	lastChild
    前一个兄弟节点	previousSbiling
    后一个兄弟节点	nextSibling
    

    克隆节点:

    cloneNode( 布尔值 ) 
    
    true:复制本节点以及所有子节点 
    false:只复制节点本身
    

    DOM节点属性

    • setAttribute("属性名",属性值) 设置属性
    • getAttribute( 属性名 ) 获取属性
    • removeAttribute( 属性名 ) 删除属性:
    • hasAttributes(属性名) 判断属性是否存在(返回ture/false

    事件

    var oDiv=document.getElementById('box');
    oDiv.onclick=function(){   
    	alert('hello world'); 
    };
    
    1. addEventListener( ) 增加事件监听
    2. removeEventListener( ) 删除事件监听

    事件分类

    • window事件
    1. onload 加载(某个页面或图像被完成)
    2. onunload 用户退出页面
    3. onresize 窗口或框架被调整尺寸
    4. onscroll 滚动条事件
    • 鼠标事件
    onclick 鼠标点击
    ondblclick 鼠标双击
    onmousedown 鼠标按键按下
    onmouseup 鼠标按键被松开
    onmouseout 鼠标从某元素移开
    onmouseover 鼠标被移到某元素之上
    onmouseenter 鼠标进入某元素
    onmouseleave 鼠标离开某元素
    onmousemove 鼠标移动
    oncontextmenu 右键菜单
    
    • input事件
    onblur 元素失去焦点。
    onfocus 元素获得焦点。input输入框
    onchange 内容改变时触发。
    
    • 键盘事件
    onkeydown 按键按下
    onkeypress 按键按下并释放
    onkeyup 按键释放
    
    • form事件
    onreset 表单重置(重置按钮)
    onsubmit 表单提交(form内有text被聚焦,直接回车可触发onsubmit)
    

    事件对象

    获取事件数据,不同的事件会有不同数据

    oDiv.onclick=function(ev){
     //ev就是事件对象
    }
    

    事件流

    1. 对象的默认行为
    2. 阻止默认行为:event.preventDefaut()

    事件流

    事件流方向:捕获 → 事件目标→冒泡

    1. 事件捕获
    2. 事件目标
    3. 事件冒泡 与 阻止事件冒泡

    事件目标

    ev.targrt
    ev.target.nodeName
    ev.target.tagName
    

    事件委派(delegate)

    原理: 将事件绑定在父级上,利用事件冒泡原理,通过判断事件的“目标元素”来触发父级上绑定的事件

    作用

    • 不用分别为子元素绑定事件
    • 为未知元素绑定事件

    事件监听

    可以为一个元素,同时绑定多个事件

    obj.addEventListener(事件,回调函数,冒泡/捕获) ;
    btn.addEventListener("click",function(ev){ //ev 事件对象
     alert('dadaqianduan');
    },false)   //false 冒泡阶段
    

    jQuery

    一款轻量级的js库
    丰富的DOM选择器
    简单的事件操作
    重新封装方法,让操作DOM属性更简单
    链式操作
    丰富的动画效果
    Ajax操作支持
    浏览器兼容
    插件扩展开发,可扩展性强
    
    不能向后兼容
    插件兼容性
    多个插件冲突
    

    jQuery 选择器

    • id选择器
    $('#box')
    
    • class选择器
    $('.box')
    
    • 标记选择器
    $('p')
    
    • * 代表所有标签

    属性选择器

    • [attribute] 匹配包含给定属性的元素
    • [attribute=value] 匹配给定的属性是某个特定值的元素
    • [attribute!=value] 匹配给定的属性不是某个特定值的元素
    • [attribute^=value] 匹配给定的属性是以某些值开始的元素
    • [attribute$=value] 配给定的属性是以某些值结尾的元素
    • [attribute*=value] 匹配给定的属性是以包含某些值的元素

    位置选择器

    :first匹配第一个元素

    :last获取最后一个元素

    :not 去除所有与给定选择器匹配的元素

    :even 匹配所有索引值为偶数的元素,从 0 开始计数

    :odd 匹配所有索引值为奇数的元素,从 0 开始计数

    :eq 匹配一个给定索引值的元素

    :gt 匹配所有大于给定索引值的元素

    :lt 匹配所有小于给定索引值的元素

    后代选择器

    $("选择器1 选择器2 ……")
    

    子代选择器

    $("选择器1>选择器2>……")
    

    选择器对象

    $("选择器").each(function(index){this}) 选择器对象的遍历
    $("选择器").find() 找前面选择器匹配到的元素的子元素
    $("选择器").not() 在前面选择器匹配到的元素中去除某个或某多个
    $("选择器").add() 在前面选择器中在追加节点
    

    子元素

    :first-child 匹配第一个子元素
    :last-child 匹配最后一个子元素
    :first-of-type 选择所有相同的元素名称的第一个兄弟元素
    :last-of-type 选择所有相同的元素名称的最后一个兄弟元素
    
    :nth-child 匹配其父元素下的第N个子或奇偶元素
    :nth-last-child() 选择所有他们父元素的第n个子元素。计数从最后一个元素开始到第一个
    :nth-last-of-type() 选择的所有他们的父级元素的第n个子元素,计数从最后一个元素到第一个
    :nth-of-type() 选择同属于一个父元素之下,并且标签名相同的子元素中的第n个
    
    :only-child 如果某个元素是父元素中唯一的子元素,那将会被匹配
    :only-of-type 选择所有没有兄弟元素,且具有相同的元素名称的元素。
    

    表单

    :input 匹配所有 input, textarea, select 和 button 元素
    :text 匹配所有的单行文本框
    :password 匹配所有密码框
    :radio 匹配所有单选按钮
    
    :checkbox 匹配所有复选框
    :submit 匹配所有提交按钮
    :image 匹配所有图像域
    :reset 匹配所有重置按钮
    
    :button 匹配所有按钮
    :file 匹配所有文件域
    :hidden 匹配所有隐藏域
    

    表单对象属性

    :enabled 匹配所有可用元素
    :disabled 匹配所有禁用元素
    
    :checked 匹配所有选中的被选中元素
    :selected 匹配所有选中的option元素
    

    DOM操作

    1. 查找获取
    text() - 设置或返回所选元素的文本内容
    html() - 设置或返回所选元素的内容
    val() - 设置或返回表单字段的值
    
    1. 内部插入
    append() 向每个匹配的元素内部追加内容
    appendTo() 把所有匹配的元素追加到另一个指定的元素集合中
    
    prepend() 向每个匹配的元素内部前置内容
    prependTo() 把所有匹配的元素前置到另一个、指定的元素集合中
    
    1. 外部插入
    after() 在每个匹配的元素之后插入内容
    before() 在每个匹配的元素之前插入内容
    
    insertAfter() 把所有匹配的元素插入到另一个、指定的元素集合的后面
    insertBefore() 把所有匹配的元素插入到另一个、指定的元素集合的前面
    
    1. 包裹
    wrap() 把所有匹配的元素用其他元素的结构化标记包裹起来
    unwrap() 这个方法将移出元素的父元素。
    
    wrapAll() 将所有匹配的元素用单个元素包裹起来
    wrapInner() 将每一个匹配的元素的子内容(包括文本节点)用一个HTML结构包裹起来
    
    1. 替换
    replaceWith() 将所有匹配的元素替换成指定的HTML或DOM元素
    replaceAll() 用匹配的元素替换掉所有 selector匹配到的元素
    
    1. 删除
    empty() 删除匹配的元素集合中所有的子节点
    remove() 从DOM中删除所有匹配的元素
    
    1. 克隆
    clone() 克隆匹配的DOM元素并且选中这些克隆的副本
    

    JQuery 事件

    页面载入

    $(document).ready(function(){
    
    })
    
    //简写方式
    $(function(){
    
    })
    

    事件绑定

    $("#box").on("click",function(){
        /**/
    })
    
    $("#box").off("click,mousemove");
    

    容器适应

    获取元素的宽高有以下几种方法:

    $(选择器).width() | innerWidth() | outerWidth()
    $(选择器).height() | innerHeight() | outerHeight()
    
    innerWidth()和innerHeight()是指元素里面内容的宽高加上内边距的宽高;
    outerWidth()和outerHeight()是指元素里面内容的宽高加上内边距的宽高和边框;
    

    获取窗口的宽高的方法如下:

    $(window).width()
    $(window).height()
    

    标签样式操作

    $(选择器).css (样式属性名[,值])	方法设置或返回被选元素的一个或多个样式 属性
    
    $(选择器).addClass(类别名)	增加类别样式
    
    $(选择器).removeClass(类别名)	去除类别样式
    
    $(选择器).toggleClass(类别名)	交替使用类别样式:有这个类别样式就去除,没有就追加;
    

    滑动

    show() 显示元素
    hide() 隐藏元素
    
    slidDown() 向下滑动显示
    slideUp() 向上滑动收起隐藏
    slideToggle() 交替滑动状态
    
    fadeIn() 淡入
    fadeOut() 淡出
    fadeTo() 动画到指定透明度
    fadeToggle() 交替淡出、淡入状态
    

    自定义动画

    animate() 自定义动画
    
    stop() 停止所有在指定元素上正在运行的动画
    
    delay() 设置一个延时来推迟执行队列中之后的项目
    
    finish() 停止当前正在运行的动画,删除所有排队的动画,并完成匹配元素所有的动画
    

    AJAX

    $.ajax()
    
    $.get() 通过远程 HTTP GET 请求载入信息
    
    $.post() 通过远程 HTTP POST 请求载入信息
    
    $.getJSON() 通过 HTTP GET 请求载入 JSON 数据
    

    工作原理

    AJAX = 异步 JavaScript 和 XML

    在浏览器中输入url地址请求服务器时,是通过Ajax发送http请求给服务器,服务的响应结果也是先返回给Ajax,先Ajax处理之后在返回给浏览器显示在页面。

    XMLHttpRequest对象

    //第一步:
    xhr = new XMLHttpRequest();
    
    //第二步
    xhr.open("post","test.php");
    
    //第三步:
    xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    
    //第四步:
    xhr.send("a=1&b=2");
    
    //第五步:
    xhr.onreadystatechange=function(){
        if(xhr.status==200 && xhr.readyState==4){
             var result=xhr.responseText;//获取到结果
    	alert(result);
    	}
    }
    

    XML和HTML的区别

    get() 和post()

    $.get(url,data,callback,dataType)
    $.post(url,data,callback,dataType)
    

    AJAX工作原理

    Ajax的基本格式如下:

    $.ajax({
    	url:'请求的资源',
    	type:'请求方式get|post',
    	data:'发送数据 名=值&名=值',
    	dataType:'回传值的类型',
    	success:function(res){ res接收返回值
    	}
    })
    

    HTTP

    HTTP(超文本传输协议)是一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。

    HTTP消息结构

    • request line

    请求行:第一行必须是请求行,用来说明请求类型、要访问的资源以及使用的HTTP版本。

    • header

    请求头:用来说明服务器要使用的附加信息。

    • blank line

    空白行:请求头部与请求体之间必须有一个空白行,必不可少

    • body

    请求体:也叫请求正文,可以添加任意的其他数据

    状态行:

    Host	接受请求的服务器地址,可以是:IP:端口 或 域名
    User-Agent	发送请求的应用程序名称(浏览器信息)
    Connection	指定与连接相关的属性,如:Connection:Keep-Alive
    Accept-Charset	客户端可以接受的编码格式
    Accept-Encoding	客户端可以接受的数据压缩格式
    Accept-Language	客户端可以接受的语言
    referer	当前请求来自哪个链接(防盗连)
    content-type	请求的文件类型
    cookie	该网站相关的会话信息
    

    url请求过程

    1. 首先客户端与服务器需要建立连接。
    2. 建立连接后,客户端发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、协议版本号,客户端信息和可能的内容。
    3. 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码status Code状态码),后边服务器信息实体信息和可能的内容。
    4. 客户端接收完, 服务器所返回的信息后,与服务器断开连接。

    如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。

    预加载

    预加载:将所有所需的资源提前请求加载到本地,后面在需要使用就直接从缓存中存取资源

    1. 使用image对象
    <img src="" style="display:none"/>
    
    1. 使用image对象
    var image = new Image();
    image.src="";
    
    1. 使用XMLHttpRequest对象
    xmlhttprequest.onreadystatechange=callback;
    xmlhttprequest.onprogress=progressCallback;
    
    xmlhttprequest.open("GET",http://xx.jpg,true);
    
    xmlhttprequest.send();
    
    function callback(){
    	if(xmlhttprequest.readyState=4 && xmlhttprequest.status==200){
            var responseText=xmlhttprequest.responseText;
        }else{
          console.log("Request was unsuccessful" + xmlhttprequest.status);
        }
    }
    
    function progressCallback(e){
      	c=e||event;
      	if(e.lengthComputable){
        	console.log("Received"+e.loaded+"of"+e.total+"bytes");
    	}
    }
    

    懒加载

    首屏加载,技术上显示要用的技术就是图片懒加载,即到可视区域再加载。

    性能优化

    JavaScript代码优化

    1. 代码与结构分离
    2. 样式与结构的分离
    3. 数据与代码分离
    • AMD:Asynchronous Module Definition,即异步模块加载机制。
    • CMD:Common Module Definition,即通用模块定义规范
    • 导出(export)与导入(import)两个模块

    提升文件加载速度

    1. 合并JavaScript代码,尽可能少的使用script标签。
    2. 无堵塞加载JavaScript
    3. 动态创建script标签来加载

    webpack

    webpack是一个module bundler(模块构建工具),由于 JavaScript 应用程序的复杂性不断增加,构建工具已成为 web 开发中不可或缺的一部分。它帮助我们去打包、编译和管理项目需要的众多资源文件和依赖库。

    webpack支持CommonJSAMD和ES6模块系统,并且兼容多种JS书写规范,可以处理模块间的依赖关系,所以具有更强大的JS模块化的功能,它能压缩图片,对CSS、js文件进行语法检查、压缩、编译打包。

    webpack的特点

    1. 可以很好的用于单页应用
    2. 同时支持 require()import 模块语法
    3. 允许非常前沿的 code splitting(代码分割) 特性
    4. 热加载可以让 React、Vue.js 和其它类似框架的本地开发更快
    5. 它是目前最受欢迎的构建工具

    webpack的缺点

    1. 不适合 web 开发的初学者
    2. 对于 CSS、图片和其它非 JS 资源文件时,需要先混淆处理
    3. 文档不够完善
    4. 变化很大,不同版本的使用方法存在较大差异

    安装

    1. 全局安装:
    //安装全局webpack
    npm install webpack -g
    
    //安装全局webpack-cli
    npm install webpack-cli -g
    

    webpack基本应用

    SPA(single page web application) 单页应用程序,是webpack打包的典型应用

    示例,主要的几个部分组成:

    index.html	主文件
    JS文件	可能有多个JS文件,可通过webpack合并打包为一个文件
    CSS文件	可能有多个CSS文件,可通过webpack合并打包为一个文件
    图片	可通过webpack压缩优化
    

    示例:

    //a.js
    var run=function(){
        console.log("aaa");
    };
    //node CommonJS模块
    //module.exports.run=run;
    
    //ES6语法
    export default {run};
    
    //b.js
    var play=function(arg){
        console.log(arg);
    };
    //node CommonJS模块
    //module.exports.play=play;
    
    //ES6语法
    export default {play};
    
    //index.js
    //node CommonJS 引入js模块
    //var a=require("./a.js");
    //var b=require("./b.js");
    
    //ES6 引入js模块
    import a from "./a.js";
    import b from "./b.js";
    
    var txt = "hello world";
    a.run();
    b.play(txt);
    

    dist文件夹(存放打包后的文件,可以先不创建,打包时可以自动创建)-dis,dist,bulit

    打包:

    webpack --mode development
    

    配置文件入门

    默认的配置文件名为:webpack.config.js

    核心概念

    一个配置文件的基本结构如下:

    //配置项
    module.exports={
        //入口  
        entry:".....",
    
        //输出配置     
        output:{......},
    
        //模块    
        module: {......},
    
        //解析         
        resolve:{......},        
    
        //插件(数组)
        plugins:[......],
    
        //开发服务器    
        devServer:{......}  
    };
    
    entry	入口 定义入口文件,默认入口文件:./src/index.js
    output	输出 定义出口文件,默认出口文件:./dist/main.js
    resolve	解析 路径映射、省略后缀名等
    module	模块 定义不同loader,让 webpack 能够处理非 JavaScript 模块
    plugins	插件 扩展webpack功能
    devServer	开发服务器 用于配置webpack-dev-server选项
    

    设置配置文件自动完成:

    // webpack是基于node构建的,只支持CommonJS模块
    module.exports={  
        //入口配置
        entry:'./src/js/main.js',  
    
        //出口配置
        output:{  
            path:__dirname +'/dist', //输出目录  __dirname:本文件所在硬盘路径(node全局变量)
            filename:'js/main.js' //文件名称(可以有子目录)
        }
    };
    

    修改webpack.json文件

    webpack.json中的"scripts"下增加:

    "scripts": {  
        "dev": "webpack --mode development",
        "build": "webpack --mode production"  
      },  
    

    执行打包

    npm run dev
    

    entry 和 output

    1. entry 入口配置 是指页面中的入口文件。也就是打包从哪个文件开始。默认入口文件:./src/index.js

    2. output 出口配置 是指生成的文件输出到哪个地方去,默认出口文件:./dist/main.js,主要有以下属性:

    path	输出路径
    filename	输出文件名
    

    示例:

    //入口
    entry: {   
        index: './src/js/main.js',  
    },
    //输出
    output: {
    	path: __dirname + "/dist",//打包后的文件存放的地方
    	filename:"main.js"//打包后输出的文件名
    },
    

    module

    webpack 只能打包js文件(只理解 JavaScript语法),无法识别其他语法的文件,如果要让webpack打包其他文件,首先需要让webpack识别不同文件,这就需要特别的模块,这种模块统称为loader

    loader分类

    转换编译	script-loader,babel-loader,ts-loader,coffee-loader
    处理样式	style-loader,css-loader,less-loader,sass-loader,postcss-loader
    处理文件	raw--loader,url-loader,file-loader
    处理数据	csv-loader,xml-loader
    处理模板语言	html-loader,pug-loader,jade-loader,markdown-loader
    清理和测试	mocha-loader,eslint-loader
    

    常用loader

    css-loader	解析css语句
    style-loader	将css-loader解析后的文本,添加<style>标签
    
    babel-loader	将ES6+、JSX语法转成ES5低版本语法
    
    url-loader	
    
    url-loader对未设置或者小于limit byte设置的图片以base64的格式进行转换 
    对于大于limit byte的图片用file-loader进行解析
    
    file-loader	
    
    解析项目中的url引入(包括img的src和background的url) 
    修改打包后文件引用路径,使之指向正确的文件
    
    less-loader	less编译器
    
    vue-loader	
    
    Vue也推出了自己的vue-loader,可以方便的打包 .vue文件 的代码
    在vue-cli(快速构建单页应用的脚手架)中得到应用。
    

    css loader

    //index.js
    import a from "./a.js";
    import b from "./b.js";
    var txt = "hello world";
    a.run();
    b.play(txt);
    
    //打包css文件
    import "./style.css";  //注意:是相对路径
    

    安装loaderloader也是依赖包,需要安装)

    npm install css-loader --save-dev
    npm install style-loader --save-dev
    

    module中定义ccs模块相关的配置:

    module: {
        rules: [
            {
                test: /.css$/, //正则
                //把css添加到html的style标签里(style-loader要先加载)
                loader: ['style-loader','css-loader'],  //loader或者use
                exclude: /node_modules/, //正则 排除node_modules目录
            }
        ]
    },
    

    babel loader

    babel是一个 js 编译器,它通过语法转换器支持最新版本的 JavaScript (包括JSX、TypeScript等新语法)。 这些插件允许你立刻使用新语法,无需等待浏览器支持。

    使用Babel首先要配置 .babelrc 文件,该文件用来设置转码规则和插件(json格式),存放在项目的根目录下。

    tips:在linux系统中,rc结尾的文件通常代表运行时自动加载的文件、配置等等。

    .babelrc配置文件中,主要是对预设(presets) 和 插件(plugins) 进行配置。.babelrc配置文件一般为如下:

    {
       "presets": [
         	["env",{"modules":false}] //modules是配置项
       ],  
    	"plugins": [
    		[ "transform-runtime",{"polyfill":false}]  //polyfill是配置项
    	]
    }
    

    预设 对js最新的语法糖进行编译,并不负责转译新增的api和全局对象。

    插件 控制如何转换代码,babel默认只转换新的js语法,而不转换新的API

    plugins

    插件(plugins)可以扩展webpack的功能,loader不能做的处理都能交给plugin来做。

    如:HtmlWebpackPlugin 插件简化了HTML文件的创建,可以通过模板文件,生成一个HTML文件

    resolve

    resolve(译:解析)配置webpack如何寻找模块对应的文件。

    alias	(译:别名)通过别名将原来导入路径映射成一个新的导入路径
    extensions	(译:扩展)数组 导入模块时,可以省略的文件后缀名
    
    resolve: {
        alias: {
            "@": path.join(__dirname,"./src") //将项目根目录下的src目录,映射为 @
        },
        extensions:[".js", ".json"]
    }
    

    其他配置项示例:

    devtool	是否生成以及如何生成sourcemap
    devserver	开启一个本地开发服务器
    watch	监听文件变化并自动打包
    watchoption	用来定制watch模式的选项
    performance	打包后命令行如何展示性能提示,如果超过某个大小是警告还是报错
    

    webpack-dev-server

    webpack-dev-server是一个小型的web服务器,可以自动监视项目文件的变化,自动刷新浏览器,其HMRHot Module Replacement 热模块替换)方式只替换更新的部分,而不是重载页面,大大提高了刷新效率。

    需要本地安装 webpack和webpack-cli
    npm install webpack --save-dev
    npm install webpack-cli --save-dev
    npm install webpack-dev-server --save-dev
    

    webpack.config.js配置文件:

    let path=require("path");
    
    //HtmlWebpackPlugin插件
    let HtmlWebpackPlugin=require('html-webpack-plugin');
    let htmlPlugin=new HtmlWebpackPlugin({  
        filename:"index.html", //生成的新文件
        template:__dirname+"/src/index_temp.html",  //模板文件
        minify:{ //压缩
            removeComments:true,  //刪除注释
            collapseWhitespace:true  //合并空格
        },
    });
    
    //配置项
    module.exports = {
        //输入
        entry:'./src/js/main.js',//主入口文件
    
        //输出
        output: {
            path: __dirname + "/dist",	//打包后的文件存放的地方
            filename:"main.js"			//打包后输出的文件名
        },
    
        //模块
        module: {
            rules: [
                {
                    test: /.css$/, //正则 解析css文件
                    //把css添加到html的style标签里(style-loader要先加载)
                    use: ['style-loader','css-loader'],
                    exclude: /node_modules/, //正则 必须要写exclude!!
                },
                {
    		        test: /\.js$/,
    		        use: 'babel-loader',
    		        exclude: /node_modules/, //正则 必须要写exclude!!
    		    },
            ]
        },
        //插件
        plugins:[
            htmlPlugin        
        ],
    
        //解析
        resolve: {
            alias: {
                "@": path.join(__dirname,"./src") //将项目根目录下的src目录,映射为 "@"
            },
            extensions:['.js', '.json']
        },
    
        //开发服务器
        devServer: {
            inline:true,	//支持dev-server自动刷新
            port:"8080",	//端口
            open:true,		//自动打开默认浏览器
        },
    }
    

    webpack-dev-serve.cmd是定义在.bin目录中的

    "scripts": {
        "dev": "webpack-dev-server --hot",
        "build": "webpack --mode production"
    }
    

    运行

    npm start
    

    vue

    MVC模式

    MVC模式是移动最广泛的软件架构之一,把应用程序强制性地划分为三部分:模型(Model)、视图(View)和控制器(Controller)。

    MVVM模式

    MVVM模式是把MVC模式的Controller改成ViewModelView的变化会自动更新ViewModel,ViewModel的变化也会自动同步到View上显示。

    基础语法

    示例:

    el	 	把 Vue 实例挂载到DOM元素上,通过id绑定html元素
    data	 	数据对象,Vue实例的数据(注意:数据不要与methods中的方法重名)
    methods	 	事件对象,包含事件所要触发的函数(注意:方法名不要与data中的数据重名)
    computed	 	计算属性
    watch	 	监听器
    directives	 	自定义指令
    钩子(hook)函数(8个)	 	hook(钩子)函数,不同生命周期引发的动作
    路由钩子函数(3个)	 	路由组件在不同状态时触发
    components	 	组件容器
    template	 	定义模板,可以是字符串,也可以是”#“选择器
    props	 	用于接收来自父组件的数据
    router	 	路由
    store	 	vuex 状态
    

    实例属性/方法

    vm.$el	Vue 实例使用的根 DOM 元素
    vm.$data	Vue的data配置项
    vm.$options	用于当前 Vue 实例的初始化选项
    
    vm.$props	当前组件接收到的 props 对象
    vm.$parent	父实例(如果当前实例有的话)
    
    vm.$root	当前组件树的根 Vue 实例
    vm.$children	当前实例的直接子组件
    
    vm.$refs	原生DOM元素或子组件注册引用信息
    vm.$slots	用来访问被插槽分发的内容
    
    vm.$router	全局路由(vue-router插件)
    vm.$store	vuex 状态对象(vuex插件)
    

    方法

    vm.$emit()	子组件可以使用 $emit 触发父组件的自定义事件
    vm.$set()	Vue.set的别名
    
    设置对象的属性, 这个方法主要用于避开 Vue 不能检测属性被添加的限制
    
    vm.$watch	侦听数据变化
    
    vm.$on()	监听当前实例上的自定义事件。事件可以由vm.$emit触发。回调函数会接收所有传入事件触发函数的额外参数。
    
    vm.$mount	可以使用 vm.$mount() 手动挂载(Vue 实例化时没有 el 选项)
    
    vm.$destroy	完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
    
    触发 beforeDestroy 和 destroyed 的钩子。
    
    属性绑定指令
    
    v-bind	动态改变dom标签上的属性<br />v-bind :class="" 简写 :class=""
    

    生命周期

    4个阶段:创建→挂载→更新→销毁

    beforeCreate	实例创建前	
    
    created	实例创建后	初始化数据(Ajax数据请求,获取Vuex状态、路由切换等)
    
    beforeMount	载入前	
    
    mounted	载入后	需要操作DOM时(应用第三方插件等)
    
    beforeUpdate	更新前	
    
    updated	更新后	通过事件修改数据、v-model引发数据变化、AJAX异步更新数据
    
    beforeDestroy	实例销毁前	
    
    destroyed	实例销毁后	切换路由(组件将被销毁)
    

    计算属性

    多次访问计算属性会立即返回之前的计算结果,而不必再次执行函数。

    计算属性具有缓存

    数组的更新检查

    push()、pop()、shift()、unshift()
    splice()、sort()、reverse()	
    
    变异方法,可触发视图更新
    
    filter(), concat() 和 slice()	
    
    非变异方法,不触发视图更新
    它们可以返回新数组,用新数组替换旧数组,就可以刷新视图
    

    事件对象

    event.type	触发的事件类型
    event.target	触发事件的HTML元素
    event.preventDefault( )	阻止事件的默认行为
    event.stopPropagation( )	阻止事件冒泡
    

    Vue组件

    1. 创建组件
    let myTemp={
    template:'#temp', //模板id
    data: function(){ //必须为函数(闭包)
        return { //必须有return,返回值为对象{}
            title:"dadaqianduan"
        }
    }
    
    1. 注册组件
    //在components配置项中注册组件
    let app=new Vue({
        el:"#box",
        components:{myTemp}
    });
    
    //全局注册组件,还可以使用Vue.component方法(仅限全局注册,不建议使用)
    Vue.component('myTemp',MyTemp);
    
    1. 使用组件
    <!--在Vue实例中使用组件-->
    <div id='box'>
      	<!--组件名如果用驼峰定义,改为短横线命名-->
        <my-temp></my-temp>
    </div>
    
    1. 事件通信
    • 父子组件之间的数据操作,是通过props属性和$emit()方法来实现的

    路由使用

    定义路由包括路由路径(path)、路由名称(name)、路由组件对象(component

    routes: [
        {
          path: '/',        // 路由路径 
          name: 'home',     // 路由名称
          component: Home   // 路由组件对象 
        },
        {
          path: '/users',
          name: 'Users',
          component: UserComponent
        },
        {
          path: '/about',
          name: 'about',  
          component: () => import( './views/About.vue')
        }
      ]
    
    1. 动态路由
    routes: [
      {
        path: '/users/:username/post/:postid',
        name: 'Users',
        component: UserComponent,
      }
    ]
    
    /user/:username	
    /user/tom	
    {username:'tom'}
    
    /user/:username/post/:postId	
    /user/tom/post/3	
    {username:'tom',postId:'3'}
    

    路由导航

    1. 路由导航守卫

    什么是路由导航守卫可以简单理解为路由组件的生命周期回调函数。

    // 路由导航守卫
    // 作用:在第一次进入当前路由组件之前被调用
    // 使用场景:获取ajax数据
    beforeRouteEnter(to, from, next) {
        // to:表示要进入的路由组件
        // from:表示将要离开的路由组件
        // next:表示后续操作函数
        // 此时还未进入到组件中,故不能使用this获取当前组件的实例
        next(function(app) {
            // 进入到当前组件后,才执行的回调
            // 此时回调参数 app 表示当前组件的实例对象
            axios.get('/users/' + app.id).then(res => {
                app.user = res.data.data;
            });
        });
    }
    beforeRouteUpdate(to, from, next) {
        // 此时,可以使用this表示当前组件对象
        const app = this;
        // 发送ajax请求
        // this表示切换前的状态
        // to表示要切换到的路由对象 route
        axios.get('/users/' + to.params.id).then(res => {
            app.user = res.data.data;
        });
        // 执行后续
        next();
    }
    
    1. 编程式路由导航
    methods: {
        login(){
            if(登陆成功){
            	//实现页面跳转
            	this.$router.push('/');
            }
        }
    }
    
    1. push()

    跳转到指定的路由地址, 并把当前地址写入到history中,参数可以是字符串路径或描述地址信息的对象

    字符串	router.push('home')
    对象	router.push({path:'home'})
    命名的路由	router.push({name:user,params:{userId:1}})
    
    1. replace( ):跳转到指定路由,它不会向 history 添加新记录,而是替换掉当前的 history 记录。

    2. 全局路由导航守卫

    示例:

    // 全局路由导航守卫
    	router.beforeEach((to, from, next) => {  
    });
    

    嵌套路由

    children: [
        {
            path: "",
            component: 路由名
        },
        {
            path: "路径名",
            component: 路由名
        }
    ]
    

    命名视图

    使用<router-view> 可以使用 name 属性为其设置名称,即命名路由的视图简称命名视图。

    示例:

    <router-view/> 
    <router-view name="content"></router-view> 
    import About from './views/About.vue';
    routes: [  
    {
      path: "/about",
      name: "about",
      components: {
        default: About,
        content: UserComponent
      }
    }
    ]
    

    回看笔者往期高赞文章,也许能收获更多喔!

    ❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更好的文章

    点赞、收藏和评论

    我是Jeskson(达达前端),感谢各位人才的:点赞、收藏和评论,我们下期见!(如本文内容有地方讲解有误,欢迎指出☞谢谢,一起学习了)

    我们下期见!

    文章持续更新,可以微信搜一搜「 程序员哆啦A梦 」第一时间阅读,回复【资料】有我准备的一线大厂资料,本文 http://www.dadaqianduan.cn/#/ 已经收录

    github收录,欢迎Starhttps://github.com/webVueBlog/WebFamily

    展开全文
  • ISTQB初级认证-知识及脑图总结

    千次阅读 多人点赞 2019-08-18 23:05:40
    前言 此文章为本人利用课余时间进行的ISTQB初级认证知识和考点的...上图中红色字体部分为重要考点和易错。 ISTQB(初级)知识和考点总结 软件测试基础(1) 为什么需要测试(1.1) 缺陷带来的危害(1.1.1) 资金受...

    前言

    此文章为本人利用课余时间进行的ISTQB初级认证知识和考点的总结。总结过程主要参考了“ISTQB测试人员认证初级大纲(2011版)”,由于作者能力与精力有限,此篇文章可能会存在纰漏,望见谅并及时指出。谢谢!

    xmind源文件下载

    ISTQB思维脑图

    ISTQB思维脑图
    上图中红色字体部分为重要考点和易错点。

    ISTQB(初级)知识和考点总结

    软件测试基础(1)

    为什么需要测试(1.1)

    • 缺陷带来的危害(1.1.1)

      • 资金受损

      • 时间受损

      • 信誉受损

      • 人员伤亡

    • 测试基本术语(1.1.2)

      • 【人为】错误(Error, Mistake)

      • 【内在】缺陷(Defect,Bug,Fault)

      • 【外部】失效(Failure)

        • 当存在缺陷的代码被执行时引发缺陷(即发现缺陷必须要运行软件)
        • 失效也可能是由于环境条件引起的
    • 测试和调试的区别
      调试和测试是两个不同的概念。动态测试可以发现由于软件缺陷引起的失效。而调试是一种发现,分析,清除引起失效原因的开发活动,随后由测试员进行的再测试是为了确认修改的代码已经解决了失效问题。每个活动的职责是截然不同的,即测试员进行测试,开发人员进行调试。

      • 调试(Debug)

        • 调试是一种开发活动;
        • 调试为了发现、分析并消除缺陷;
        • 调试一边修改代码一边进行验证;
        • 调试由开发人员执行;

        注意:调试的对象只能是代码,不能是文档;

      • 测试(Test)

        • 动态测试可以发现缺陷引起的失效;
        • 再测试(Verify)是为了验证代码修改已经解决了失效;
        • 测试由测试人员执行;

        注意:测试的对象不仅限于代码,还可以包括文档;

    • 外部和内部质量特性(1.1.4)
      软件满足规定或潜在客户需求特性的程度(ISO9126)

      • 功能性

        • 适合性

        • 准确性

        • 互操作性

        • 保密安全性

        • 功能依从性

      • 可靠性

        • 成熟性

        • 容错性

        • 可靠依从性

      • 易用性

        • 易理解性

        • 易学性

        • 易操作性

        • 吸引性

        • 易用依从性

      • 效率

        • 时间特性

        • 资源利用性

        • 效率依从性

      • 维护性

        • 易分析性

        • 易改变性

        • 稳定性

        • 易测试性

        • 维护依从性

      • 可移植性

        • 适应性

        • 易安装性

        • 共存性

        • 易替换性

        • 可移植依从性

    • 测试和质量的关系(1.1.4)
      软件测试是对软件质量的度量和评估,提供软件质量的信息,不能仅靠软件测试保证质量。

      • 度量及评估软件质量特性
        可以根据测试中所发现的缺陷,对软件功能和非功能性需求以及特性(例如:可靠性、可用性、 效率、可维护性和可移植性)进行度量,从而评估软件质量。

      • 树立对于软件质量的信心
        当测试发现很少或者没有发现缺陷的时候,测试就会帮助树立对于软件质量的信心。

      • 改进软件开发过程
        通过分析在其他项目中发现的缺陷和引起缺陷的根本原因,可以改进软件开发过程。

      • 质量保证工作的一部分
        测试应该作为开发过程中质量保证工作的不可或缺的一部分(与开发标准、培训和缺陷分析一样)。

    • 测试的充分性(1.1.5)

      • 风险

        • 技术风险

        • 商业产品风险

        • 项目风险

      • 时间限制

      • 预算限制

    测试总体目标(1.2)

    • 【早期阶段】预防缺陷

      • 静态测试
    • 【开发阶段】发现缺陷

      • 组件测试

      • 集成测试

      • 系统测试

    • 【验收阶段】增加对质量的信心

      • 验收测试
    • 【运行阶段】为决策提供信息

      • 维护测试

    测试的基本原则(1.3)

    • 原则 1 - 测试显示存在缺陷
      测试可以显示存在缺陷,但不能证明系统不存在缺陷。测试可以减少软件中存在未被发现缺陷的可能性,但即使测试没有发现任何缺陷,也不能证明软件或系统是完全正确的。

    • 原则 2 - 穷尽测试是不可行的
      除了小型项目,进行完全(各种输入和前提条件的组合)的测试是不可行的。通过运用风险分 析和不同系统功能的测试优先级,来确定测试的关注点,从而替代穷尽测试。

      助记:
      不要追求完美,考虑测试的充分性。

    • 原则 3 - 测试尽早介入
      为了尽早发现缺陷,在软件或系统开发生命周期中,测试活动应该尽可能早的介入,并且应该将关注点放在已经定义的测试目标上。

      助记:
      越早发现缺陷影响越小

    • 原则 4 - 缺陷集群性
      测试工作的分配比例应该与预期的和后期观察到的缺陷分布模块相适应。少数模块通常包含大部分在测试版本中发现的缺陷或失效。

      助记:
      缺陷多的模块要着重测试

    • 原则 5 - 杀虫剂悖论
      采用同样的测试用例多次重复进行测试,最后将不再能够发现新的缺陷。为了克服这种“杀虫 剂悖论”,测试用例需要进行定期评审和修改,同时需要不断增加新的不同的测试用例来测试软件或 系统的不同部分,从而发现潜在的更多的缺陷。

      助记:
      要尽量测新版本;随时更新测试用例、环境、工具和计划。

    • 原则 6 - 测试活动依赖于测试背景
      针对不同的测试背景,进行不同的的测试活动。比如,对安全关键的软件进行测试,与对一般的电子商务软件的测试是不一样的。

      助记:
      测试人员要有行业背景和行业知识!

    • 原则 7 - 不存在缺陷(就是有用系统)的谬论
      假如系统无法使用,或者系统不能完成客户的需求和期望,发现和修改缺陷是没有任何意义的。

    基本的测试过程(1.4)

      1. 测试计划和控制
        测试计划的主要活动是:识别测试任务、定义测试目标以及为了实现测试目标和任务确定必要的测试活动。

      测试控制是持续进行的活动:通过对测试实际进度和测试计划之间的比较,报告测试的状态,包括与计划之间存在的偏差。测试控制包括在必要的时候采取必要的措施来满足测试的任务和目标。 需要在项目的整个生命周期中对测试活动进行监督,以达到控制测试过程的目的。同时,测试计划 的制定也需要考虑测试监控活动的反馈信息。

      测试计划和控制阶段的任务将在第五章讲述。

      1. 测试分析和设计
        测试分析和设计是将概括的测试目标转化为具体的测试条件和测试用例的一系列活动。

      主要任务:
       评审测试依据(比如需求、软件完整性级别1(风险等级)、风险分析报告、系统架构、设 计和接口说明);
       评估测试依据和测试对象的可测性;
       通过对测试项、规格说明、测试对象行为和结构的分析,识别测试条件并确定其优先级;
       设计测试用例并确定优先级;
       确定测试条件和测试用例所需要的测试数据;
       规划测试环境的搭建和确定测试需要的基础设施和工具;
       创建测试依据和测试用例间的双向可追溯性。

      1. 测试实现和执行
        通过特定的顺序组织测试用例来完成测试规程和脚本的设计,并且包括测试执行所需的其他任何信息,以及测试环境的搭建和运行测试。

      主要任务:
       测试用例的开发、实现并确定它们的优先级。(包括识别测试数据);
       开发测试规程并确定优先级,创建测试数据,同时也可以准备测试用具和设计自动化测试脚本;
       根据测试规程创建测试套件,以提高测试执行的效率;
       确认已经正确搭建了测试环境;
       确认并更新测试依据和测试用例间的双向可追溯性;
       根据计划的执行顺序,通过手工或使用测试执行工具来执行测试规程;
       记录测试执行的结果,以及被测软件、测试工具和测试件的标识和版本;
       将实际结果和预期结果进行比较;
       对实际结果和预期结果之间的差异,作为事件上报,并且进行分析以确定引起差异的原因(例如:代码缺陷、具体测试数据缺陷、测试文档缺陷、或测试执行的方法有误等);
       缺陷修正后,重新进行测试活动。比如通过再次执行上次执行失败的用例来确认缺陷是否 已经被修正(确认测试)。执行修正后的测试用例或执行一些测试用例来确保缺陷的修正没有对软件未修改的部分造成不良影响或对于缺陷的修正没有引发其他的缺陷(回归测试)。

      1. 评估出口准则和报告
        评估出口准则是将测试的执行结果和已经定义的测试目标进行比较的活动。这个活动在各个测试级别上都需要进行。(参见章节 2.2)

      主要任务:
       按照测试计划中定义的测试出口准则检查测试日志;
       评估是否需要进行更多的测试,或是否需要更改测试的出口准则;
       为利益相关者提供一个测试总结报告。

      1. 测试结束活动
        测试结束活动就是从已完成的测试活动中收集和整合有用的数据,这些数据可以是测试经验、测试件、影响测试的因素和其他数据。
        在以下几种情况下需要执行测试结束活动,例如:当软件系统正式发布、当一个测试项目完成(或取消)、当达到一个里程碑或当一个维护版本完成时。

      主要任务:
       检查提交了哪些计划的可交付产品;
       事件报告是否关闭、或对未关闭的事件报告提交变更需求;
       记录系统的验收;
       记录和归档测试件、测试环境和测试基础设备,以便以后的重复使用; 移交测试件到维护部门;
       分析和记录所获得的经验教训,用于以后的项目和测试成熟度改进;
       使用为测试成熟度的提高所收集的信息。

    测试的独立性级别(1.5)

    • 由开发者本人执行
      测试由软件本身编写的人员来执行(最低级别的独立)

    • 由其他开发人员执行
      测试由一个其他开发人员(如来自同一开发小组)来执行

    • 由独立的测试小组或专家执行
      测试由组织内的一个或多个其他小组成员(如独立的测试小组)或测试专家(如可用性或性能测试专家)来执行

    • 由外包或其他外部组织执行
      测试由来自其他组织或其他公司的成员来执行(如测试外包或其他外部组织的鉴定,最高级别的独立)

    软件测试与开发如何相处(1.5)

    沟通方面的问题经常会发生,特别是当测试员只是被视为不受欢迎的缺陷消息的传递者的时候。 然而可以使用下面的一些方法来改善测试员和其他小组成员之间的沟通和相互关系:
     以合作而不是争斗的方式开始项目,时时提醒项目的每位成员:共同目标是追求高质量的 产品;
     对产品中发现的问题以中性的和以事实为依据的方式来沟通,而不要指责引入这个问题的 小组成员或个人。比如,客观而实际地编写缺陷报告和评审发现的问题;
     尽量理解其他成员的感受,以及他们为什么会有这种反应;
     确信其他成员已经理解你的描述,反之亦然。

    职业道德(1.6)

    公共 - 认证测试工程师应当以公众利益为目标。
    客户和雇主 - 在保持与公众利益一致的原则下,认证测试工程师应注意满足客户和雇主的最高利益。
    产品 - 认证测试工程师应当确保他们提供的(在产品和系统中由他们测试的)发布版本符合最高的专业标准。
    判断 - 认证测试工程师应当维护他们职业判断的完整性和独立性。
    认证软件测试管理人员和测试领导人员应赞成和促进对软件测试合乎道德规范的管理。
    专业 - 在与公众利益一致的原则下,认证测试工程师应当推进其专业的完整性和声誉。
    同事 - 认证测试工程师对其同事应持平等和互助和支持的态度,并促进与软件开发人员的合作。
    自我 - 认证测试工程师应当参与终生职业实践的学习,并促进合乎道德的职业实践方法。

    软件生命周期中的测试(2)

    软件开发模型(2.1)

    软件开发模型是指软件开发所依据的方式和过程。

    如何选择开发模型?

    • 根据项目的内容

    • 根据产品的特征

    • V模型(顺序开发模型)(2.1.1)
      “V模型”也称“顺序开发模型”,它适合需求明确且不常改变的场景。

      用户需求 <———— 验收测试
      系统设计 <——— 系统测试
      概要设计 <—— 集成测试
      详细设计 <— 组件测试
      编码/实现

      备注:
      箭头表示验证关系

      • 组件测试

      • 集成测试

      • 系统测试

      • 验收测试

    • 迭代-增量开发模型(2.1.2)
      迭代-增量开发模型由需求建立、设计、构建和测试等一系列相对较短的开发周期构成。

      在每次迭代过程中, 对迭代产生的系统可能需要在不同的测试级别上进行测试。通过将增量模块加入到以前开发的模块中,形成一个逐渐增大的系统,这个系统同样需要进行测试。在完成第一次迭代后,对所有的迭代 进行回归测试会变得越来越重要。验证和确认可以在每个增量模块中进行。

      • 原型开发

      • 快速应用开发(RAD)

      • 统一软件开发过程(RUP)

      • 敏捷开发(Agile)
        “敏捷开发模型”适合需求快速变化的场景。

    测试级别(2.2)

    “V模型”也称“顺序开发模型”,它适合需求明确且不常改变的场景。

    用户需求 <———— 验收测试
    系统设计 <——— 系统测试
    概要设计 <—— 集成测试
    详细设计 <— 组件测试
    编码/实现

    备注:
    箭头表示验证关系

    • 组件测试(2.2.1)

      • 也称“单元测试”或“模块测试”;

      • 在软件编码完成后,对每个程序模块进行测试;

      • 通常由开发人员进行,测试人员辅助;

        • 概述
          在独立可测试的软件中(模块、程序、对象和类等),可以通过组件测试发现缺陷,以及验证软 件功能。根据开发生命周期和系统的背景,组件测试可以和系统的其他部分分开,单独进行测试。 在组件测试过程中,会使用到桩、驱动器和模拟器。

          组件测试可能包括功能测试和特定的非功能特征测试,比如资源行为测试(如内存泄漏)或健 壮性测试和结构测试(比如分支覆盖)。根据工作产品,例如组件规格说明、软件设计或数据模型等设计测试用例。

          通常,通过开发环境的支持,比如组件测试框架或调试工具,组件测试会深入到代码中,而且实际上设计代码的开发人员通常也会参与其中。在这种情况下,一旦发现缺陷,就可以立即进行修改,而不需要正式的缺陷管理过程。

        • 测试依据

          • 组件需求说明

          • 详细设计文档

          • 代码

        • 测试对象

          • 组件

          • 程序

          • 数据转换/移植程序

          • 数据库模型

    • 集成测试(2.2.2)

      • 也称“组装测试”或“联合测试”;

      • 关注模块之间的配合,用以发现接口上的缺陷;

      • 集成应当是渐进的,可以“自顶向下”也可以“自底向上”集成;

        • 概述
          集成测试是对组件之间的接口进行测试,以及测试一个系统内不同部分的相互作用,比如操作系统、文件系统、硬件或系统之间的接口。

          集成的规模越大,就越难在某一特定的组件或系统中定位缺陷,从而增加了风险并会花费额外的更多时间去发现和修理这些故障。
          系统化集成的策略可以根据系统结构(例如自顶向下或自底向上)、功能任务集、事务处理顺序 或系统和组件的其他方面等来制定。为了能方便快速地隔离故障和定位缺陷,集成程度应该逐步增加,而不是采用“大爆炸”式的集成。

          测试特定的非功能特征(比如性能)也可以包含在系统集成测试中。
          在集成的每个阶段,测试员只是把精力集中在集成本身。举例来说,假如集成模块 A 和模块 B, 测试人员是应该关注两个模块之间的交互,而不是每个模块的功能。功能测试和结构测试方法都可以应用在集成测试。

        • 测试依据

          • 软件和系统设计文档

          • 系统架构

          • 工具流

          • 用例

        • 测试对象

          • 子系统

          • 数据库实现

          • 基础结构

          • 接口

          • 系统配置和配置数据

    • 系统测试(2.2.3)

      • 将整个程序安装到运行环境中,对硬件、网络、操作系统及支撑平台构成的整体系统进行测试。

      • 通常由独立的测试团队进行。

        • 概述
          系统测试关注的是在开发项目或程序中定义的一个完整的系统/产品的行为。

          在系统测试中,测试环境应该尽量和最终的目标或生产环境相一致,从而减少不能发现和环境相关的失效的风险。

          系统测试可能包含基于不同方面的测试:基于风险评估的、基于需求规格说明的、基于业务过程的、基于用例的、或基于其他对系统行为的更高级别描述或模型的、基于与操作系统的相互作用的、基于系统资源等的测试。

          针对功能需求的系统测试开始时可以选择 最适合的基于规格说明的测试即黑盒技术来对系统进行测试。比如:可以根据业务准则描述的因果 组合来生成决策表。基于结构的技术即白盒测试技术,可以评估测试的覆盖率,可以基于评估覆盖 一个结构元素,如菜单结构或者页面的导航等的完整性。

          系统测试通常由独立的测试团队进行。

        • 测试依据

          • 系统和软件需求规格说明

          • 用例

          • 功能规格说明

          • 风险分析报告

        • 测试对象

          • 系统、用户手册和操作手册

          • 系统配置和配置数据

    • 验收测试(2.2.4)

      • 也称“交付测试”或“确认测试”;

      • 在交付软件前,需要检测与证实软件是否满足了需求说明书中规定的要求。

        • 概述
          通常是由使用系统的用户或客户来进行,同时系统的其他利益相关者也可能参与其中。

          验收测试的目的是建立对系统、系统的某部分或特定的系统非功能特征建立信心。发现缺陷不是验收测试的主要目标。验收测试可以用来评估系统对于部署和使用的准备情况,但是验收测试不 一定是最后级别的测试。

          验收测试可以在多个测试级别上进行。

        • 测试依据

          • 用户需求

          • 系统需求

          • 用例

          • 业务流程

          • 风险分析报告

        • 测试对象

          • 基于完全集成系统的业务流程

          • 用户处理过程

          • 结构

          • 报告

          • 配置数据

        • 典型类型

          • 用户验收测试
            最终操作人员参与

          • 操作(验收)测试
            由系统管理员来进行,测试内容主要包括:
             系统备份/恢复测试;
             灾难恢复测试;
             用户管理测试;
             维护任务测试;
             数据加载和移植活动;
             安全漏洞阶段性检查。

          • 合同和法规性验收测试
            合同性:根据合同中规定的验收准则进行测试;
            法规性:根据法律法规进行测试,如政府、法律和安全方面的法律法规。

          • Alpha和Beta(或现场)测试
            Alpha 测试通常在开发组织现场进行,但测试并非由开发团队执行。Beta 测试或实地测试,是在客户或潜在客户现场进行并由他们执行。

    测试类型(2.3)

    • 功能性测试(2.3.1)
      可以采用基于规格说明的技术,根据软件或系统的功能来设计测试条件和测试用例(参见第 4 章)。功能测试主要是考虑软件的外部表现行为(黑盒测试)。

      安全性测试是功能测试的一种,它会对安全性相关的功能(比如防火墙)进行测试,从而检测系统和数据是否能抵御外部恶意的威胁,如病毒等。互操作性测试是另一种功能性测试,评估软件产品与其他一个或多个组件或系统交互的能力。

      注意:
      安全性和互操作性测试是功能性测试。

    • 非功能性测试(2.3.2)
      非功能测试包括但不限于:性能测试、负载测试、压力测试、可用性测试、可维护性测试、可靠性测试和可移植性测试。非功能性测试就是测试系统运行的表现如何。

    • 结构测试(2.3.3)
      可以在任何测试级别上进行结构测试(白盒测试)。

      结构测试技术最好在进行基于规格说明的测试之后使用,以便通过评估结构类型的覆盖来测量测试的完整性。

      覆盖是指结构通过测试套件检验的程度,以项被覆盖的百分比来表示。假如覆盖率不是 100%, 可能需要设计更多的测试用例,来测试被遗漏的项,从而提高测试的覆盖。有关覆盖技术参见第 4 章。

      在所有的测试级别,特别是在组件测试和组件集成测试中,可以利用工具来测量代码内某些元 素的覆盖率,比如语句覆盖和判定覆盖。结构测试也可以基于系统的结构,比如调用层次结构。

      结构测试方法也同样可以运用到系统、系统集成或验收测试级别(比如业务模型或菜单结构)。

    • 再测试和回归测试(2.3.4)
      当发现和修改了一个缺陷后,应进行再测试以确定已经成功的修改了原来的缺陷,这称之为确认。调试(定位并修复缺陷)是一种开发活动,不是一种测试活动。

      回归测试是对已被测过的程序在修改缺陷后进行的重复测试,以发现在这些变更后是否有新的 缺陷引入或被屏蔽。

      当软件发生变更或者应用软件的环境发生变化时,需要进行回归测试。回归测试的规模可以根据在以前正常运行的软件中发现新的缺陷的风险大小来决定。

      回归测试可以在所有的测试级别上进行,同时适用于功能测试、非功能测试和结构测试。回归测试套件一般都会执行多次,而且通常很少有变动,因此将回归测试自动化是很好的选择。

    维护测试(2.4)

    维护测试是在一个现有的运行系统上进行,且一旦对软件或系统进行修改、移植或退役处理时,就需要进行维护测试。

    当数据从另一个应用程序移植到正在维护的系统时,需要移植测试(转换测试)。

    为系统退役而进行的维护测试应该包括数据移植测试,或当数据要长时间的保存时还须存档测试。

    维护测试根据变更情况的不同,可以在某一测试级别或所有测试级别和测试类型上进行。确定变更如何影响现有系统的过程,称之为影响分析,它有助于决定实施回归测试的广度和深度。

    如果规格说明遗失、过时或测试人员没有具备领域知识,进行维护测试将是一件困难的事情。

    软件静态测试技术(3)

    静态测试分类

    • 评审

      • 非正式评审

      • 正式评审

        • 走查(Walk through)

        • 技术评审(Technical Review)

        • 正规审查(Inspection)

    • 基于工具的静态分析

      • 词法和语法分析

      • 静态错误分析

    静态技术和测试过程(3.1)

    • 概述
      静态测试技术通过手工检查(评审)或自动化分析(静态分析)的方式对代码或者其他的项目文档进行检查。

      评审是对软件工作产品(包括代码)进行测试的一种方式,可以在动态测试执行之前进行。
      在生命周期早期的评审过程中发现并修改缺陷(例如发现需求中的缺陷)的成本会比在动态测试中才发现并修改这些缺陷的成本低的多。

    • 目标
      评审、静态分析和动态测试具有共同的目标是识别缺陷。
      它们之间是互补的,不同的技术可以有效和高效地发现不同类型的缺陷。

    • 特点

      • 不需要运行代码;
      • 直接发现缺陷本身;
      • 检查对象不仅是代码还可以是文档;
      • 可以在早期发现缺陷,大幅降低成本;
      • 相对动态测试而言,静态测试成本低效率高;

    评审过程(3.2)

    • 正式评审的阶段(3.2.1)

        1. 计划阶段
           定义评审标准;
           选择人员;
           分配角色;
           为更加正式的评审类型(比如审查)制定入口和出口准则;
           选择需要进行评审的文档的内容;
           核对入口准则(针对更正式的评审类型)。
        1. 预备会阶段
           分发文档;
           向评审参与者解释评审的目标、过程和文档。
        1. 个人准备阶段
           先行评审文档,为评审会议做准备;
           标注可能的缺陷、问题和建议;
        1. 评审会议阶段
           讨论和记录,并留下文档化的结果或会议纪要(针对更正式的评审类型);
           标注缺陷、 提出处理缺陷的建议、对缺陷作出决策;
           在任何形式的会议期间或跟踪任何类型的电子通信期间检查/评价和记录问题。
        1. 返工阶段
           修改发现的缺陷(通常由作者来进行);
           记录缺陷更新的状态(在正式评审中)。
        1. 跟踪结果阶段
           检查缺陷是否已得到解决;
           收集度量数据;
           核对出口准则(针对更正式的评审类型)。
    • 角色和职责(3.2.2)

      • 经理
        经理:决定是否需要进行评审,在项目计划中分派时间,判断是否已达到评审的目标。

        注意:
        经理未必出席评审会议。

      • 主持人
        主持人:主持文档或文档集的评审活动,包括策划评审、召开会议和会议后的跟踪。假如需要,主持人可能还需要进行不同观点之间的协调。主持人通常是评审成功与否的关键。

        注意:
        主持人是评审成败的关键。

      • 作者
        作者:待评审文档的作者或主要责任人。

      • 评审员
        评审员:具有专门技术或业务背景的人员(也称为检查员(checker)或审查员(inspector)),他们在必要的准备后,标识和描述被评审产品存在的问题(如缺陷)。所选择的评审员应该
        在评审过程中代表不同的观点和角色,并且应该参与各种评审会议。

      • 记录员
        记录员:记录所有的事件、问题,以及在会议过程中识别的未解决的问题。

        注意:
        记录员可以是其他角色兼职负责,例如主持人,但不能是作者本人。

    • 评审类型(3.2.3)
      非正式评审:

      • 评审对象是正在进行中的工作产品;
      • 不需要明确定义的过程,可以没有书面指导性材料。

      正式评审:

      • 评审对象是已经确认完成的工作产品;

      • 需要遵循明确定义的过程,参与人员有明确的职责划分,有明确定义的入口和出口准则,以及规范化的书面材料。

        • 非正式评审
          主要目的:以较低的成本获得收益。

        • 走查(Walk through)
          主要目的:学习、增加理解、发现缺陷。

          在实际情况中可以是非常正式的,也可能是非常不正式的;

        • 技术评审(Technical Review)
          主要目的:讨论、作决策、评估候选方案、发现缺陷、解决技术问题、检查与规格及标准的符合程度。

          在实际情况中可以是在不正式的和非常正式的之间;

        • 正规审查(Inspection)
          主要目的:发现缺陷。

    • 技术评审的注意事项

      1. 评审应针对材料而非作者;
      2. 评审会议不宜超过两个小时:当被审核材料多时,应该拆分成若干部分进行评审;
      3. 限制争辩的时间:当无法取得一致认同时,先记录问题,可另行安排时间进行讨论;
      4. 阐明问题而不试图解决问题:评审会上不要解决问题,应记录问题,会后再解决问题;

    静态分析的工具支持(3.3)

    • 概述
      静态分析的目的是发现软件源代码和软件模型中的缺陷。静态分析的执行并不需要使用工具去 实际运行被测软件。而动态测试是真正运行软件的代码。静态分析可以定位那些在测试过程很难发 现的缺陷。与评审一样,静态分析通常发现的是缺陷而不是失效。静态分析工具能够分析程序代码 (比如控制流和数据流),以及产生如 HTML 和 XML 的输出。

    • 静态分析的好处
      静态分析的好处:
       在测试执行之前尽早发现缺陷;
       通过度量的计算(比如高复杂性测量),早期警示代码和设计可能存在问题的方面;
       可以发现在动态测试过程不容易发现的一些缺陷;
       可以发现软件模块之间的相互依赖性和不一致性,例如链接;
       改进代码和设计的可维护性;
       在开发过程中学习经验教训,从而预防缺陷。

    • 静态分析策略
      开发人员通常在组件测试和集成测试之前或期间,或当代码签入到配置管理工具时使用静态分析工具(按照预先定义的规则或编程规范进行检查)。

      设计人员在软件建模期间也使用静态分析工具。

      静态分析工具会产生大量的警告信息,需要很好的管理这些信息,从而可以有效地使用静态分析工具。

      编译器也可以为静态分析提供一些帮助,包括度量的计算。

    • 工具能够发现的典型缺陷
      通过静态分析工具能够发现的典型缺陷如下:
       引用一个没有定义值的变量;
       模块和组件之间接口不一致;
       从未使用的变量;
       不可达代码或死代码;
       逻辑上的遗漏与错误(潜在的无限循环);
       过于复杂的结构;
       违背编程规则;
       安全漏洞;
       代码和软件模型的语法错误。

    测试设计技术(4)

    测试开发过程(4.1)

    • 测试分析阶段
      在测试分析阶段,要对测试基础文档进行分析,从而决定测试什么,也就是明确测试的条件。 将测试条件定义为能通过一个或多个测试用例进行验证的一个条目或事件(比如功能、事务处理、 质量特征或结构元素等)。
      建立从测试条件到需求的可追溯性,有助于需求变更时的影响分析和测试用例集的需求覆盖率 分析。在测试分析阶段,除了考虑一些其它的因素,基于已经识别的风险,实施具体的测试方法从 而选择要采用的测试技术(有关风险分析的更多的内容请参见第 5 章)。

    • 测试设计阶段
      在测试设计阶段,要定义和记录测试用例和测试数据。测试用例由:一组输入值、执行的前提 条件、预期结果和执行的后置条件等元素组成,以覆盖一定的产生目标或测试条件。测试设计规格 说明(包含测试条件)和测试用例规格说明的内容在“软件测试文档标准(IEEE Std 829-1998)” 中有具体的描述。
      预期的测试结果应该作为测试用例规格说明的一部分,同时包含输出、数据和状态的变化,以 及其他的测试结果。假如没有明确预期结果,则一个看似合理却错误的结果可能被视为正确的结果。 理想情况下预期结果应该在测试执行之前明确定义。

    • 测试实现阶段
      在测试实现阶段,测试用例的开发、实现、确定优先级和组织都应该包含在测试规程规格说明 中(IEEE STD 829-1998)。测试规程(或者手工测试脚本)描述了测试用例执行的顺序。如果使用 测试执行工具进行测试,这种测试的动作顺序将在测试脚本中描述(自动化的测试规程)。
      不同的测试规程和自动化测试脚本要体现在测试执行进度表中,该计划定义了不同测试规程和可能的自动化测试脚本的执行顺序、执行的时间和执行者。测试执行进度表同时考虑了其他的因素,比如回归测试、测试优先级以及技术和逻辑的依赖等。

    测试设计技术的种类(4.2)

    • 基于规格说明/黑盒(4.3)
      基于规格说明的测试技术具有以下共同特点:
       使用正式或非正式的模型来描述需要解决的问题、软件或其组件等;  根据这些模型,可以系统地导出测试用例。

      • 等价类
        有效等价类
        无效等价类

      • 边界值
        临界值
        刚好超出临界值
        刚好小于临界值

      • 决策表
        因果图与判定表:
        复杂条件构成因果图
        根据因果图生成判定表

      • 状态转换
        根据软件的状态、状态间的转换、触发状态变化(转换)的输入或事件以及从状态转换导致的可能的行动来进行测试。

      • 用例
        描述了参与者(用户或系统)之间的相互作用,并从这些交互产生一个从系统用户或客户的角度所期望和能观察到的结果。

        用例非常有助于设计用户/客户参与的验收测试; 也可以帮助发现由于不同组件之间的相互作用和相互影响而产生的集成缺陷,这是在单个的组件测 试中是无法发现的。

        备注:用例指的是用户如何使用软件,即“Use Case”。

    • 基于结构/白盒(4.4)
      基于结构的技术的共同特点:
       根据软件的结构信息设计测试用例,比如软件代码和详细设计信息;
       可以通过已有的测试用例测量软件的测试覆盖率,并通过系统化的导出设计用例来提高覆盖率。

      • 语句覆盖
        在组件测试中,语句覆盖是指评价一个测试用例套件中已经执行的可执行语句的百分比。

      • 判定覆盖
        判定覆盖,和分支测试相关,是指评价在一个测试用例套中已经执行的判定(例如 if 语句的 true 和 false 选项)输出的百分比。

        备注:“判定覆盖”比“语句覆盖”更全面,100%的“判定覆盖”可以保证 100%的“语句覆盖”,反之则不行。

    • 基于经验(4.5)
      基于经验的方法具有以下共同特点:
       测试用例根据参与人员的经验和知识来编写;
       测试人员、开发人员、用户和其他的利益相关者对软件、软件使用和环境等方面所掌握的知识作为信息来源之一;
       对可能存在的缺陷及其分布情况的了解作为另一个信息来源。

      注意:这种技术依据测试员的经验,所以产生的效果会有极大的不同。

      • 探索性测试
        探索性测试是指依据包含测试目标的测试章程来同时进行测试设计、测试执行、测试记录和学习,并且是在规定时间内进行的。
        这种方法在规格说明较少或不完备且时间压力大的情况下使用更有帮助,或者作为对其他更为正式的测试的增加或补充。
        它可以作为测试过程中的检查,以有助于确保能发现最为严重的缺陷。

      • 错误推测法
        错误推测法的一个结构化方法是列举可能的错误,并设计测试来攻击这些错误,这种系统的方法称之为缺陷攻击。

    测试管理(5)

    测试组织(5.1)

    • 测试组织的独立性
      独立测试的优点:
       独立的测试员是公正的,可以发现一些其他不同的缺陷。;
       一个独立的测试员可以验证在系统规格说明和实现阶段所做的一些假设。
      独立测试的缺点:
       与开发小组脱离(如果完全独立);
       开发人员可能丧失对软件质量的责任感;
       独立的测试员可能被视为瓶颈或者成为延时发布而被责备的对象。

    • 测试组织模式

      • 基于技能的组织模式

      • 基于项目的组织模式

    • 测试角色及职责

      • 测试组长
        测试组长可能的主要任务包括:
         与项目经理以及其他人共同协调测试策略和测试计划;
         制定或评审项目的测试策略和组织的测试方针;
         将测试的安排合并到其他项目活动中,比如集成计划;
         制定测试计划(要考虑背景,了解测试目标和风险),包括选择测试方法,估算测试的时间、工作量和成本,获取资源,定义测试级别、测试周期并规划事件管理;
         启动测试规格说明、测试准备、测试实施和测试执行,监督测试结果并检查出口准则;
         根据测试结果和测试过程(有时记录在状态报告中)调整测试计划,并采取任何必要措施对存在的问题进行补救;
         对测试件进行配置管理,保证测试件的可追溯性;
         引入合适的度量项以测量测试进度,评估测试和产品的质量;
         决定什么应该自动化,自动化的程度,以及如何实现;
         选择测试工具支持测试,并为测试员组织测试工具使用的培训;
         决定关于测试环境实施的问题;
         根据在测试过程中收集的信息编写测试总结报告。

      • 测试员
        测试员可能的主要任务包括:
         评审和参与测试计划的制定;
         分析、评审和评估用户需求、规格说明书及模型的可测试性;
         创建测试规格说明;
         建立测试环境(通常需要系统管理员,网络管理员协同完成);
         准备和获取测试数据;
         进行所有级别的测试,执行并记录测试日志,评估测试结果,记录和预期结果之间的偏差。
         根据需要使用测试管理工具和测试监控工具;
         实施自动化测试(可能需要开发人员或测试自动化专家的支持);
         在可行的情况下,测量组件和系统的性能;
         对他人的测试进行评审。

    测试计划和估算(5.2)

    • 测试计划(5.2.1)

      1. 测试计划受到很多因素的影响:组织的测试方针、测试范围、测试目标、风险、约束、关键程度、可测试性和资源的可用性等。
      2. 随着项目和测试计划的不断推进,将有更多的信息和具体细节包含在计划中。
      3. 测试计划是个持续的活动,需要在整个生命周期过程和活动中进行。
      4. 从测试中得到的反馈信息可以识别变化的风险,从而对计划作相应的调整。
    • 测试计划活动(5.2.2)
      对整个系统或部分系统可能的测试计划活动包括:
       确定测试的范围和风险,明确测试的目标;
       决定总体测试方法,包括测试级别、入口和出口准则的界定;
       把测试活动整合和协调到整个软件生命周期活动中去(采购、供应、开发和运维);
       决定测试什么?测试由什么角色来执行?如何进行测试?如何评估测试结果?
       为测试分析和设计活动安排时间进度;
       为测试实现、执行和评估安排时间进度;
       为已定义的不同测试活动分配资源;
       定义测试文档的数量、详细程度、结构和模板;
       为监控测试准备和执行、缺陷解决和风险问题选择度量项;
       确定测试规程的详细程度,以提供足够的信息支持可复用的测试准备和执行。

    • 入口准则(5.2.3)
      入口准则定义了什么时候可以开始测试,如某个测试级别的开始,或什么时候一组测试准备就绪可以执行。

      入口准则主要包含:
       测试环境已经准备就绪并可用;
       测试工具在测试环境中已经准备就绪;
       可测的代码可用;
       测试数据可用。

    • 出口准则(5.2.4)
      测试出口准则(exit criteria)的目的是:定义什么时候可以停止测试,比如某个测试级别的 结束,或者当测试达到了规定的目标。

      出口准则主要包含:
       完整性测量,比如代码、功能或风险的覆盖率;
       对缺陷密度或可靠性度量的估算;
       成本;
       遗留风险,例如没有被修改的缺陷或在某些部分测试覆盖不足;  进度表,例如基于交付到市场的时间。

    • 测试估算(5.2.5)

      • 测试工作量
        测试工作量的内容:

        1. 测试用例设计;
        2. 测试环境设置;
        3. 测试用例执行;
        4. 测试缺陷报告;

        一旦估算了测试工作量,就可以识别资源和制定时间进度表。

      • 决策因素
        测试的工作量可能取决于多种因素,包括:
         产品的特点:规格说明和用于测试模型的其它信息(即测试依据)的质量,产品的规模,问题域的复杂度,可靠性、安全性的需求和文档的需求;
         开发过程的特点:组织的稳定性、使用的工具、测试过程、参与者的技能水平和时间紧迫程度等;
         测试的输出:缺陷的数量和需要返工的工作量。

      • 估算方法
        两种估算测试工作量的方法:
         基于度量的方法:根据以前或相似项目的度量值来进行测试工作量的估算,或者根据典型的数据来进行估算;
         基于专家的方法:由任务的责任人或专家来进行测试任务工作量的估算。

    • 测试方法(5.2.6)

      • 测试策略与方法
        测试方法是测试策略的具体实现。

        仅做了解:
        测试策略(Testing Strategy)通常是描述如何测试软件的总体方法和目标。它是关于如何测试系统的正式描述,需要确定针对所有测试级别的测试策略。包括确定测试环境、阶段、类型、方法和技术。描述测试包含多少个测试级别(如组件测试、集成测试、系统测试等)以及每个阶段内进行的测试种类(如功能测试、性能测试、压力测试等)。
        【从2010版大纲开始:为保持测试方法(test approach)与术语表中的定义一致。术语“测试策略(test strategy)” 将不再作为术语要求了。】

      • 典型的测试方法
        典型的测试方法包括:
         分析的方法,比如基于风险的测试,直接针对风险最高的部分进行测试;
         基于模型的方法,比如随机测试利用失效率(如:可靠性增长模型)或使用率(如:运行概 况)的统计信息;
         系统的方法,比如基于失效的方法(包括错误推测和故障攻击),基于检查表的方法和基于质量特征的方法; 基于与过程或符合标准的方法,比如在行业标准中规定的方法或各类敏捷的方法;
         动态和启发式的方法,类似于探索性测试,测试很大程度上依赖于事件而非提前计划,而且执行和评估几乎是同时进行的;
         咨询式的方法,比如测试覆盖率主要是根据测试小组以外的业务领域和/或技术领域专家的建议和指导来推动的;
         可重用的方法,比如重用已有的测试材料,广泛的功能回归测试的自动化,标准测试套件等。

        可以结合使用不同的测试方法,比如基于风险的动态方法。

    测试过程的监控(5.3)

    • 测试监控的目的
      测试监控的目的是:

      1. 提供关于测试活动的反馈信息,使测试活动保持可视性。监控的信息可以通过手工或自动的方式进行收集,同时可以用来衡量出口准则,比如测试覆盖率。
      2. 也可以用度量数据对照原计划的时间进度和预算来评估测试的进度。
    • 常用的测试度量项目
      常用的测试度量项有:
       测试用例准备工作完成的百分比(或按计划已编写的测试用例的百分比);
       测试环境准备工作完成的百分比;
       测试用例执行情况(例如:执行/没有执行的测试用例数,通过/失败的测试用例数);
       缺陷信息(例如:缺陷密度、发现并修改的缺陷、失效率、重新测试的结果);
       需求、风险或代码的测试覆盖率;
       测试员对产品的主观信心;
       测试里程碑的日期;
       测试成本,包括寻找下一个缺陷或执行下一轮测试所需成本与收益的比较。

    • 测试报告
      测试报告是对测试工作和活动等相关信息的总结,主要包括:
       在测试周期内发生了什么?比如达到测试出口准则的日期;
       通过分析相关信息和度量可以对下一步的活动提供建议和做出决策,比如对遗留缺陷的评估、继续进行测试的经济效益、未解决的风险以及被测试软件的置信度等。

      测试总结报告的大纲可以参考“软件测试文档标准”(IEEE Std 829-1998)。

      需要在测试级别的过程中和完成时收集度量信息,来评估:
       该测试级别的测试目标实现的充分性;
       采用的测试方法的适当性;
       针对测试目标的测试的有效性。

      注意:
      “软件测试文档标准”(IEEE Std 829-1998)的内容在考题中会有体现,建议了解。

    • 测试控制
      测试控制描述了根据收集和报告的测试信息和度量而采取的指导或纠正措施。措施可能包括任何测试活动,也可能影响其它软件生命周期中的活动或任务。

      测试控制措施的例子:
       基于测试监控信息来做决策;
       如果一个已识别的风险发生(如软件交付延期),重新确定测试优先级;
       根据测试环境可用性,改变测试的时间进度表;
       设定入口准则:规定修改后的缺陷必须经过开发人员再测试(确认测试)后才能将它们集成到版本中去。

    配置管理(5.4)

    配置管理的目的是在整个项目和产品的生命周期内,建立和维护软件或系统产品(组件、数据和文档)的完整性。

    对测试而言,采用配置管理可以确保:
     测试件的所有相关项都已经被识别,版本受控,相互之间有关联以及和开发项(测试对象)之间有关联的变更可跟踪,从而保证可追溯性;
     在测试文档中,所有被标识的文档和软件项能被清晰明确的引用。

    对于测试员来说,配置管理可以帮助他们唯一地标识(并且复制)测试项、测试文档、测试用例和测试用具。

    在测试计划阶段,应该选择配置管理的规程和基础设施(工具),将其文档化并予以实施。

    风险和测试(5.5)

    • 项目风险(5.5.1)
      项目风险是围绕项目按目标交付的能力的一系列风险,比如:
       组织因素:
       技能、培训和人员的不足;
       个人问题;
       政策因素,比如:
       与测试员进行需求和测试结果沟通方面存在的问题;
       测试和评审中发现的信息未能得到进一步跟踪(如未改进开发和测试实践);
       对测试的态度或预期不合理(如:没有意识到在测试中发现缺陷的价值)。
       技术因素:
       不能定义正确的需求;
       给定现有限制的情况下,没能满足需求的程度;
       测试环境没有及时准备好;
       数据转换、迁移计划,开发和测试数据转换/迁移工具造成的延迟;
       低质量的设计、编码、配置数据、测试数据和测试。
       供应商因素:
       第三方存在的问题;
       合同方面的问题。

      在分析、管理和缓解这些风险的时候,测试经理需要遵循完善的项目管理原则。“软件测试文档 标准”(IEEE Std 829-1998)中指出,测试计划需要陈述风险和应急措施。

    • 产品风险(5.5.2)
      在软件或系统中的潜在失效部分(即将来可能发生不利事件或危险)称之为产品风险,因为它们对产品质量而言是一个风险,包括:
       故障频发的软件交付使用;
       软件/硬件对个人或公司造成潜在损害的可能性;
       劣质的软件特性(比如功能性、可靠性、易用性和性能等);
       低劣的数据完整性和质量(例如:数据迁移问题、数据转换问题、数据传输问题、违反数 据标准问题);
       软件没有实现既定的功能。

    • 基于风险的测试方法
      风险通常可以用来决定从什么地方开始测试,什么地方需要更多的测试。测试可以用来降低风险或可以减少负面事件的影响。

      在项目初期阶段,使用基于风险的方法进行测试,有利于降低产品风险的级别。

      在基于风险的测试方法中,识别出的风险可以用于:
       决定采用的测试技术;
       决定要进行测试的范围;
       确定测试的优先级,尝试尽早的发现严重缺陷;
       决定是否可以通过一些非测试的活动来降低风险(比如对缺乏经验的设计者进行相应的培训)。

      风险管理活动提供了一些系统化的方法:
       评估(并定期重新评估)可能出现的错误(风险);
       决定哪些风险是重要的需要处理的;
       处理风险的具体措施。

    事件管理(5.6)

    • 事件报告的主要目标
      事件报告的主要目标如下:
       为开发人员和其他人员提供问题反馈,在需要的时候可以进行识别、隔离和纠正;
       为测试组长提供一种有效跟踪被测系统质量和测试进度的方法;
       为测试过程改进提供资料。

    • 事件报告的具体内容
      事件报告的具体内容主要包括:
       提交事件的时间,提交的组织和作者;
       预期和实际的结果;
       识别测试项(配置项)和环境;
       发现事件时软件或系统所处的生命周期阶段;
       为了能确保重现和解决事件需要描述事件(包括日志、数据库备份或截屏);
       对利益相关者的影响范围和程度;
       对系统影响的严重程度;
       修复的紧迫性/优先级;
       事件状态(例如:打开的、延期的、重复的、待修复的、修复后待重测的或关闭的等);
       结论、建议和批准;
       全局的影响,比如事件引起的变更可能会对系统的其他部分产生影响;
       变更历史记录,比如针对事件的隔离、修改和已修改的确认,项目组成员所采取的行动顺序;
       参考,包括发现问题所用的测试用例规格说明的标识号。

      事件报告的大纲也可以参考“软件测试文档标准”(IEEE Std 829-1998)。

    软件测试工具(6)

    测试工具的类型(6.1)

    测试工具大体上可分为三类:

    1. 功能测试工具
    2. 性能测试工具
    3. 测试管理工具
    • 测试管理(6.1.3)

      • 测试管理工具

      • 需求管理工具

      • 事件管理工具

      • 配置管理工具

    • 静态测试(6.1.4)

      • 评审工具

      • 静态分析工具

      • 建模工具

    • 测试规格说明(6.1.5)

      • 测试设计工具

      • 测试数据准备工具

    • 测试执行和记录(6.1.6)

      • 测试执行工具

      • 测试用具/单元测试框架工具

      • 测试比较器

      • 覆盖率测量工具

      • 安全性测试工具

    • 性能和监控(6.1.7)

      • 动态分析工具

      • 性能/负载/压力测试工具

      • 监控工具

    • 特定应用领域(6.1.8)

      • 数据质量评估

    有效使用工具(6.2)

    • 潜在的收益与风险(6.2.1)

      • 潜在收益
        使用工具的潜在收益包括:
         减少重复性的工作(比如,执行回归测试,重新输入相同测试数据,代码规则检查);
         更好的一致性和可重复性(比如,用工具按照相同的顺序和频率执行测试,从需求生成测试);
         客观的评估(比如,静态测量、覆盖率);
         容易得到测试和测试的相关信息(比如,关于测试进展的统计和图表,事件发生率和性能)。

      • 潜在风险
        使用工具存在的风险:
         对工具抱有不切实际的期望(包括功能性和易用性);
         低估首次引入工具所需的时间、成本和工作量(包括培训和额外的专业知识);
         低估从工具中获得较大和持续性收益需要付出的时间和工作量(包括使用工具的方式需要更改测试过程,并不断改进);
         低估了对工具生成的结果进行维护所需的工作量;
         对工具过分依赖(替代测试设计或者对一些更适合手工测试的方面却使用自动测试工具);
         忽视了在工具中对测试对象的版本控制;
         忽视了多个重要工具之间的关联和互操作性,例如:需求管理工具、版本控制工具、事件管理工具、缺陷跟踪工具和其他从不同供应商获得的工具;
         工具供应商破产、停止维护工具或将工具卖给其他供应商的风险;
         供应商对工具的支持、升级和缺陷修复反应不力;
         开源/免费工具项目中止的风险;
         其他不可预知的风险,例如不能支持新平台。

    • 工具类型的特殊考虑(6.2.2)

      • 测试执行工具
        注意:

        1. 所有的测试方法都需要脚本语言方面的技术专家(或测试员或测试自动化专家)。
        2. 无论使用什么脚本技术,都需要储存每次测试的预期结果,便于后续比较。
        • 自动化测试
          正确认识自动化测试:

          1. 为了获得可观收益,经常需要为这类工具投入很多工作量。
          2. 通过记录测试员手动操作的捕捉过程往往开始看起来似乎很吸引人,但是这种方法不适合大量的自动化测试。捕获的脚本只是用特定数据和动作来线性表示每个脚本的一部分。当发生意外事件时,这类脚本是不稳定的。
        • 数据驱动技术

          1. 数据驱动的方法是将测试输入(数据)与测试用例分离,并将测试输入存放在一个电子表格中,这样可以使用不同的数据进行相同的测试。
          2. 不熟悉脚本语言的测试员可以从一个表格内输入测试数据并执行事先定义好的测试脚本。

          注意:此方法需要脚本语言方面的技术专家。

        • 关键字驱动技术

          1. 在关键字驱动的测试方法中,电子表格含有描述系统要采取的行为的关键字(也称为行为字)和测试数据。
          2. 测试员(即使不熟悉脚本语言)也能针对被测应用,使用这些关键字来定义测试。

          注意:此方法需要脚本语言方面的技术专家。

      • 静态分析工具
        警告信息不阻碍代码转换成可执行程序,但理想情况是应予以处理,以便将来更容易进行代码的维护。
        在开始就有计划的逐步使用分析工具来过滤一些警告信息是一种有效的方法。

      • 测试管理工具
        测试管理工具需要有与其他工具和表格有接口,以便产生符合组织所需格式的有用信息。

    组织内引入工具(6.3)

    • 需要考虑的关键点
      为组织选择工具所需要考虑的关键点有:
       评估组织的成熟度、分析引入工具的优点和缺点和认识引入工具能改善测试过程的可能性;
       根据清晰的需求和客观的准则进行评估;
       概念验证,在评估阶段要确认在现有的情况下使用工具对被测软件是否有足够效果,或为了有效使用工具,目前的基础设施需要如何改变;
       评估供应商(包括培训、提供的支持及其他商业方面考量),如果是非商业性工具要评估提供服务的供应商;
       为了在工具使用方面得到更好的指导和培训,需要先收集内部需求;
       评估培训需求时需要考虑现有测试团队的自动化测试技能;
       根据实际的情况估算成本-收益比。

    • 试点项目的目的
      将选择的工具引入组织要从一个试点项目开始,试点项目有以下目的:
       对工具有更多的认识;
       评估工具与现有的过程以及实践的配合程度,确定哪些方面需要作修改;
       定义一套标准的方法来使用、管理、储存 和维护工具及测试资产(比如,定义文件和测试的命名规则、创建库和定义模块化测试套件);
       评估在付出合理的成本后能否得到预期的收益。

    • 成功部署的因素
      在组织内成功部署工具的因素包括:
       逐步在组织的其余部分将工具推广到测试中;
       调整并改进过程来配合工具的使用;
       为新使用者提供培训和指导;
       定义使用指南;
       实施一种在实际运用中收集工具使用情况的方法;
       监控工具的使用和收益情况;
       为测试团队使用工具提供支持;
       在所有团队内收集经验和教训。

    展开全文
  • 初级程序员考试知识总结

    万次阅读 多人点赞 2018-10-14 15:55:53
    MAX函数:MAX(B2,B5)B2和B5两个数的最大值,&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;nbsp;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;nbsp;&
  • 初级算法学习步骤

    千次阅读 多人点赞 2018-07-29 18:51:51
    前言 零散整理一个多月终于整理完了。...一直没时间吧之前的总结整理出来,现在准备整理一下用java做算法的一些东西……学习了两个月左右算法,从啥都不会到小白再到算是初级……做一个总结,请高手多多指...
  • MongoDB初级部分

    万次阅读 2020-07-10 17:04:33
    快速入门 是跨平台、文档型(对象)的... Document(文档的数据结构和JSON基本一样,所有存储在集合中的数据都是BSON格式) BSON是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。 .
  • 产品经理的七个阶段

    千次阅读 2017-12-19 11:40:29
    个阶段基本上每天的工作都是写PRD、画原型、做DEMO。也就是基本处于一种被动分配任务的状态,接收的都是“二手需求”。一般而言自己思考产生的产品优化会比较少。也就是目前IT行业里面数百万的产品助理,甚至一些...
  • 花了三天通读了一些中英文文档,最后天一天看了一框架(gorm/gin),基本已经是初级小菜鸟阶段了… 安装配置文档: 编译器安装: download 参考知乎 开发环境配置: vscode&golang 安装vscode go插件可能有些...
  • 关于软件测试的几反思 - 测试工作的三个阶段

    万次阅读 多人点赞 2014-03-18 23:11:16
    其实这样的三个阶段也是一粗略的划分,并不一定要逐步的来发展,其实都是一些具体的做法和实践。以我目前经历过的实践只想到这样的层次,应该还有更高级的阶段。 我们越到后面我们发现进一步的努力带来的提升幅度...
  • 程序员的成长的六个阶段

    千次阅读 2008-04-04 09:04:00
    第一阶段阶段主要是能熟练地使用某种语言。这就相当于练武中的套路和架式这些表面...第三阶段阶段能深入地了解某个平台系统的底层,已经具有了初级的内功的能力,也就是“手中有剑,心中无剑”。第四阶级此阶段能直
  • grub4dos初级教程

    千次阅读 2018-07-27 10:28:14
    grub4dos初级教程 - GRUB4DOS、SYSLINUX及其它启动管理软件讨论专区 - 中国DOS联盟论坛 - 中国DOS联盟之联合DOS论坛 http://cndos.fam.cx/forum/viewthread.php?tid=28300&amp;amp;fpage=1 目录 1 grub4dos...
  • Java初级工程师常见面试题

    千次阅读 多人点赞 2019-01-28 15:42:02
    对于一个初级java开发人员的面试,面试时间一般为30~40分钟,有短的,先写一张笔试卷子,然后面试20分钟,也有长一些的,我最长的一次面试将近2小时,最后一轮纯技术面,看见面试官在一张纸上记上一个个知识,...
  • 16个初级自动化面试题,你知道不?

    万次阅读 2020-09-16 08:00:05
    线性脚本框架: 这是一个基本级别的自动化测试框架,其形式为“记录和回放”,但采用线性方式。这种类型的框架主要用于测试小型应用程序。 数据驱动框架:用于通过传递不同的测试数据集来创建测试自动化脚本。包含...
  • 昨天我收到了一条这样的消息: 如果你也有这样的疑惑,大概率你还处在初中级前端的阶段,对前端的理解也不够深,没有任何贬低的意思,毕竟这是必经的过程,但最终能不能成为大佬,就看...
  • 初级web程序员进阶指南

    千次阅读 多人点赞 2018-08-16 13:15:27
    初级程序员经常犯的错误集锦 1 命名不规范 2 日志不规范 3 拒绝写接口和假数据 4 不写单元测试 5 盲目集成 6 逻辑不清 7 不做方案 8 不关注性能 9 害怕重构 10 做出来就好,不考虑优雅的方案 11 不考虑...
  • 初级前端工程师需要掌握技能

    千次阅读 2019-07-11 10:45:43
    我们在经历过入门得阶段,已经了解了前端要做什么,并且把基本得语法学习过了可以独立做一些简单的页面了,那么就要继续学习达到初级前端工程师的水平,对于初级的前端工程师需要了解的就特别多了。 初级前端...
  • 个初级的前端工程师需要知道些什么?

    千次阅读 多人点赞 2016-07-19 20:10:37
    在经历过入门的阶段,已经了解了前端要做什么,并且把基本的语法学习过了可以独立做一些简单的页面了,那么就要继续学习达到初级前端工程师的水平,对于初级的前端工程师需要了解的就特别多了,需要对整个前端有一...
  • SAP初级顾问是如何炼成的!

    千次阅读 2012-11-08 14:15:03
     作为一每一想要转入SAP行业的学习者,我想大家都和我一样都想尽快的掌握SAP软件的学习方法。究其最根本的原因在于掌握了SAP软件之后可以将它实实在在的转换为生产力。再通俗一点来讲如果掌握了SAP软件的实施...
  • 产品经理的四个阶段(分享会笔记)

    千次阅读 2016-09-08 13:35:44
    提笔画流程,上马定需求,进可稳迭代,退可跪开发。我把产品经理分成四个阶段...这个阶段对于产品的好与坏的评判标准,基本是基于自己作为小白用户的视角。如何避免在这个阶段被开发吐槽:1.1 想清楚方案idea可能是上面
  • 第一阶段阶段主要是能熟练地使用某种语言。这就相当于练武中的套路和架式这些表面的东西。...第三阶段阶段能深入地了解某个平台系统的底层,已经具有了初级的内功的能力,也就是“手中有剑,心中无剑”。第四阶
  • 晚上自学java两个月能找工作吗?

    千次阅读 2018-10-26 14:24:22
    如果只是靠晚上自学两个月直接找一份java的工作在当前基本上很难,虽然现在java还是就业第一大语言,但入门的门槛明显高了许多,现在看招聘岗位上java几乎占到了很大比例,为什么职位多反而门槛提升了?主要还是从业...
  • 2020年茶艺师(初级)试题及答案及茶艺师(初级)复审考试,包含茶艺师(初级)试题及答案答案和解析及茶艺师(初级)复审考试练习。由安全生产模拟考试一点通公众号结合国家茶艺师(初级)考试最新大纲及茶艺师...
  • 初级产品经理的日常工作流程汇总

    千次阅读 2016-09-19 11:09:26
    前言:可能有一些产品新人在面试的时候经常被问产品经理工作内容,在这里不谈那些高级产品经理的工作内容(产品战略、需求发掘、项目管理…)只谈谈初级产品经理的工作内容,因为我作为一名产品新人,暂时接触的一些...
  • 两个月,这让我想到了我当年第一份工作,也是两个月从零学JAVA。当时就买了3本书,强啃了6周。记住一点,时间有限,别听网上很多大牛的,他们说的太杂。你要抓重点,你只要让人家知道,你足够初级开发就可以了,学了...
  • Android面试题【初级工程师版】

    千次阅读 2017-11-24 08:36:22
    初级工程师面试题 Android 基础(2016.01.20 更新)(★★). 一、 Activity 1、 什么是 Activity?...5、 两个 Activity 之间跳转时必然会执行的是哪几个方法? 6、 横竖屏切换时 Activit
  • 股票市场运动的四个阶段

    千次阅读 2006-10-31 12:43:00
    一、股票市场运动的四个阶段 以30日均线为标准,一次完整的股票市场循环运动必然包含上图框定的四个阶段,没有任何一只股票能够例外。30日均线是机构庄家操盘战略战术动作展开的生命线,其中的短线操作价值务必要...
  • 毕业生和初级程序员(一般是工作经验3年以下)大多处于事业的青黄不接的阶段,在找工作时往往会遇到缺乏实际项目经验的瓶颈,作为技术面试官,我也经常在面试过程中感受到这些候选人缺乏实际经...
  • 初级web前端面试题

    千次阅读 多人点赞 2020-11-07 15:14:32
    文章目录一、JS1、js基本类型和引用类型2、如何判断js数据类型3、js 拷贝4、事件处理机制5、原型和原型链6、什么是闭包7、事件循环机制(event loop)8、前端模块化9、es6新增特性1.let代替var关键字;2.const3.箭头...
  • 哈喽,大家好,今天我们来聊聊如何成为一名初级软件测试工程师,需要必备那些知识和技能。 什么是软件测试 软件测试的经典定义是:在规定的条件下对程序进行操作,以发现程序错误,衡量软件品质,并对其是否能满足...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,741
精华内容 11,096
关键字:

初级阶段两个基本点是