Coder Social home page Coder Social logo

justcodebryan.github.io's Introduction

  • 👋 Hi, I’m @justcodebryan
  • 👀 I’m interested in Front-end
  • 🌱 I’m currently learning React & Typescript

Blog Link: justcodebryan blog

justcodebryan.github.io's People

Contributors

justcodebryan avatar

Stargazers

 avatar

Watchers

 avatar

justcodebryan.github.io's Issues

JavaScript中的变量提升

JavaScript中的变量提升问题是一个很常见的面试题目,在阅读《你不知道的JavaScript》这本书又有了新的感悟。

变量提升是什么

首先来看书中的两个例子:

  // example 1
  a = 2;
  var a;
  console.log(a);  // ans:2
  // example 2
  console.log(a);
  var a = 2;  // ans:undefined

这两个例子非常简单,解释如下

  1. 例1中的var a变量的声明被提升到了最上面,所以在下面进行赋值的时候,成功地将2赋给了a这个变量。可以看作是下面这样的代码:
  var a;
  a = 2;
  console.log(a);
  1. 例2中可以像上面一样先将变量提升的代码在脑里想一想,得到下面这样的代码:
  var a;
  console.log(a);  // 定义了变量a但是没有给它赋值,所以它的值现在是undefined
  a = 2;

函数的变量提升

js中的函数同样会进行变量提升这一操作,但是函数有两种声明形式

  1. 函数声明 - function func() { };
  2. 函数表达式 - var func = function() { };

需要注意的是函数声明才会将其整个函数提升到整个脚本的最顶上去声明,但是函数表达式会像前面的变量提升一样,先声明变量然后再给它赋值,只不过是赋给变量一个函数(完全可以将其与变量提升一样处理)。在函数表达式的变量提升中,如果出现了先调用然后再声明的情况,会抛出的错误是TypeError(类型错误), 而不是ReferenceError(引用错误),这里可以理解为先调用的变量的值应该为undefined, 而undefined并不是一个函数,不能调用,所以会抛出TypeError

即使是将一个具名的函数赋值给一个变量,也是不能在声明之前调用的:

  foo(); // TypeError
  bar(); // ReferenceError
  
  var foo = function bar() {
    ...
  }

经过变量提升以后,代码应该变成以下这种情况了:

  var foo;
 
  foo(); // TypeError
  bar(); // ReferenceError
  
  foo = function() {
    var bar = ...self...
  }

之前在学习变量提升这一块内容的时候,时常会有疑问,如果多个函数变量提升到底会变成什么样?

  foo();  // 1

  var foo;

  function foo() {
    console.log(1);
  }

  foo = function() {
    console.log(2);
  }

在这段代码中,尽管var foo是在function foo() ...声明之前的,但是他是重复的声明,所以被忽略了,因此函数声明会被提升到普通变量之前。但是尽管var声明会被忽略掉,但是出现在后面的函数声明还是可以覆盖掉前面的

  foo();  // 3

  function foo() {
    console.log(1);
  }

  var foo = function() {
    console.log(2);
 }

  function foo() {
    console.log(3);
  }

总结

  • Javascript编译器会将var a = 2看作var aa = 2两个过程
  • 函数和变量都会提升
  • 声明本身会被提升,但是包括函数表达式的赋值在内的赋值操作不会提升
  • 如果函数声明和变量声明同时存在且同名,变量的声明会因为重复被忽略
  • 如果多个同名的函数声明,后面的声明会覆盖掉前面的声明

CSS居中解决方案

CSS 居中方案 📏

元素水平居中

将行内元素包裹在一个属性displayblock的父元素中, 父元素中添加

.box {
  text-align: center;
}

块状元素解决方案

需要为父元素设定宽高

.box {
  width: 200px;
  margin: 0 auto;
}

元素水平垂直居中

🌰position元素已知宽度, 绝对定位+margin反向偏移(transform)

.wrap {
  position: relative;
  background-color: orange;
  width: 300px;
  height: 300px;
}
.example2 {
  background-color: red;
  width: 100px;
  height: 100px;
  position: absolute;
  left: 50%;
  top: 50%;
  margin: -50px 0 0 -50px;
  /* 第二种: 将margin换成transform, 如下 */
  /* transform: translate(-50%, -50%); */
}

🌰flex布局

