applyMiddleware

applyMiddleware(…middlewares)

使用包含自定义功能的middleware来扩展Redux是一种推荐方式。Middleware可以让你包装store的dispatch方法来达到你想要的目的。
同事,middleware还拥有“可组合”这一关键特性。多个middleware可以被组合到一起使用,徐北广场middleware链。其中每个middleward
都不需要关心链中它前后的middleware的任何信息

例如redux-thunk支持dispatch function,以此让action creator控制反转。被dispatch的function会接收dispatch作为参数,并且可以
异步调用它们。这类的function就称为thunk

Middleware并不需要和createStore绑在一起使用。

参数

  • …middlewares(arguments):

遵循Redux middlewareAPI的函数。每个middleware接收store的dispatch和getState函数作为命名参数,并返回一个函数
该函数会被传入称为next的下一个middleware的dispatch方法,并返回一个接收action的新函数,这个函数可以直接调用next(action),
或者在其他需要的时刻调用。调用链中最后一个middleware会接收真实的store的dispatch方法作为next参数,并结束调用链

返回值

(Function)一个应用了middleware后的store enhancer。这个store enhancer就是一个函数,并且需要应用到createStore。它会
返回一个应用了middleware的新的createStore

Example:自定义Logger Middleware

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { createStore, applyMiddleware } from 'redux';
import todos from './reducers';

function logger({getState}) {
return (next) => (action) => {
console.log('will dispatch', action);

//调用middleware链中下一个middleware的dispatch
let returnValue = next(action);

console.log('state after dispatch', getState());

return returnValue;
}
}

let createStoreWithMiddleware = applyMiddleware(logger)(createStore);
let store = createStoreWithMiddleware(todos, ['use redux']);

store.dispatch({type: 'ADD_TODO', text: 'understand the middleware'})

/*
控制台打印
will dispatch:{type: 'ADD_TODO', text: 'use redux'}
state after dispatch['use redux', 'understand the middleware']
*/



Example:使用Thunk Middleware来做异步Action

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import * as reducers from './reducers';

//调用applyMiddleware,使用middleware增强createStore
let createStoreWithMiddleware = applyMiddleware(thunk)(store);

let reducer = combineReducers(reducers);
//像原生createStore一样使用
let store = createStoreWithMiddleware(reducer);

function fetchSecretSauce() {
} return fetch('http:xx.xx.xx/xx/xx');

//普通的action creator,它们返回的action不需要middleware就能被dispatch
//但是,它们只表达【事实】,并不表达【异步数据流】
function makeASandwich(forPerson, secretSauce) {
return {
type: 'MAKE_SANDWICH',
forPerson,
secretSauce
}
}

function apologize(fromPerson, toPerson, error) {
return {
type: 'APOLOGIZE',
fromPerson,
toPerson,
error
}
}

function withdrawMoney(amount) {
return {
type: 'WITHDRAW',
amount
}
}

//即使不使用middleware,也可以dispatch action:
store.dispatch(withdrawMoney(100))

//如何处理异步action?如:API调用,或者路由跳转
//用thunk:它是一个返回函数的函数

function makeASandWichSecretSauce(forPerson) {

//控制反转!返回一个接收`dispatch`的函数
//Thunk middleware知道如何把异步 thunk action 转为普通action
return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
)
}
}

//Thunk middleware可以让我们像dispatch普通action一样dispatch异步的thunk action
store.dispatch(makeASandWichSecretSauce('Me'));

Redux API和词汇解释

Redux API和词汇解释 ###词汇解释 - State

1
顶层state为一个对象,键-值集合,尽可能保证state可序列化。


- Action

1
2
Action是一个用来表示即将改变state的对象。它是将数据放入store的唯一途径。action必须拥有一个`type`域,用来
指明需要执行的action type


- Reducer

1
2
3
Reducer接收两个参数:之前累计运算的结果和当前被累计的值,返回的是一个新的累计结果。在Redux中,累计对的运算
结果是state对象,而被累计的值是action。Reducer由上次累计的结果state与当前被累积的action计算得到一个新state。
`Reducer必须是纯函数,不要在Reducer中有副作用操作,比如API调用`


- dispatch函数

1
2
*dispatch function*是一个接收action或者异步action的函数,该函数要么往store分发一个或多个action,要么不分发
任何action


