Skip to content

Commit

Permalink
blog
Browse files Browse the repository at this point in the history
  • Loading branch information
jynba committed Aug 27, 2023
1 parent c18f9b6 commit f94521d
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 2 deletions.
Binary file added blogs/net/1693034257366.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions blogs/net/css_pre.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
title: HTTP协议的发展
date: 2023-8-24
tags:
- CSS
categories:
- CSS优先级
---

在 CSS 中,样式的优先级非常重要,它可以决定哪种样式最终被应用。因此,了解 CSS 样式的优先级规则非常必要。

## 什么是 CSS 优先级?

简单来说,CSS 优先级是一个用于确定相同元素上不同 CSS 规则的优先级的值。这个值的大小决定了最终应用哪些规则的样式。

## CSS 优先级计算规则

CSS 优先级计算规则如下:

1. 标签选择器(如`p``div`):1
2. 类选择器(如`.my-class`):10
3. ID 选择器(如`#my-id`):100
4. HTML 属性选择器(如`[type="text"]`):10
5. 伪类选择器(如`::before`):10
6. 伪元素选择器(如`:hover`):1
7. `!important`:无限大

在计算优先级时,只需要把相同的选择器类型的值相加,然后按照优先级高低依次比较即可。

举个例子:

```css
div p.my-class#my-id[type='text']:hover::before {
color: red !important;
}
```