.wrap {
  background-color: #ff8c00;
  width: 200px;
  height: 200px;
  display: flex;
  justify-content: center; /* 使子项目水平居中 */
  align-items: center; /* 使子项目垂直居中 */
}
.example3 {
  background-color: #f00;
  width: 100px;
  height: 100px;
}

还可以使用table-cell布局, 但是因为该布局对于资源的消耗过大, 现在基本没有使用, 不必了解

🌰 绝对布局

<div class="wrap">
  <div class="example3">
    居中显示
  </div>
</div>
.wrap {
  position: relative;
  background-color: orange;
  width: 200px;
  height: 200px;
}
.example3 {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: red;
  width: 100px;
  height: 100px;
  margin: auto;
}

🌰 给子元素相对定位, 在通过translateY()得到垂直居中

.wrap {
  position: relative;
  background-color: orange;
  width: 200px;
  height: 200px;
}
.example3 {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background-color: red;
  width: 100px;
  height: 100px;
  margin: 0 auto;
}

🌰 利用inline-blockvertical-align: middle去对齐after伪元素

居中块的尺寸可以做包裹性、自适应内容,兼容性也相当好。缺点是水平居中需要考虑inline-block间隔中的留白(代码换行符遗留问题)

.wrap {
  text-align: center;
  overflow: auto;
  width: 200px;
  height: 200px;
  background-color: orange;
}
.example3 {
  display: inline-block;
  background-color: red;
  vertical-align: middle;
  width: 100px;
  height: 100px;
}
.wrap:after {
  content: '';
  display: inline-block;
  vertical-align: middle;
  height: 100%;
  margin-left: -0.25em;
  /* To offset spacing. May vary by font */
}

🌰display: flex-box

能解决各种排列布局问题

.wrap {
  display: -webkit-flex;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-box;
  display: flex;
  -webkit-box-align: center;
  -moz-box-align: center;
  -ms-flex-align: center;
  -webkit-align-items: center;
  align-items: center;
  -webkit-box-pack: center;
  -moz-box-pack: center;
  -ms-flex-pack: center;
  -webkit-justify-content: center;
  justify-content: center;
  width: 200px;
  height: 200px;
  background-color: orange;
}
.example3 {
  width: 100px;
  height: 100px;
  background-color: red;
}

前端测试驱动开发初探-jest+enzyme+enzyme-adapter-react-16+react+typescript

测试分类

按照软件工程自底而上的概念, 前端测试分为以下四类:

  1. 单元测试(Unit Testing)

    指的是以原件的单元为单位,对软件进行测试。单元可以是一个函数,也可以是一个模块或一个组件,基本特征就是只要输入不变,必定返回同样的输出。一个软件越容易些单元测试,就表明它的模块化结构越好,给模块之间的耦合越弱。React的组件化和函数式编程,天生适合进行单元测试

  2. 集成测试(Integration Testing)

    在单元测试的基础上,将所有模块按照设计要求组装成子系统或者系统,进行测试

  3. 端对端测试(E2E Testing)

    在正式全面的测试之前,对主要功能进行的与测试,确认主要功能是否满足需要,软件是否能正常运行

  4. 功能测试

    相当于是黑盒测试,测试者不了解程序的内部情况,不需要具备编程语言的专门知识,只知道程序的输入、输出和功能,从用户的角度针对软件界面、功能和外部结构进行测试,不考虑内部的逻辑

建议的测试数量:

单元测试 > E2E测试 > 快照测试

选型: Jest + Enzyme + enzyme-adapter-react-16

安装依赖, 因为是React16以上的版本, 所以需要安装插件

yarn add jest enzyme enzyme-adapter-react-16 @types/jest @types/enzyme @types/enzyme-adapter-react-16 ts-jest -D

因为需要使用sass, 所以需要添加identity-obj-proxy才能使得测试正常运行

yarn add identity-obj-proxy -D

快照序列化需要enzyme-to-json

yarn add enzyme-to-json -D

package.json中添加

{
    "scripts":{
        "test": "jest --config jest.config.js --no-cache"
    }
}

在根目录下添加__mocks__文件夹, 并且创建js文件命名为fileTransformer.js

用于mock其他文件, 图片之类的文件

// fileTransformer.js
const path = require('path');

module.exports = {
  process(src, filename, config, options) {
    return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
  },
};

在根目录添加一个文件jest.config.js