一般的dispatch function 和store实例提供的没有middleware的base dispatch function之间的区别: Base dispatch functionzing是同步地把action与上一次从store返回的state发往reducer,然后重新计算出新的state,它 期望action会是一个可以被reducer消费的普通对象。

1
2
3
`Middleware`封装了base dispatch function,允许dispatch function处理action之外的异步action。middleware封装了
base dispatch function,允许dispatch function处理action之外的异步action。Middleware可以改变、延迟、忽略action
或异步action,也可以在传递给下一个middleware之前对它们进行解释


- Action Creator
Action Creator就是一个创建action的函数,action是一个信息的负载,而action creator是一个创建action的工厂,调用action creator 只会生成action,但不分发,需要调用store的dispatchfunction才会引起变化,bound action creator,是指一个函数调用action creator并 立即将结果分发给一个特定的store实例。

1
如果action creator需要读取当前的state、调用API、或引起诸如路由变化等副作用,那么它应该返回一个异步action而不是action。


- 异步Action

1
2
3
异步action是一个发给dispatch函数的值,但是这个值还不能被reducer消费。在发往base dispatch() function之前,middleware会把异步action
转化成一个或一组action。异步action可以有多种type,这取决于你所使用的middleware,比如thunk,虽然不会立即把数据传递给reducer,但是一旦
操作完成就会出发action的分发事件。


- Middleware

1
2
3
4
Middleware是一个组合dispatch function的高阶函数,返回一个新的dispatch function,通常将异步actions转换成action。

Middleware利用复合函数使其可以组合其他函数,可用于记录action日志、产生其他诸如变化路由的操作用,或将异步的API调用
变为一组同步的action


- Store

1
Store维持着应用的state tree对象。因为应用的构建发生于reducer,所以一个Redux应用当中应当只有一个store


- Store Creator

1
2
Store Creator是一个创建Redux store的函数。理解由Redux导出的base store creator与从store enhancer返回的store creator
之间的区别


- Store enhancer

1
2
Store enhancer是一个组合store creator的高阶函数,返回一个新的强化过的store creator。这与middleware相似,它允许你通过符合函数
改变store接口



API文档
#### 顶级暴露的方法 - createStore(reducer, [initialState])

1
创建一个Redux store来以存放应用中的所有state,应用中有且仅有一个store

参数

  1. reducer(*Function*):接收两个参数,分别是当前的state树和要处理的action,返回新的state树。

  2. [initialState](any):初始时的state,如果你使用combineReducers创建reducer,它必须是一个普通对象,与传入的keys保持同样的结构

返回值

1
(Store):保存了应用所有state的对象。**改变state的惟一方法是dispatch action。**你也可以subscribe坚挺state的变化,然后更新UI


- combineReducers(reducers)

1
2
3
**state对象的结构由传入的多个reducer的key决定**,通过为传入对象的reducer命名来控制state key的命名。例如
`combineReducers({todos: todoReducer, counter: counterReducer})`将state的结构变为`{todos: 'xxx', counter: 'xxx'}`
ES6的简写方法`combineReducers({ counter, todos })`等价于`combineReducers({ counter: counter, todos: todos })`

####Store API
*Store

  • getState()

  • dispatch(action)

  • subscribe(listener)

  • getReducer()

  • replaceReducer(nextReducer)

react生命周期

组件的生命周期说明


constructor的说明

组建初始化时运行,仅一次

componentWillMount(组件即将镶嵌)

在初始化渲染之前立刻调用,如果在这个方法内调用setState()方法,render()将会感知到更新后的state,将会执行仅一次,尽管state改变了。

componentDidMount(组件已经镶嵌)

仅调用一次
在组件初始化渲染之后立刻调用一次,在生命周期的这个时间点,组件拥有一个DOM展现,你可以通过ReactDOM.findDOMNode(this)获取整个dom节点
也可以访问dom组件中的ref属性代表的组件,可以在该方法中发送ajax请求
原先的this.getDOMNode()已经被弃用了,

componentWillReceiveProps(组件即将接收props)

在组件接收到新的props的时候调用,在初始化渲染的时候,改方法不会调用,用此函数可以作为react在prop传入之后,render()渲染之前更新state的机会。
老的props可以通过this.props获取到。在该函数中调用this.setState()将不会引起第二次渲染。

shouldComponentUpdate(组件应该被更新)

在接受到新的props或者state,将要渲染之前调用。该方法在初始化渲染的时候不会调用,在使用forceUpdate方法的时候也不会。如果确定新的props和state
不会导致组件更新,则此处应该返回false.