这个选择器包含标签选择器(div、p)、类选择器(.my-class)、ID 选择器(#my-id)、HTML 属性选择器([type="text"])、伪类选择器(:hover)和伪元素选择器(::before),根据上面的计算规则,它的优先级为:

- 标签选择器:2(div、p 各 1)
- 类选择器:10
- ID 选择器:100
- HTML 属性选择器:10
- 伪类选择器:10
- 伪元素选择器:1
- !important:无限大

因此,这个选择器的总优先级是 133(2+10+100+10+10+1)。如果有两个优先级相同的规则,则后面的规则会覆盖前面的规则。

## CSS 优先级的实际应用

了解 CSS 优先级的计算规则之后,我们就可以合理地使用 CSS 的优先级来实现想要的效果。

例如,我们想要让某一个链接的颜色变为蓝色,而其他链接保持原来的颜色。我们可以这样写 CSS 代码:

```css
a {
color: black;
}

a.highlight {
color: blue;
}
```

这个样式表中,第一个规则中的标签选择器权值为 1,第二个规则中的类选择器权值为 10,因此第二个规则的优先级更高,它可以覆盖第一个规则的样式。

## 总结

CSS 优先级是 CSS 样式确定应用顺序的重要规则,可以根据优先级计算规则来合理地使用 CSS 样式。对 CSS 优先级的理解对 Web 开发非常重要。

总的来说,我们需要避免使用`!important`这个选择器,因为它会让代码变得不可预测。我们应该通过增加 CSS 选择器的具体性来提高它们的优先级。这样可以更好地维护代码、减少样式冲突。

### 注意点:

1. 如果有子标签就先不管父标签优先级是多大,先看子标签的 css;同级别才看 css 选择器优先级

2. div > .c1 就是 1+10=11 和 div .c1 一样是 11 (叠加)
57 changes: 57 additions & 0 deletions blogs/net/http_cache.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: HTTPA缓存机制
date: 2023-8-24
tags:
- HTTPcache
categories:
- HTTP
---

### HTTP 缓存机制

HTTP 缓存机制是一种用于在客户端和服务器之间减少数据传输的策略,它可以通过将一些资源(如文本、图像、样式表等)存储在客户端或中间代理服务器上,以便在后续请求中可以更快地获取这些资源。HTTP 缓存机制有助于提高网站性能和减少网络流量。

`Cache-Control` 是 HTTP 头部的一个字段,它可以用来指定资源的缓存策略。`Cache-Control` 头部中的不同指令可以控制缓存的行为。其中,`no-store``no-cache` 是两个常用的指令,它们有一些区别:

1. **no-store**

`Cache-Control: no-store` 指令表示不允许缓存服务器或客户端存储任何关于请求/响应的内容。这意味着每次请求都要直接与服务器进行交互,无法使用本地缓存或代理服务器缓存。这个指令适用于敏感信息,如个人身份信息或银行账户等,以确保数据不会被保存在缓存中,从而增加安全性。

2. **no-cache**

`Cache-Control: no-cache` 指令表示可以缓存内容,但在使用之前需要向服务器验证资源是否仍然有效。即使缓存中有资源的副本,客户端或代理服务器在使用之前都必须向服务器发送请求以验证资源的有效性。如果资源仍然有效,服务器将返回一个 304 Not Modified 响应,告诉客户端使用缓存的版本。这个指令适用于需要频繁验证资源的情况,以确保获取的是最新的版本。

**状态码 304 表示协商缓存**

默认情况下,浏览器的缓存策略会根据响应的 HTTP 头部信息来决定是否缓存资源以及缓存的方式。浏览器根据响应的头部信息中的 `Cache-Control``Expires``ETag` 等字段来判断是否应该缓存资源以及如何使用缓存。以下是一些常见的默认行为:

1. **Cache-Control: public**: 如果服务器在响应中设置了 `Cache-Control: public`,表示资源可以被公共缓存(包括浏览器缓存和代理服务器缓存)。这通常适用于不包含敏感信息的资源,如静态文件、图像等。
2. **Cache-Control: private**: 如果服务器在响应中设置了 `Cache-Control: private`**表示资源只能被客户端缓存,而不能被代理服务器缓存**。这适用于包含用户**个人信息的资源**,防止敏感信息被代理服务器缓存。
3. **Cache-Control: no-cache**: 如果服务器设置了 `Cache-Control: no-cache`,表示浏览器可以缓存资源,但**在使用之前需要向服务器验证资源是否仍然有效**。这会导致浏览器每次请求都会向服务器发送请求,以验证资源的有效性。
4. **Expires 头部**: 如果响应中包含了 `Expires` 头部,它指定了资源过期的日期和时间。浏览器会比较当前时间和过期时间,如果资源还未过期,浏览器就会使用缓存的版本。然而,`Expires` 头部不是很灵活,因为它依赖于服务器和客户端时间的同步。

#### 客户端缓存和服务端缓存

- **资源的动态性:** 如果资源经常变化,客户端缓存可能无法始终提供最新的内容,此时服务端缓存可能更合适。
- **用户体验:** 对于需要快速响应的用户操作,客户端缓存能够提供更快的加载速度。
- **服务器负载:** 如果服务器处理大量请求并且资源计算密集,服务端缓存可以减轻服务器负载。
- **缓存有效性管理:** 需要注意及时更新客户端和服务端缓存,确保缓存的数据始终准确。

Expires: response header 里的过期时间,浏览器再次加载资源时,如果在这个过期时间内,则命中强缓仔 Cache-Control: 当值设为 max-age=300 时,则代表在这个请求正确返回时间 (浏览器也会记录下来)的 5 分钟内再次加载资源,就会命中强缓存。
cache-control 除了该字段外,还有下面几个比较常用的设置值:
-no-cache: 不使用本地缓存。需要使用缓存协商,先与服务器确认返回的响应是否被更改,如果之前的响应中存
在 ETag,那么请求的时候会与服务端验证,如果资源未被更改,则可以避免重新下载.-no-store: 直接禁止浏览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。
-public: 可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器
-private: 只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存.
Expires:设置以分钟为单位的绝对过期时间,设置相对过期时间,max-age 指明以秒为单位的缓存时

### 浏览器缓存过程

![缓存过程](./1693034257366.png)
1 浏览器第一次加载资源,服务器返回 200,浏览器将资源文件从服务器上请求下载下来,并把 response header 及该请求的返回时间一并缓存;

2.下一次加载资源时,先比较当前时间和上一次返回 200 时的时间差,如果没有超过 cache-control 设置的 max-age,则没有过期,命中强缓存,不发请求直接从本地缓存读取该文件(如果浏览器不支持 HTTP1.1,则用 expires 判断是否过期);如果时间过期,则向服务器发送 header 带有 lf-None-Match 和 lf-Modified-Since 的请求

3.服务器收到请求后,优先根据 Etag 的值判断被请求的文件有没有做修改,Etag 值一致则没有修改,命中协商缓存,返回 304;如果不一致则有改动,直接返回新的资源文件带上新的 Etag 值并返回 200;

4.如果服务器收到的请求没有 Etag 值,则将 If-Modified-Since 和被请求文件的最后修改时间做比对商缓存,返回 304;不一致则返回新的 last-modified 和文件并返回 200;
85 changes: 85 additions & 0 deletions blogs/net/jwt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: 什么是JWT?
date: 2023-8-24
tags:
- HTTPjwt
categories:
- HTTP
---

### 什么是 JWT?

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

### 什么时候你应该用 JSON Web Token?

下列场景中使用 JSON Web Token 是很有用的:

- Authorization (授权) : 这是使用 JWT 的最常见场景。一旦用户登录,后续每个请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。**单点登录**是现在广泛使用的 JWT 的一个特性,因为它的开销很小,并且可以轻松地跨域使用。(参考学校管理系统)
- Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens 无疑是一种很好的方式。因为 JWT 可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

### JSON Web Token 的结构是什么样的

JSON Web Token 由三部分组成,它们之间用圆点(.)连接。这三部分分别是:

- Header
- Header header 典型的由两部分组成:token 的类型(“JWT”)和算法名称(比如:HMAC SHA256 或者 RSA 等等)。
- Payload
- JWT 的第二部分是 payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。
- Signature
- 签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的 token,它还可以验证 JWT 的发送方是否为它所称的发送方。

### JSON Web Tokens 是如何工作的

如果 token 是在授权头(Authorization header)中发送的,那么跨源资源共享(CORS)将不会成为问题,因为它不使用 cookie

### 基于 Token 的身份认证 与 基于服务器的身份认证

HTTP 协议是无状态的,也就是说,如果我们已经认证了一个用户,那么他下一次请求的时候,服务器不知道我是谁,我们必须再次认证

- 传统的做法是将已经认证过的用户信息存储在服务器上,比如 Session。用户下次请求的时候带着 Session ID,然后服务器以此检查用户是否认证过

这种基于服务器的身份认证方式存在一些问题:

- Sessions : 每次用户认证通过以后,服务器需要创建一条记录保存用户信息,通常是在内存中,随着认证通过的用户越来越多,服务器的在这里的开销就会越来越大。
- Scalability : 由于 Session 是在内存中的,这就带来一些扩展性的问题。
- CORS : 当我们想要扩展我们的应用,让我们的数据被多个移动设备使用时,我们必须考虑跨资源共享问题。当使用 AJAX 调用从另一个域名下获取资源时,我们可能会遇到禁止请求的问题。
- CSRF : 用户很容易受到 CSRF 攻击

- Session 是在服务器端的,而 JWT 是在客户端的。

基于 Token 的身份认证是如何工作的 基于 Token 的身份认证是无状态的,服务器或者 Session 中不会存储任何用户信息。

没有会话信息意味着应用程序可以根据需要扩展和添加更多的机器,而不必担心用户登录的位置。

虽然这一实现可能会有所不同,但其主要流程如下:

-用户携带用户名和密码请求访问 -服务器校验用户凭据 -应用提供一个 token 给客户端 -客户端存储 token,并且在随后的每一次请求中都带着它 -服务器校验 token 并返回数据

- 用 Token 的好处 - 无状态和可扩展性:Tokens 存储在客户端。完全无状态,可扩展。我们的负载均衡器可以将用户传递到任意服务器,因为在任何地方都没有状态或会话信息。 - 安全:Token 不是 Cookie
- 有助于防止 CSRF 攻击。即使在你的实现中将 token 存储到客户端的 Cookie 中,这个 Cookie 也只是一种存储机制,而非身份认证机制。
- token 在一段时间以后会过期,这个时候用户需要重新登录。

### JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是**放在 HTTP 请求的头信息`Authorization`字段里面**

- JWT 的**最大缺点**是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。

## cookie 和 token 的区别

**Cookie:**

1. **存储位置:** Cookie 是存储在客户端浏览器中的小文本文件。每当浏览器发送请求到同一域名下的服务器时,它会自动将相应的 Cookie 信息附加到请求头中。
2. **大小限制:** 单个 Cookie 的大小通常有限制,通常在几 KB 到几十 KB 之间,不同浏览器可能有不同的限制。
3. **跨域限制:** Cookie 默认情况下只能在设置它的域名下使用,跨域请求不会自动携带 Cookie。
4. **安全性:** Cookie 可以设置为 HttpOnly,防止被 JavaScript 访问,提高安全性。

**Token:**

1. **存储位置:** Token 是存储在客户端(通常是内存中)的令牌,通常以字符串形式传递在请求的头部中,或者通过查询参数等方式传递给服务器。
2. **大小限制:** Token 的大小没有明确限制,但过大的 Token 可能会导致网络传输效率下降。
3. **跨域限制:** Token 可以在跨域请求中手动传递,通常通过设置请求头的方式传递。
4. **安全性:** Token 可以采用加密算法来保证安全性,但它可能需要开发者自己来实现。Token 也可以设置过期时间,有时会自动刷新,增加安全性。
122 changes: 122 additions & 0 deletions blogs/net/keep_alive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
title: Vue中的keep-alive
date: 2023-8-24
tags:
- keep-alive
categories:
- vue
---

#### 一、概念:

keep-alive 实现了组件的缓存,当组件切换时不会对当前组件进行卸载。常用的 2 个**属性 include / exclude 以及 max 属性****2 个生命周期 activated / deactivated**,以及**LRU 算法**

include 对哪些进行缓存

exclude 对哪些不进行缓存

max 最多缓存多少个

#### 二、原理:

keep-alive 是一个组件,这个组件中有三个属性,分别是 include、exclude、max,

在 created 中创建缓存列表和缓存组件的 key 列表,

销毁的时候会做一个循环销毁清空所有的缓存和 key

当 mounted 时会监控 include 和 include 属性,进行组件的缓存处理。

如果发生变化会动态的添加和删除缓存。渲染的时候会去拿默认插槽,只缓存第一个组件,取出组件的名字,判断是否在缓存中,如果在就缓存,不在就直接 return 掉,缓存的时候,如果组件没有 key,就自己通过组件的标签、key 和 cid 拼接一个 key。如果该组件缓存过,就直接拿到组件实例。如果没有缓存过就把当前的 vnode 缓存,和 key 做一个对应关系。

这里面有一个算法叫 LRU,如果有 key 就不停的取,如果超限了就采用 LRU 进行删除最近最久未使用的,从前面删除,LRU 就是将当前使用的往数组的后面移,在最前面的就是最久未使用的。

**LRU 算法:最近最久使用算法** (维护一个最近最常使用的队列)

三、源码:

文件位置:src/core/components/keep-alive.js

```js
export default {
name: 'keep-alive',
abstract: true,

props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number],
},

created() {
this.cache = Object.create(null); // 创建缓存列表
this.keys = []; // 创建缓存组件的key列表
},

destroyed() {
for (const key in this.cache) {
// keep-alive销毁时,循环清空所有的缓存和key
pruneCacheEntry(this.cache, key, this.keys);
}
},

mounted() {
// 会监控include 和 include属性 进行组件的缓存处理
this.$watch('include', (val) => {
pruneCache(this, (name) => matches(val, name));
});
this.$watch('exclude', (val) => {
pruneCache(this, (name) => !matches(val, name));
});
},

render() {
const slot = this.$slots.default; // 会默认拿插槽
const vnode: VNode = getFirstComponentChild(slot); // 只缓存第一个组件
const componentOptions: ?VNodeComponentOptions =
vnode && vnode.componentOptions;
if (componentOptions) {
// check pattern
const name: ?string = getComponentName(componentOptions); // 取出组件的名字
const { include, exclude } = this;
if (
// 判断是否缓存
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode;
}
const { cache, keys } = this;
const key: ?string =
vnode.key == null
? // same constructor may get registered as different local components
// so cid alone is not enough (#3269)
componentOptions.Ctor.cid +
(componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key;
// 如果组件没key 就自己通过 组件的标签和key和cid 拼接一个key
if (cache[key]) {
// 如果缓存中有key
vnode.componentInstance = cache[key].componentInstance; // 直接拿到组件实例
// make current key freshest
remove(keys, key); // 删除当前的key // LRU 最近最久未使用法
keys.push(key); // 并将key放到缓存的最后面
} else {
cache[key] = vnode; // 缓存vnode
keys.push(key); // 将key 存入
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
// 缓存的太多超过了max就需要删除掉

pruneCacheEntry(cache, keys[0], keys, this._vnode); // 要删除第0个 但是现在渲染的就是第0个
}
}

vnode.data.keepAlive = true; // 并且标准keep-alive下的组件是一个缓存组件
}
return vnode || (slot && slot[0]); // 返回当前的虚拟节点
},
};
```
Loading

0 comments on commit f94521d

Please sign in to comment.