// jest.config.js文件
module.exports = {
  verbose: true,
  setupFiles: ['./tests/setup.js'],
  setupFilesAfterEnv: ['./tests/setupAfterEnv.ts'],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'md'],	// 文件扩展名
  moduleNameMapper: {
    "\\.(css|scss)$": "identity-obj-proxy"	// scss文件处理的依赖
  },
  transform: {
    "^.+\\.tsx?$": "ts-jest",
    "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileTransformer.js"
  },
  testRegex: "(/__test__/.*|(\\.|/)(test|spec))\\.tsx?$",	// 正则表达式找到需要测试的文件
  globals: {
    "ts-jest": {
      tsConfig: 'tsconfig.json',
    },
  },
  testPathIgnorePatterns: ['/node_modules/', 'dist'],	// 忽略测试的文件夹
  collectCoverage: true,	// 收集覆盖率
  coverageDirectory: './coverage',	// 覆盖率报告
  snapshotSerializers: ["enzyme-to-json/serializer"]	// 快照序列化
};

在根目录下添加tests文件夹

创建setup.js

// setup.js
const React = require('react');

// eslint-disable-next-line no-console
console.log('Current React Version:', React.version);

/* eslint-disable global-require */
if (typeof window !== 'undefined') {
  global.window.resizeTo = (width, height) => {
    global.window.innerWidth = width || global.window.innerWidth;
    global.window.innerHeight = height || global.window.innerHeight;
    global.window.dispatchEvent(new Event('resize'));
  };
  global.window.scrollTo = () => {};
  if (!window.matchMedia) {
    Object.defineProperty(global.window, 'matchMedia', {
      value: jest.fn(query => ({
        matches: query.includes('max-width'),
        addListener: jest.fn(),
        removeListener: jest.fn(),
      })),
    });
  }
}

// 因为是React16版本的缘故, 需要配置adapter
// 配置全局adapter
const Enzyme = require('enzyme');

const Adapter = require('enzyme-adapter-react-16');

Enzyme.configure({ adapter: new Adapter() });

Object.assign(Enzyme.ReactWrapper.prototype, {
  findObserver() {
    return this.find('ResizeObserver');
  },
  triggerResize() {
    const ob = this.findObserver();
    ob.instance().onResize([{ target: ob.getDOMNode() }]);
  },
});

创建setupAfterEnv.ts:

import toMatchRenderedSnapshot from './matchers/rendered-snapshot';

expect.extend({
  toMatchRenderedSnapshot,
});

快照测试生成一个UI结构, 并用字符串的形式放在__snapshots__文件里, 通过, 比较两个字符串来判断UI是否发生改变

创建matchers/rendered-snapshot.ts

import { render } from 'enzyme';
import { ReactElement } from 'react';

// 快照测试
export default function toMatchRenderedSnapshot(
  this: jest.MatcherUtils,
  jsx: ReactElement<unknown>,
): { message(): string; pass: boolean } {
  try {
    expect(render(jsx)).toMatchSnapshot();

    return {
      message: () => 'expected JSX not to match snapshot',
      pass: true,
    };
  } catch (e) {
    return {
      message: () => `expected JSX to match snapshot: ${e.message}`,
      pass: false,
    };
  }
}

单元测试

模范

命名规范: ***.test.tsx / index.spec.tsx

describe('测试套件标题', () => {
    it('测试标题', () => {
        
    })
})

常用API

此处仅仅列出比较常用的API

Jest

globals API

afterAll(fn, timeout):所有测试用例跑完以后执行的方法

beforeAll(fn, timeout):所有测试用例执行之前执行的方法

afterEach(fn):在每个测试用例执行完后执行的方法

beforeEach(fn):在每个测试用例执行之前需要执行的方法

常见断言

toHaveBeenCalled():用来判断mock function是否被调用过

toHaveBeenCalledTimes(number):用来判断mock function被调用的次数

resolves:用来取出promise为fulfilled时包裹的值,支持链式调用

rejects:用来取出promise为rejected时包裹的值,支持链式调用

Enzyme

渲染方法

  1. shallow: 浅渲染, 子组件不会渲染, 效率高, 可以用jQuery的方式访问组件信息
  2. mount: 完全渲染, 将组件渲染加载成一个真实的DOM节点
  3. render : 将React组件渲染成静态的HTML字符串, 可以用来分析html结构

因为shallowmount返回的是DOM对象, 可以用simulate进行交互模拟, render不可以

shallow - 一般情况下都能满足

render - 需要对子组件进行判断

mount - 需要测试组件的生命周期