shouldComponentUpdate() {
    return nextProps.id != this.props.id;
}

如果shouldComponentUpdate返回false,则render()将不会执行,直到下一次state改变(另外componentWillUpdate和componentDidUpdate也不会被调用)
默认情况下,shouldComponentUpdate总会返回true,使用shouldComponentUpdate可以提升应用的性能

componentWillUpdate(组件即将更新)

在接受到新的props和state之前立刻调用,在初始化渲染的时候该方法不会被调用。使用该方法做一些更新之前的准备工作。
注:不能在该方法中使用this.setState()如果需要更新state来相应某个prop的改变请使用componentWillReceiveProps()

componentDidUpdate(组件已经更新)

在组件的更新已经同步到DOM中之后立刻被调用。改方法不会在初始化渲染的时候调用。使用该方法可以在组件更新之后操作DOM元素
使用该方法,你仍然可以获取DOM节点

componentWillUnmount(组件将要移除)

在组件从DOM中移除的时候立刻被调用。
在该方法中执行任何必要的清理,比如无效的定时器,或者清除在componentDidMount中创建的DOM元素

WEB服务器、代理

代理和网关的对比
代理连接的是两个或多个使用相同协议的应用程序,二网关连接的则是两个或多个使用不同协议的
端点。网关扮演的是“协议转换器”的角色,即使客户端和服务器使用不同的协议,客户端也可以通过
它完成与服务器之间的事务处理

为什么使用代理

  • 安全防火墙
  • Web缓存
  • 反向代理

    代理可以假扮web服务器。这些被称为替代物或者反向代理的代理接受发送给Web服务器的真是请求
    ,但与Web服务器不同的是,他们可以发起与其他服务器的通信,以便按需定位所请求的内容。
    可以用这些反向代理来提高访问慢速Web服务器上公共内容时的性能。在这种配置中,通常将这些
    反向代理称为服务器加速器

  • 内容路由器

    代理服务器可以作为“内容路由器”使用,根据因特网流量状况以及内容类型将请求导向特定的Web
    服务器。
    内容路由器也可以用来实现各种服务级的请求。

  • 转码器

    代理服务器在将内容发送给客户端之前,可以修改内容的主体格式。在这些数据表示法之间进行的
    透明转换被称为转码
    转码代理可以在传输GIF图片时,将其转换成JPEG图片,以减小尺寸,也可以对图片进行压缩,同样
    也可以对文本文件进行压缩,代理甚至可以在传输文档的过程中将其转换成外语。

  • 匿名者

    匿名者代理会主动从HTTP报文中删除身份特性(比如客户端IP地址、From首部、Referer首部、
    cookie、URI的会话ID)从而提供高度的私密性和匿名性

代理会何去何从

  1. 怎样将代理部署到网络中去
  2. 怎样将代理以层级方式连接在一起
  3. 怎样先将网络流量导入到代理服务器中

代理服务器的部署

  • 出口代理
  • 访问(入口)代理
  • 反向代理

    代理通常会被部署在网络边缘,在Web服务器之前,作为代替物(反向代理),在那里他们可以处理
    所有传送给Web服务器的请求,并只在必要时向Web服务器请求资源,反向代理通常会直接冒用Web
    服务器的名字和ip地址,这样所有的请求就会被发送给代理而不是服务器了

  • 网络交换代理

代理的层次结构
可以通过代理的层次结构将代理级联起来,在代理的层次结构中,会将报文从一个代理传给另一个代
理,直到最终抵达原始服务器为止(然后通过代理传回给客户端)
代理层次结构中的路由
代理服务器可以根据众多因素,将报文转发给一个不断变化的代理服务器和原始服务器集。比如:

  • 负载均衡
  • 地理位置附近的路由
  • 协议/类型路由

    子代理可能会根据不同的URI将报文转发到不同的父代理和原始服务器上去,某些特定类型的URI
    可能会通过一些特殊的代理服务器转发请求,一遍进行特殊的协议处理。

代理如何获取流量
有四种方式可以使客户端流量流向代理

  1. 修改客户端
  2. 修改网络
  3. 修改DNS的命名空间
  4. 修改Web服务器

在浏览器输入URL后,发生了什么?

  1. 浏览器解析主机名
  2. 浏览器查询这个主机名的IP地址(DNS)
  3. 浏览器获得端口号
  4. 浏览器发起IP:port的连接
  5. 浏览器想服务器发送一条HTTP GET报文
  6. 浏览器从服务器读取HTTP响应报文
  7. 浏览器关闭连接

HTTP通信都是由TCP/IP承载的,TCP/IP是一种常用的分组交换网络分层协议集.TCP的数据是通过名为IP分组
(或IP数据报)的小数据块来发送的。HTTP(应用层)->TCP(传输层)->IP(网络层)->网络接口(数据链路层),
HTTP要传送一条报文时,会以流的形式将报文数据的内容通过一条打开的TCP连接按序传输。TCP收到数据之后
会将数据流砍成被称作段的小数据块,并将段封装在IP分组中,通过因特网传输,这些工作都是通过TCP/IP软件
处理的。
每个TCP段都是由IP分组承载,从一个IP地址发送到另一个IP地址,每个IP分组中都包括:

  1. 一个IP分组首部(通常为20字节),包含了源和目的地的IP地址,长度和其他标记
  2. 一个TCP段首部(通常为20字节),包含了TCP的端口号、TCP的控制标记,以及数据排序和完成性的数字值
  3. 一个TCP数据块(0个或多个字节)

TCP连接通过4个值识别: <源IP地址, 源端口号, 目的地IP地址, 目的地端口号>

TCP客户端和服务器是如何通过TCP套接字接口进行通信的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
客户端                                                        服务器
S1 创建新的套接字(socket)
S2 将套接字绑定到端口上去(bind)
S3 允许套接字进行连接(listen)
S4 等待连接(accept)
C1 获取IP地址和端口号
C2 创建新的套接字(socket)
C3 连接到服务器的IP:port上去(connect)
S5 通知应用程序有连接到来
S6开始读取请求
C4 连接成功
C5 发送HTTP请求(write)
C6 等待HTTP响应(read)
S7 处理HTTP请求报文
C7 处理HTTP响应 S8 回送HTTP响应(write)
C8关闭连接(close) S9 关闭连接(close)

对TCP性能的考虑

HTTP紧挨着TCP,位于其上层,所以HTTP事务的性能取决于底层TCP通道的性能。TCP网络的时延取决于
硬件速度、网络和服务器的负载、请求和响应报文的尺寸、以及客户端和服务器之间的距离。

最常见的TCP相关时延,包括:

  1. TCP连接建立握手
  2. TCP慢启动拥塞控制
  3. 数据聚集的Nagle算法
  4. 用于捎带的TCP延迟确认算法
  5. TIME_WRIT时延和端口耗尽

TCP连接的握手时延
建立一条新的TCP连接时,甚至是在发生任意数据之前,TCP软件之间会交换一系列的IP分组,对连接
的有关参数进行沟通,如果连接只是用来传送少量数据,这些交换过程就会严重降低HTTP的性能。

TCP连接握手的步骤

  1. 请求新的TCP连接时,客户端要向服务器发送一个小的TCP分组,这个分组设置了一个特殊的SYN标记,说明这是一个连接请求。
  2. 如果服务器接受了连接,就会对一些连接参数进行计算,并向客户端回送一个TCP分组,这个分组中的SYN和ACK标记都被置位,说明请求已被接受。
  3. 最后,客户端向服务器回送一个确认信息,通知它连接已成功建立,现代的TCP栈都允许客户端在这个确认分组中发送数据。

网关、隧道及中继

网关类型:服务器协议转换器、服务端安全网关、客户端安全网关以及应用程序服务器