类的原型方法可以通过shallowmount方法渲染组件以后, 在通过 spyOn(instance: Prototype, MethodName: string)来监控并且测试类的原型的方法

注意: 需要在spy完某一个函数之后对其进行mockRestore

常用方法

  1. simulate(event, mock): 模拟事件, 用来触发事件, event为事件名称, mock为一个event object
  2. find(selector): 根据选择器查找节点, selector可以是CSS中的选择器, 或者是组件的构造函数, 组件的display name
  3. state(): 返回根组件的状态, 但是如果使用React Hooks中的useState是无法获取到该状态
  4. text(): 返回当前组件的文本内容
  5. html(): 返回当前组件的HTML代码

测试覆盖率报告

%stmts是语句覆盖率(statement coverage)表示是不是每个语句都执行了?

Branch代表分支, 比如if-else分支中没有测试到的

Funcs代表函数, 没有测试到的函数也会在显示百分比

Lines代表覆盖的行数, 显示已经覆盖的行数的百分比

Uncovered Line: 表示测试没有覆盖到行数, 具体是哪几行没有被覆盖到

下面会显示通过的测试套件, 测试数量以及快照数量

相关资料

  1. Jest官方文档
  2. 对 React 组件进行单元测试
  3. 如何对 react hooks 进行单元测试
  4. 使用Jest进行React单元测试
  5. React 测试指南
  6. 使用jest+enzyme测试react组件
  7. 用 Enzyme 测试使用 Hooks 的 React 函数组件

浏览器输入URL到显示页面中一些疑问整理

  1. 为什么JS的解析会阻塞DOM Tree的解析

    因为JS的解析运行被默认为有可能使用document.write方法, document.write方法会向文档流里面写入字节, 这样的话, 会强制刷新剩下的文档

    所以为了避免重复操作, 就将剩下的dom解析给阻塞了

    而且在这时的js脚本能够操作前面已经解析完成的dom元素, 而不能操作后面还未解析的dom元素, 因为还未解析出来, dom元素还不存在

    document.write使用流程:

    1. document.open
    2. document.write
    3. document.close

    从另外一个角度理解, 当浏览器在解析DOM树的时候, 已经调用了document.open了, 那么在这种情况下, 使用document.write可以直接在文档中间的位置写入内容, 当文档解析完了触发documentloaded的时候, 浏览器会调用document.close。这个时候相当于写入文件完成并且管道关闭了,如果还要再进行document.write这个操作的时候, 需要重新调用document.open。因为是系统自动调用的, 系统不知道哪个document,所以就会重新生成一个空白的document , 并且把document.write里面要写入的内容写进去, 并且覆盖掉原来的document, 这样就会造成documentloaded之后调用document.write会将整个页面都覆盖掉。

  2. JS script不同标签的行为(async, defer)

    async - 异步加载, 同步执行

    defer - 异步加载, onload之后执行

  3. CSSOM解析会阻塞DOM Tree的解析或者JS的运行吗

这里共有两种情况:

  1. 如果CSS外链标签在文件的最上方, 异步下载解析, 但是文件中间有一个script标签, 当整个文件解析到了script标签的时候, 假定CSS标签还未下载完成, 如果script标签里面有get ComptuedStyle的操作, CSS的解析就会阻塞JS的执行, 直到CSS解析完成了以后, 获取了正确的样式, 这样计算出来的结果才是正确的。
<html>
  <head>
    <link src="./index.css" />
    <title>test app</title>
  </head>
  <body>
    <script src="./test.js"></script>
  </body>