HTTP/*: 服务端Web网关

请求流入到原始服务器,服务器Web网关会将客户端HTTP请求转换为其他协议

HTTP/HTTPS:服务器端安全网关

一个组织可以通过网关对所有的输入Web请求加密,以提供额外的隐私和安全性保护,客户端可以用
普通的HTTP浏览web内容,单网关会自动加密用户的对话

HTTPS/HTTP客户端安全加速器网关

HTTPS/HTTP网关位于Web服务器之前,通常可以作为不可见的拦截网关或者反向代理使用,他们接
收安全的HTTPS流量,对安全流量进行解密,并向Web服务器发送普通的HTTP请求。

隧道

Web隧道,这种方式可以通过HTTP应用程序访问使用非HTTP协议的应用程序。Web隧道允许用户通过
非HTTP连接发送非HTTP流量,这样就可以在HTTP上捎带其他协议数据了。使用Web隧道最常见的原因
就是要在HTTP连接中嵌入非HTTP流量,这样,这类流量就可以穿过只允许Web流量通过的防火墙了。

用CONNECT建立HTTP隧道

Web隧道是用HTTP的CONNECT方法建立起来的。CONNECT并不是HTTP/1.1核心规范的一部分,但却是
一种得到广泛应用的扩展。CONNECT方法请求隧道网关创建一条到达任意目的服务器和端口的TCP连接,
并对客户端和服务器之间的后继数据进行盲转发。通过CONNECT方法可以与使用任意协议的任意服务器
建立TCP连接。

CONNECT请求

除了起始行之外语法与其余HTTP方法类似

1
2
CONNECT host.netspace.com:443 http/1.0
User-agent: Mozilla/4.0

CONNECT响应

发送了请求之后,客户端会等待来自网关的响应,和普通HTTP报文一样,响应码200表示成功,按照
惯例,响应中的响应短语通常被设置为”Connection Established”

1
2
HTTP/1.0 200 Connection Established
Proxy-agent: Netscape-Proxy/1.1

与普通HTTP响应不同,这个响应并不需要包含Content-Type首部。此时连接只是对原始字节进行
转接,不再是报文的承载者,所以不需要使用内容类型了。

中继

HTTP中继是没有完全遵循HTTP规范的简单HTTP代理。中继负责处理HTTP中建立连接的部分,然后
对字节进行盲转发。某些简单中继实现中存在一个更常见的问题是,由于它们无法正确处理Connection
首部,所以有潜在的挂起keep-alive连接的可能。

客户端识别与cookie技术

常见的用来承载用户相关信息的HTTP首部

From: 用户的E-mail地址
User-Agent: 用户的浏览器软件
Refer: 用户是从这个页面上依照连接跳转过来的
Authorization: 用户名和密码
Client-IP(扩展请求):客户端的ip地址
X-Forwarded-For(扩展请求): 客户端的IP地址
Cookie(扩展请求): 服务器产生的ID标签

使用客户端IP地址识别用户存在的缺点

  • 客户端IP地址描述的是机器,而不是用户,多个用用户共享一台电脑,就无法区分了。
  • 很多因特网服务提供商都会在用户登录时为其动态分配IP地址。用户每次登录,都会得到一个不
    同地址。
  • 为提供安全性,并对稀缺的地址资源进行管理,很多用户都是通过网络地址转换防火墙来浏览网
    络内容。
  • HTTP代理和网关通常会打开一些新的、到原始服务器的TCP连接,Web服务器看到的将是代理服务
    器的IP地址而不是客户端的,有的代理会添加特殊的Client-IP或X-Forward-For扩展首部来保存原始
    的IP地址,但并不是所有的代理都支持这种行为。

胖URL的缺点

  • URL比较丑陋
  • 无法共享URL,胖URL中包含了与特定用户和会话有关的状态信息,如果将这个URL发送给其他人
    可能会在无意之间中将你的个人信息共享出去
  • 破坏缓存 为每个URL生成特有的版本就意味着不在有可供访问的URL需要缓存了
  • 额外的服务器负荷 服务器需要重写HTML页面使URL变胖
  • 逃逸口 用户转到其他站点或者请求一个特定的URL时,就很容易在无意中”逃离胖URL会话”
  • 在会话间是非持久的 除非用户收藏了特定的胖URL,否则用户退出登录时,所有的信息都会丢失

cookie

可以笼统的将cookie分为两类: 会话cookie和持久cookie。会话cookie是一种临时cookie,它记
录了用户访问站点时的设置和偏好。用户退出浏览器时,会话cookie就被删除了。持久cookie的生存
时间更长一些;它们存储在硬盘上,浏览器退出,计算机重启它们仍然存在。会话cookie和持久cookie
之间唯一的区别就是它们的过期时间。如果设置了Discard参数,或者没有设置Expires或Max-Age参数
来说明扩展的过期时间,这个cookie就是一个会话cookie。

cookie是如何工作的

用户首次访问Web站点时,Web服务器对用户一无所知。Web服务器希望这个用户会再次回来,所以在
响应报文中添加Set-Cookie首部。cookie中可以包含任何信息,但它们通常都只包含一个服务器为了
进行追踪而产生的独特的标识码。浏览器会记住从服务器返回的Set-Cookie或Set-Cookie2首部中的
cookie内容存储在浏览器的cookie数据库中。将来用户返回同一站点时,浏览器会挑中那个服务器
贴到用户上的那些cookie,并在一个cookie请求首部中将其传回去。

cookie罐:客户端的状态

cookie的基本思想就是让浏览器积累一组服务器特有的信息,每次访问服务器时都将这些信息提供
给他,因为浏览器要负责存储cookie信息,所以此系统被称为客户端侧状态,cookie内容的字段们:

  • domain(域)cookie的域,控制哪些站点可以看到那个cookie
  • allh 是域中所有的主机都获取cookie,还是只有指定了名字的主机获取。
  • path(路径)域中与cookie相关的路径前缀。这个属性列出的URL路径前缀下所有cookie都是有
    效的
  • secure(安全)是否只有在使用SSL连接时才发送这个cookie
  • expiration cookie的过期秒数
  • name cookie变量的名字
  • value cookie变量的值

缓存(二)

控制缓存的能力
服务器可以通过HTTP定义的几种方式来指定在文档过期之前可以将其缓存多长时间,按照优先级递
减的顺序,服务器可以:

  1. Cache-Control: no-store
  2. Cache-Control: no-cache
  3. Cache-Control: must-revalidate
  4. Cache-Control: max-age
  5. Expires
  6. 不附加任何过期信息,让缓存确定自己的过期日期

no-Stroe与no-cache首部可以防止缓存提供未经证实的已缓存对象,表示为no-store的响应会
精致缓存对响应进行复制。缓存通常会向非缓存代理服务器一样,向客户端转发一条no-store响应,
然后删除对象。标示为no-cache的响应实际上是可以存储在本地缓冲区中的

Cache-Contro:max-age首部表示从服务器将文档传过来之时起,可以认为此文档处于新鲜状态的
秒数,还有一个s-maxage首部,其行为与max-age类似,但仅用于共享缓存,服务器可以请求缓存
不要缓存文档,或者将最大使用期设置为0,从而每次访问的时候进行刷新

不推荐使用Expires首部,它指定的是实际的过期日期而不是秒数,由于很多服务器的时钟不同,或
者不正确,所以最好用剩余秒数,而不是绝对日期。

must-revalidate响应首部告诉缓存,在事先没有跟原始服务器进行再验证的情况下,不能提供这
个对象的陈旧副本。缓存仍然可以随意提供新鲜的副本。如果在缓存进行must-revalidate新鲜度
检查时,原始服务器不可用,缓存就必须返回一条504Gateway Timeout错误

如果响应中没有Cache-Control: max-age首部,也没有Expires首部,缓存可以计算出一个试探
性最大使用期

缓存(一)

再验证
缓存的新鲜度检测被称为HTTP再验证,通过If-Modified-Since首部,将这个首部添加到GET请求
中去,就可以告诉服务器,只有在缓存了对象的副本之后,又对其进行修改的情况下,才发送此对象
服务器收到GET If-Modified-Since请求时会发生的情况:

  • 再验证命中

    如果服务器对象未被修改,服务器会向客户端发送一个小的Http 304Not Modified响应

  • 再验证未命中

    如果服务器对象与已缓存副本不同,服务器向客户端发送一条普通的、带有完整内容的HTTp 200 OK响应

  • 对象被删除

    如果服务器对象已被删除了,服务器就返回一个404缓存也会将其副本删除

区分命中和未命中的情况

客户端有一种方法可以判断响应是否来自缓存,就是使用Date首部,将响应中Date首部的值与当前
时间进行比较,如果响应中的日期比较早,客户端通常就可以认为这是一条缓存响应。客户端也可以
通过Age首部来检测缓存的响应,通过这个首部可以分辨出这条响应的使用期。

缓存的处理步骤

  1. 接收

    缓存从网络中读取抵达的报文

  2. 解析

    缓存对报文进行解析,提取出URL和各种首部

  3. 查询

    缓存查看是否有本地副本可用,如果没有,就获取一份副本(并将其保存在本地)

  4. 新鲜度检测

    缓存查看已缓存副本是否足够新鲜,如果不是,就询问服务器是否有任何更新

  5. 创建响应

    缓存会用新的首部和已缓存的主题来构建一条响应报文

  6. 发送

    缓存通过网络将响应发回给客户端

  7. 日志

    缓存可选地创建一个日志文件条目来描述这个事务