</html>
  1. 前面的情况同上, 唯一不同的点就是, script标签里面没有做get computed style的操作。这时,就和一般的操作是一样的。

  2. 浏览器并行下载的最大数量

    各个浏览器的并行下载数量不同, 之前的最大是6个

    Firefox 2:  2
    Firefox 3+: 6
    Opera 9.26: 4
    Opera 12:   6
    Safari 3:   4
    Safari 5:   6
    IE 7:       2
    IE 8:       6
    IE 10:      8
    Edge:       6
    Chrome:     6
  3. 为什么需要document.write方法
    以下内容摘自Stack Overflow
    When document.write() is executed after page load, it replaces the entire header and body tag with the given parameter value in string.

    • document.write (henceforth DW) does not work in XHTML
    • DW does not directly modify the DOM, preventing further manipulation (trying to find evidence of this, but it's at best situational)
    • DW executed after the page has finished loading will overwrite the page, or write a new page, or not work
    • DW executes where encountered: it cannot inject at a given node point
    • DW is effectively writing serialised text which is not the way the DOM works conceptually, and is an easy way to create bugs (.innerHTML has the same problem)

    The only seem appropriate usage for document.write() is when working third parties like Google Analytics and such for including their scripts. This is because document.write() is mostly available in any browser. Since third party companies have no control over the user’s browser dependencies (ex. jQuery), document.write() can be used as a fallback or a default method.(兼容性问题, 当需要加入google analytics等的脚本, 这是最容易加入依赖的方法)

  4. render tree的行为, 如果CSS发生了改变以后

    repaint(重绘)和reflow(重排)

  5. 阻塞渲染和阻塞解析

    阻塞渲染: CSS的解析会阻塞渲染 -> 基于不要让用户看到没有CSS, 即没有样式的页面, 但是也会有例外的情况, 有可能很久没有返回CSS, 这时, CSS一直无法解析, 那就有可能出现没有样式的页面, 但是最后接收到了页面CSS就会让重新刷新

    阻塞解析: JS的运行会阻塞DOM树解析

    并行下载: 最大数量下载量 -> 下载的时间和时机看代码的位置和配置

HTTP-基础

HTTP

HTTP 简介

定义: Web使用一种名为HTTPHyperText Transfer Protocol,超文本传输协议)的协议作为规范,完成从客户端到服务器等一系列运作流程。而协议是指规则的约定。可以说,Web是建立在HTTP协议上通信的。

采用请求/响应模型

  • 请求报文

    • 请求方法
    • URL
    • 协议版本
    • 请求头部
    • 请求数据
  • 状态行(响应)

    • 协议版本
    • 成功或者错误的代码
    • 服务器信息
    • 响应头部
    • 响应数据

特点: 不保存状态的协议 -> 无状态协议

TCP/IP 协议族

  • 应用层
    应用层规定了向用户提供应用服务时通信的协议
    例如:
    • FTP(File Transfer Protocol, 文件传输协议)
    • DNS(Domain Name System, 域名系统)
    • HTTP
  • 传输层
    传输层对接上层应用层, 提供处于网络连接中两台计算机之间的数据传输所使用的协议
    传输层有两个性质不同的协议:
    • TCP(Transimission Control Protocol) - 传输控制协议
      • 全双工
      • 三次握手建立连接, 四次挥手断开连接 -> 传输稳定可靠
      • 没有UDP那么高效
    • UDP(User Data Protocol) - 用户数据报协议
      • 面向无连接
      • 不保证有序且不丢失的传递到对端(不稳定)
      • 高效轻便
  • 网络层
    网络层规定了数据通过怎么样的传输路线到达对方计算机(IP协议)
    与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所起的所用就是在众多的选项内选择一条传输路线。
  • 链路层
    用来处理连接网络的硬件部分, 包括控制操作系统, 硬件的设备驱动, NIC(Network Interface Card, 网络适配器, 网卡), 及光纤等物理可见部分

一般的 web 应用通信传输流是这样的
web 应用通信流程

名词辨析

  • 串行连接: HTTP有无连接的特性,即每次连接只能处理一个请求,收到响应后立即断开连接。HTTP/1.0版本(称为串行连接或短连接、短轮询)中每次HTTP通信后都要断开TCP连接,所以每个新的HTTP请求都需要建立一个新的连接。
  • 持久连接: 长连接,长轮询。
    • 一定时间内,同一域名下的HTTP请求,只要两端都没有提出断开连接,则持久保持TCP连接状态,其他请求可以复用这个连接通道。
    • HTTP/1实现并默认了所有连接都是持久连接,这样客户端发起多个HTTP请求时就减少了TCP握手造成的网络资源和通信时间的浪费。
    • 缺点: 持久连接采用阻塞模式,下次请求必须等到上次响应返回后才能发起,如果上次的请求还没返回响应内容,下次请求就只能等着(就是常说的线头阻塞)
  • 管道化持久连接: 管道化可以不用等待响应返回而发送下个请求并按顺序返回响应, 现代浏览器并未默认开启管道化
    • 多个请求共用同一个TCP连接
    • 不用等待可以发出多个请求
    • 响应必须按顺序返回
  • HTTP/2.0多路复用: 每个HTTP请求都有一个序列标识符, 这样浏览器就可以并发多个请求, 服务器收到数据根据标识符重新排列成不同的请求报文, 而不会导致数据错乱。同样服务器可以返回多个响应给浏览器
  • WebSocket: HTML5提出的一种客户端和服务端通讯的全双工协议

URI

HTTP协议使用URI定位互联网上的资源。概念:

  • URI(Universal Resource Identifier:统一资源标识符)
  • URL(Universal Resource Locator:统一资源定位符)
  • URN(Universal Resource Name:统一资源名称)。

URI, URL和URN的关系

HTTP版本

HTTP/1.0

简单的网页和网络请求, 比较简单
特点: 每次请求都打开一个新的TCP连接, 收到响应之后立即断开连接

HTTP/1.1

  • 引入更多缓存控制策略, 如Entity tag, If-Unmodified-Since, If-Match, If-None-Match
  • 允许范围请求, 即在请求头中加入Range头部
  • 请求消息和响应消息都必须包含在Host头部, 以区分同一个物理主机中的不同虚拟主机的域名
  • 默认开启持久连接

HTTP/2.0

两个重要概念:

  • 帧(frame): 数据传输的最小单位, 每个帧都有序列标识表明该帧属于哪个流
  • 流(stream): 多个帧组成的数据流, 每个流表示一个请求

新特性:

  • 新的二进制格式, HTTP/1.x基于文本, 解析存在天然缺陷。
  • 多路复用:
    • 一个TCP连接可以存在多个流, 服务端则可以通过帧中的标识知道该帧属于哪个流(即请求),通过重新排序还原请求。
    • 允许并发的发起多个请求,每个请求及该请求的响应不需要等待其他的请求或响应,避免了线头阻塞问题, 提高传输性能
  • 头部压缩: 使用encoder减少需要传输的头部大小, 通讯双方各自cache一份头部fields表, 避免重复头部的传输, 减小了需要传输的大小
  • 服务端推送: 这里的服务端推送指把客户端所需要的css/js/img资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤(从缓存中取)

## HTTP/3.0
HTTP/2.0的缺点: 因为多路复用, 同一域名下只需要使用一个TCP连接, 连接出现了丢包, 整个TCP都要等待重连, 导致后面的数据都被阻塞了。
HTTP/1.x因为使用多个TCP连接, 单个TCP出现丢包, 只会影响一个连接

HTTP报文

用于HTTP协议交互的信息被称为HTTP报文:

  • 客户端的HTTP报文叫请求报文
    • 请求行(请求方法, 协议版本)
    • 请求首部(请求URI, 客户端信息等)
    • 内容实体(用户信息和资源信息等)
      请求报文结构
  • 服务端的HTTP报文叫响应报文
    • 状态行(协议版本, 状态码)
    • 响应首部(服务器名称, 资源标识等)
    • 内容实体(服务器返回的资源信息)
      响应报文结构

请求方法

  • GET:一般用于获取服务器资源
  • POST:一般用于传输实体主体
  • PUT:一般用于传输文件
  • DELETE:用于删除文件
  • HEAD:用于获取报文首部,不返回报文主体
  • OPTIONS:用于询问请求URI资源支持的方法

GET方法和POST方法的区别:

状态码

2XX: 成功
3XX: 重定向(表明浏览器要执行特殊处理)
4XX: 客户端错误
5XX: 服务器错误

首部字段

两种请求

浏览器发送CORS请求(跨域请求)时, 会将请求分为简单请求与复杂请求

简单请求:

  1. 请求的方法只能为HEADGETPOST
  2. 无自定义请求头
  3. Content-Type只能是这几种:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

简单请求 - 先执行后判断

@startuml

participant 浏览器 as Browser
participant 服务器 as Server

note left Browser: 检测到请求是CORS请求, 添加origin字段(其中包含页面源信息: 协议、域名、端口)
Browser -> Server: 发送CORS请求

note right Server: 对比origin, 判断是否接受该源
Server -> Browser: 响应请求

note left Browser: 检查响应头是否允许跨域信息, 如果不允许, 浏览器抛出相应的错误信息

@enduml

复杂请求:

  1. PUT, Delete方法的ajax请求
  2. 发送JSON格式的ajax请求(比如post数据)
  3. 带自定义头的ajax请求

复杂请求在发生请求时, 如果是 CORS 请求,浏览器预先发送一个 option 请求。浏览器这种行为被称之为预检请求(注意如果不是跨域请求就不会发生预检请求,比如反向代理)。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.