CSS 的图形（Shape）概览 - CSS：层叠样式表

CSS 的图形（Shape）概览
================

CSS Shapes Level 1 Specification 在 CSS 中描述了几何图形的 shape（以下叫做形状）。在“Level 1 of the specification”中定义的形状被设计用来绘制浮动的元素。这篇文章提供关于“你能如何绘制这些形状”的概览。

举个例子：你在左侧设计一个浮动元素，它可以令文本环绕于该浮动元素的右侧和底部，并且以一种矩形的样式。如果你接着指定一个圆形的样式，文本就会以圆形的样式环绕。.

事实上，我们有多种方式去创建这类形状，并且我们在指导中将会发现 CSS 形状是怎么工作的，然后就可以跟着感觉走了。

Specification 定义了什么？
--------------------

Specification 定义了三种新的权限：

* `shape-outside` — 允许定义基本形状。
* `shape-image-threshold` — 设定一个渗出阈值。如果一幅图像被用于定义形状，那么只有在大于等于渗出阈值的部分才会显示，其他部分不会显示出来。.
* `shape-margin` — 在形状外面加上边框。
定义基本的形状
-------

`shape-outside` 权限允许定义一个形状，它需要很多参数共同定义而成，这些参数被定义在 `<basic-shape>` 数据类型中。下面是一个小例子。

下面的例子中，左侧有一幅浮动的图像，然后使用`shape-outside` 权限定义一个`circle(50%)`属性。结果为文字环绕于图像，并且图像不显示为矩形，而显示为圆形。

正如这个级别的元素必须被浮动化才能适用`<basic-shape>` 一样，有时候在创建依赖时就会发生副作用。如果在浏览器中不支持形状，那么用户就会看到文本围绕在矩形的图片周围。有了形状支持之后，视觉效果就增强了。

### 基本形状

`circle(50%)` 属性是一个基本形状的例子。它定义了四个 `<basic-shape>` 函数，分别是：

* `inset()`
* `circle()`
* `ellipse()`
* `polygon()`

使用 `inset()` 修饰文本围绕效果时，你还可以增加偏移量，否则就会出现文本过于接近目标对象的情况。

我们已经看到了 `circle()` 如何创建圆形形状。 `ellipse()` 则用来创建椭圆形形状。如果还有特殊要求，使用 `polygon()` 可以创建任意难度的形状。

在我们的 Guide to Basic Shapes (en-US) 中，我们探索每一种形状，并且学会如何创建它们。

### 含有 Box 参数的形状

形状可以加上 Box 参数，因此你可以创建形状在：

* `border-box`
* `padding-box`
* `content-box`
* `margin-box`

当中。

在下面的例子中你可以改变 `border-box` 参数，然后查看形状靠近或者远离 Box 的效果。

详见 Shapes From Box Values (en-US)。

### 由 Image 创建的形状

有意思的是，可以使用带有 Alpha 通道的图像来创建（形状的）路径——文本将会围绕不透明的图像部分。这个特性允许文本覆盖图像，有时候可以将文本围绕于一幅不可见的图像，达到一种多边形文本显示的效果。

需要注意的是，这种方式必须是 CORS compatible 的，否则 `shape-outside` 相当于 `none` ，你将不会得到任何形状。

在下面的例子中，我们有一幅全透明的图像，使用如下 URL，并且指定 `shape-outside`. 属性，创建出一个模糊的形状：一幅气球图像。

#### `shape-image-threshold`

`shape-image-threshold` 属性用于设定图像透明度的阈值并用来创建形状。如果`shape-image-threshold` 是 `0.0` (缺省值) ，那么图像必须是全透明的。如果是 `1.0` 那么图像必须是模糊的。中间的值就代表了区分图像哪部分透明的阈值，以创建形状。

如果我们使用图像来创建形状，那么你可以看到阈值在起作用。在这么多例子中，如果你改变了阈值大小，形状会随之变化。

下面我们进入更深层次的Shapes from Images (en-US)学习。

`shape-margin` 属性
-----------------

`shape-margin` 属性在 `shape-outside`.周围加上边框。它使得文本的 Box 形状更短了，而且远离了形状本身。

在下面的例子中，我们在基本形状中加入了 `shape-margin` 属性。改变边框的宽度可以将文本的距离增大。

将创建的元素作为浮动元素
------------

在上面的例子中，我们使用图像或者可见元素创建形状，意思是你可以在网页上看到这些图形。或者，你也可以只创建图形（而使图形不可见），这就需要一个浮动元素，在浮动元素的基础上就可以使图形不可见。它可以是插入到文档中的冗余元素，可以是空的`<div>` 或者 `<span>` ，但是我们建议用创建好的内容。这就意味着我们可以保留 CSS 中的样式。

在下面的例子中，我们在创建好的内容中插入一个宽高为 150px 的内容。然后，我们可以使用基本基本形状，Box 参数甚至是 Alpha 通道去创建图形，使得文本可以环绕这个图形。

和`clip-path`的关系
---------------

基本图形和 Box 参数被用来创建图形时，和 `clip-path` 参数是等效的。所以如果你想要用图像创建形状的同时剪去部分图像，你可以用如下参数：

下面的图像是一个蓝色背景的方形图像，使用 `shape-outside: ellipse(40% 50%);` 和 `clip-path: ellipse(40% 50%);` 参数去剪去相同的区域，这个区域被定义为形状。

形状的开发工具
-------

和 CSS 形状的开发工具类似，Firefox 在 Firefox DevTools 中自带 Shape Path Editor (en-US) 开发工具。这个工具让你可以查看网页中的任意形状，甚至可以实时显示改变形状之后的效果。如果你的多边形不正确，你可以用 Shapes Editor 调节，然后复制粘贴到 CSS 中。

在 Firefox 60 当中，对于含有 `clip-path`的属性的元素默认启用 Shape Path Editor。你也可以编辑 含有 `shape-outside` 的属性的元素，但前提是开启`layout.css.shape-outside.enabled` 首选项。

未来的 CSS 形状特性
------------

形状初始化包括了一个 `shape-inside` 属性，用来在元素中创建形状。这个属性有可能在非浮动元素中创建形状，被移动到了 level 2 规则中。由于 `shape-inside` 以前属于 Level 1 规则，你或许可以在网上查到它的详细信息和属性。


font-family - CSS：层叠样式表

font-family
===========

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2016.

* Learn more
* See full compatibility
* Report feedback
**`font-family`** 属性允许我们为页面上的字体指定一个由 `@font-face` 规则定义的字体族

The **`font-family`** CSS descriptor allows authors to specify the font family for the font specified in an `@font-face` rule.

|  |  |
| --- | --- |
| Related at-rule | `@font-face` |
| 初始值 | `n/a (required)` |
| 计算值 | as specified |

语法
--

```css
/\* <string> values \*/
font-family: "font family";
font-family: "another font family";

/\* <IDENT> value \*/
font-family: examplefont;
```

### 取值

`<family-name>`

指定字体族名称

### Formal syntax
```

font-family =
  <family-name>

<family-name> =
  <string>         |
  <custom-ident>+

```

示例
--

```css
@font-face {
  font-family: examplefont;
  src: url("examplefont.ttf");
}
```

规范
--

| Specification |
| --- |
| CSS Fonts Module Level 4 # font-family-desc |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


src - CSS：层叠样式表

src
===

Baseline Widely available
-------------------------

This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2016.

* Learn more
* See full compatibility
* Report feedback
`@font-face` 中的 src 描述符指定了包含字体数据的资源。`@font-face` 规则中需要指定这个属性。

它的值是一个有优先级的，以逗号分割的外部引用或者本地安装的字体名称。当需要使用字体时，用户代理（指浏览器的意思）将会使用以逗号分隔的参考集中能成功激活的第一个参考值。如果是包含无效数据的字体或者本地的字体资源不存在，用户代理将会忽略当前字体并且加载下一个字体。

与 CSS 中的其他 URL 一样，URL 可以是相对的，在这种情况下，它相对于包含 @ font-face 规则的样式表的位置进行解析。对于 SVG 字体，URL 指向文档中定义的包含 SVG 字体的元素。如果省略元素引用，则默认引用第一个定义字体。同样，字体容器只加载`@font-face` 规则给定的其中一种字体。片段标识符用于指示要加载的字体。如果容器格式缺少定义的片段标识符方案，将会使用一个简单的基于 1 的索引方案（e.g., "font-collection#1" for the first font, "font-collection#2" for the second font, etc.）

|  |  |
| --- | --- |
| Related at-rule | `@font-face` |
| 初始值 | `n/a (required)` |
| 计算值 | as specified |

Syntax
------

```css
/\* <url> values \*/
src: url(https://somewebsite.com/path/to/font.woff); /\* Absolute URL \*/
src: url(path/to/font.woff); /\* Relative URL \*/
src: url(path/to/font.woff) format("woff"); /\* Explicit format \*/
src: url("path/to/font.woff"); /\* Quoted URL \*/
src: url(path/to/svgfont.svg#example); /\* Fragment identifying font \*/

/\* <font-face-name> values \*/
src: local(font); /\* Unquoted name \*/
src: local(some font); /\* Name containing space \*/
src: local("font"); /\* Quoted name \*/

/\* Multiple items \*/
src:
  local(font),
  url(path/to/font.svg) format("svg"),
  url(path/to/font.woff) format("woff"),
  url(path/to/font.otf) format("opentype");
```

### Values

`<url> [ format( <string># ) ]?`

Specifies an external reference consisting of a `<url>()`, followed by an optional hint using the

`format()`

function to describe the format of the font resource referenced by that URL. The format hint contains a comma-separated list of format strings that denote well-known font formats. If a user agent doesn't support the specified formats, it skips downloading the font resource. If no format hints are supplied, the font resource is always downloaded.

`<font-face-name>`

Specifies the name of a locally-installed font face using the

`local()`

function, which uniquely identifies a single font face within a larger family. The name can optionally be enclosed in quotes.

### Formal syntax
```

src =
  <font-src-list>

```

Examples
--------

```css
@font-face {
  font-family: examplefont;
  src:
    local(Example Font),
    url("examplefont.woff") format("woff"),
    url("examplefont.otf") format("opentype");
}
```

Specifications
--------------

| Specification |
| --- |
| CSS Fonts Module Level 4 # src-desc |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


font-style - CSS：层叠样式表

font-style
==========

概述
--

**font-style** 描述符允许开发者在 @font-rule 规则中为指定字体样式 (该条目区别于 font-style，意指用在 @font-rule 中的 font-style 属性)

For a particular font family, authors can download various font faces which correspond to the different styles of the same font family, and then use the `font-style` descriptor to explicitly specify the font face's style. The values for the CSS descriptor is same as that of its corresponding font property.

|  |  |
| --- | --- |
| Related at-rule | `@font-face` |
| 初始值 | `normal` |
| 计算值 | as specified |

Syntax
------

```css
font-style: normal;
font-style: italic;
font-style: oblique;
```

### Values

**`normal`**
 Selects the normal version of the font-family.

**`italic`**
 Specifies that font-face is an italicized version of the normal font .

**`oblique`**
 Specifies that the font-face is an artificially sloped version of the normal font.

### Formal syntax
```

font-style =
  auto                                      |
  normal                                    |
  italic                                    |
  oblique [ <angle [-90deg,90deg]>{1,2} ]?

```

Examples
--------

As an example, consider the garamond font family, in its normal form, we get the following result:

```css
@font-face {
  font-family: garamond;
  src: url("garamond.ttf");
}
```

![unstyled Garamond](/zh-CN/docs/Web/CSS/@font-face/font-style/garamondunstyled.jpg)

The italicized version of this text uses the same glyphs present in the unstyled version, but they are artificially sloped by a few degrees.

![artificially sloped garamond](/zh-CN/docs/Web/CSS/@font-face/font-style/garamondartificialstyle.jpg)

On the other hand, if a true italicized version of the font family exists, we can include it in the `src` descriptor and specify the font style as italic, so that it is clear that the font is italicized. True italics use different glyphs and are a bit different from their upright counterparts, having some unique features and generally have a rounded and calligraphic quality. These fonts are specially created by font designers and are **not** artificially sloped.

```css
@font-face {
  font-family: garamond;
  src: url("garamond-italic.ttf");
  font-style: italic;
}
```

![italic garamond](/zh-CN/docs/Web/CSS/@font-face/font-style/garamonditalic.jpg)

Specifications
--------------

| Specification |
| --- |
| CSS Fonts Module Level 4 # font-prop-desc |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


font-display - CSS：层叠样式表

font-display
============

`font-display` 属性决定了一个 @font-face 在不同的下载时间和可用时间下是如何展示的。

字体显示时间轴
-------

字体显示时间线基于一个计时器，该计时器在用户代理尝试使用给定下载字体的那一刻开始。时间线分为三个时间段，在这三个时间段中指定使用字体的元素的渲染行为。

字体阻塞周期

如果未加载字体，任何试图使用它的元素都必须渲染不可见的后备字体。如果在此期间字体已成功加载，则正常使用它。

字体交换周期

如果未加载字体，任何尝试使用它的元素都必须呈现后备字体。如果在此期间字体已成功加载，则正常使用它。

字体失败周期

如果未加载字体，用户代理将其视为导致正常字体回退的失败加载。

|  |  |
| --- | --- |
| Related at-rule | `@font-face` |
| 初始值 | `auto` |
| 计算值 | as specified |

语法
--

```css
/\* 关键字值 \*/
font-display: auto;
font-display: block;
font-display: swap;
font-display: fallback;
font-display: optional;
```

### 属性值

`auto`

字体显示策略由用户代理定义。

`block`

为字体提供一个短暂的阻塞周期和无限的交换周期。

`swap`

为字体提供一个非常小的阻塞周期和无限的交换周期。

`fallback`

为字体提供一个非常小的阻塞周期和短暂的交换周期。

`optional`

为字体提供一个非常小的阻塞周期，并且没有交换周期。

### 形式化语法
```

font-display =
  auto      |
  block     |
  swap      |
  fallback  |
  optional

```

例子
--

```css
@ font-face {
  font-family: ExampleFont;
  src: url（/path/to/fonts/examplefont.woff）format（'woff'），
       url（/path/to/fonts/examplefont.eot）format（'eot'）;
  font-weight: 400;
  font-style: normal;
  font-display: fallback;
}
```

规范
--

| Specification |
| --- |
| CSS Fonts Module Level 4 # font-display-desc |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `font-family`
* `font-stretch` (en-US)
* `font-style`
* `font-weight` (en-US)
* `font-variant` (en-US)
* `font-feature-settings`
* `font-variation-settings` (en-US)
* `src`
* `unicode-range` (en-US)


rotateY() - CSS：层叠样式表

rotateY()
=========

**`rotateY()`** 函数定义了一个转换，它可以让一个元素围绕纵坐标 (垂直轴) 旋转，而不会对其进行变形。它的结果是一个`<transform-function>` 数据类型。

尝试一下
----

旋转轴围绕原点旋转，而这个原点通过`transform-origin` 属性来定义。

**备注：** `rotateY(a)` 相当于 `rotate3d(0, 1, 0, a)`.

**备注：** 与二维平面上的旋转不同，三维旋转的组合顺序通常是不可交换的。换句话说，三维旋转的应用顺序，将会影响最终结果。

语法
--

**`rotateY()`** 引起的旋转量由`<angle>`指定。如果为正，则顺时针方向移动；如果为负，则逆时针方向移动。
```

rotateY(a)
```

### 参数值

`a`

`a` 是一个`<angle>` ，表示旋转的角度。正数角度表示顺时针旋转，负数则表示逆时针旋转。

| 笛卡儿坐标 ℝ2 | 齐次坐标 ℝℙ2 | 笛卡儿坐标 ℝ3 | 齐次坐标 ℝℙ3 |
| --- | --- | --- | --- |
|
 This transformation applies to the 3D space and can't be represented on
 the plane.
  |

cos(a)
0
sin(a)

0
1
0

-sin(a)
0
cos(a)

 |

cos(a)
0
sin(a)
0

0
1
0
0

-sin(a)
0
cos(a)
0

0
0
0
1

 |

示例
--

### HTML

```html
<div>Normal</div>
<div class="rotated">Rotated</div>
```

### CSS

```css
div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.rotated {
  transform: rotateY(60deg);
  background-color: pink;
}
```

### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 2 # funcdef-rotatey |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


matrix() - CSS：层叠样式表

matrix()
========

CSS 函数 `matrix()` 指定了一个由指定的 6 个值组成的 2D 变换矩阵。这种矩阵的常量值是隐含的，而不是由参数传递的；其他的参数是以列优先的顺序描述的。

`matrix(a, b, c, d, tx, ty)` 是 `matrix3d(a, b, 0, 0, c, d, 0, 0, 0, 0, 1, 0, tx, ty, 0, 1)` 的简写。

**备注：** 直到 Firefox 16, Gecko 才接受对于 **tx** 和 **ty** 的 `<length>` 值。

语法
--
```

matrix(a, b, c, d, tx, ty)
```

参数值
---

*a* *b* *c* *d*

描述线性变换的 `<number>` 。

*tx* *ty*

描述如何应用这个变换的 `<number>` 。

| ℝ2 下的笛卡尔坐标 | ℝℙ2 下齐次坐标 | ℝ3下的笛卡尔坐标 | ℝℙ3下的齐次坐标 |
| --- | --- | --- | --- |
|

a
c

b
d

 |

a c txb d ty0 0 1
  |

a c tx

b d ty0 0 1

 |

a
c
0
tx

b
d
0
ty

0
0
1
0

0
0
0
1

 |
| `[a b c d tx ty]` |

这些值表示以下函数：

matrix( scaleX(), skewY(), skewX(), scaleY(), translateX(), translateY() )

示例
--

### HTML
```

<div>Normal</div>
<div class="changed">Changed</div>
```

### CSS
```

div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.changed {
  transform: matrix(1, 2, -1, 1, 80, 80);
  background-color: pink;
}
```

### 结果

请看英文版案例 /zh-CN/docs/Web/CSS/transform-function/matrix#Result

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-matrix |


rotate() - CSS：层叠样式表

rotate()
========

CSS 的 **`rotate()`** 函数定义了一种将元素围绕一个定点（由`transform-origin`属性指定）旋转而不变形的转换。指定的角度定义了旋转的量度。若角度为正，则顺时针方向旋转，否则逆时针方向旋转。旋转 180° 也被称为点反射。

尝试一下
----

元素旋转的固定点 - 如上所述 - 也称为**变换原点**。这默认为元素的中心，但你可以使用`transform-origin`属性设置自己的自定义变换原点。

语法
--

`<angle>` 指定了 `rotate()` 的旋转程度。参数为正时，顺时针旋转；参数为负时，逆时针旋转。180° 旋转称为*点反演*。
```

rotate(a)
```

### 值

*a*

是一种`<angle>`，表示旋转的角度。正角度表示了顺时针的旋转，负角度表示逆时针的旋转。

| 笛卡尔坐标系 on ℝ2 | 齐次坐标系 on ℝℙ2 | 笛卡尔坐标系 on ℝ3 | 齐次坐标系 on ℝℙ3 |
| --- | --- | --- | --- |
|

cos(a)
-sin(a)

sin(a)
cos(a)

 |

cos(a)
-sin(a)
0

sin(a)
cos(a)
0

0
0
1

 |

cos(a)
-sin(a)
0

sin(a)
cos(a)
0

0
0
1

 |

cos(a)
-sin(a)
0
0

sin(a)
cos(a)
0
0

0
0
1
0

0
0
0
1

 |
| `[cos(a) sin(a) -sin(a) cos(a) 0 0]` |

示例
--

### HTML

```html
<div>Normal</div>
<div class="rotated">Rotated</div>
```

### CSS

```css
div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.rotated {
  transform: rotate(45deg); /\* Equal to rotateZ(45deg) \*/
  background-color: pink;
}
```

### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-rotate |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`
* `rotate3d()`


translate() - CSS：层叠样式表

translate()
===========

**`translate()`** 这个 CSS 函数在水平和/或垂直方向上重新定位元素。其结果是 `<transform-function>` 类型。

尝试一下
----

该变换由二维向量构成。它的坐标定义了元素在每个方向上移动了多少。

语法
--

```css
/\* Single <length-percentage> values \*/
transform: translate(200px);
transform: translate(50%);

/\* Double <length-percentage> values \*/
transform: translate(100px, 200px);
transform: translate(100px, 50%);
transform: translate(30%, 200px);
transform: translate(30%, 50%);
```

### 值

单个 `<length-percentage>` 作为参数

该值是 `<length>` 或者 `<percentage>` 代表翻译向量的横坐标 (horizontal, x-coordinate) . 而向量的纵坐标 (vertical, y-coordinate) 会被默认为 `0`. 例如，`translate(2)` 等价于 `translate(2, 0)` 。里面还可以填百分比值，百分比值是指 `transform-box` 属性定义的参考框的宽度。

参数是两个 `<length-percentage>` 构成

此值描述两个 `<length>` 或 `<percentage>` 值，分别代表翻译的横坐标 (x-coordinate) 和纵坐标 (y-coordinate) 向量。百分比作为第一个值表示宽度，第二个部分表示由 `transform-box` 属性定义的参考框的高度。

| Cartesian coordinates on ℝ2 | Homogeneous coordinates on ℝℙ2 | Cartesian coordinates on ℝ3 | Homogeneous coordinates on ℝℙ3 |
| --- | --- | --- | --- |
| 平移不是 ℝ2 中的线性变换，因此不能使用矩阵来表示。 |

1
 0
tx

0
 1
ty

0
0
1

 |

1
 0
tx

0
 1
ty

0
0
1

 |

1
 0
0
tx

0
 1
0
ty

0
0
1
0

0
0
0
1

 |
| `[1 0 0 1 tx ty]` |

### 形式语法

```css
translate(<length-percentage>, <length-percentage>?)
```

示例
--

### 使用单个轴平移

#### HTML

```html
<div>Static</div>
<div class="moved">Moved</div>
<div>Static</div>
```

#### CSS

```css
div {
  width: 60px;
  height: 60px;
  background-color: skyblue;
}

.moved {
  transform: translate(
    10px
  ); /\* Equal to: translateX(10px) or translate(10px, 0) \*/
  background-color: pink;
}
```

#### 结果

### y 轴和 x 轴都平移

#### HTML

```html
<div>Static</div>
<div class="moved">Moved</div>
<div>Static</div>
```

#### CSS

```css
div {
  width: 60px;
  height: 60px;
  background-color: skyblue;
}

.moved {
  transform: translate(10px, 10px);
  background-color: pink;
}
```

#### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-translate |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


perspective() - CSS：层叠样式表

perspective()
=============

透视函数定义了到 z=0 的坐标轴，使用者能够得到 3D 定位元素的透视，每个 3D 元素在 Z 轴的位置大于零意味着离使用者更近，因此会变得更大，同理当元素 Z 周小于零时会变得 ge

T 更小，变化的幅度决定于属性值的变化大小

语法
--
```

perspective(l)
```

参数
--

*l*

这个`<length>` 得到的是距离 0 坐标的距离。被用作提供一个透视渐变属性给某个元素。如果这个值是 0 或者不合法的值，将不会产生透视的变化。

| Cartesian coordinates on ℝ2 | Homogeneous coordinates on ℝℙ2 | Cartesian coordinates on ℝ3 | Homogeneous coordinates on ℝℙ3 |
| --- | --- | --- | --- |
| 这个属性变化提供了一个 3D 空间，并且不会变成平面类型 |
 A perspective is not a linear transform in ℝ3 and cannot be
 represented using a matrix in the Cartesian coordinates system.
  |

1
 0
0
0

0
 1
0
0

0
0
1
0

0
0

−1
 /d

1

 |

Examples
--------

TBD

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 2 # funcdef-perspective |


rotate3d() - CSS：层叠样式表

rotate3d()
==========

**`rotate3d()`** CSS 函数定义一个变换，它将元素围绕固定轴移动而不使其变形。运动量由指定的角度定义; 如果为正，运动将为顺时针，如果为负，则为逆时针。

尝试一下
----

在 3D 空间之中，旋转有 3 个自由维度，描述了旋转轴。旋转轴由一组 [x, y, z] 矢量定义，并且通过变换源点传递（即通过 `transform-origin` CSS 属性定义）。如果这些矢量被赋予非标准值，即 3 个坐标值的平方和不等于 1 时，它将会被内部隐式标准化。非标准矢量，例如空值和 [0, 0, 0]，将会使旋转不起作用，但是不影响整个 CSS 属性的其他效果（译者注：如 transform 中的多项变换）。

**备注：** 与平面旋转相反的是，3D 旋转的组合通常是不可交换的；这意味着定义旋转规则的值的顺序是严格控制的。

语法
--
```

rotate3d(x, y, z, a)
```

### 值

*x*

`<number>` 类型，可以是 0 到 1 之间的数值，表示旋转轴 X 坐标方向的矢量。

*y*

`<number>` 类型，可以是 0 到 1 之间的数值，表示旋转轴 Y 坐标方向的矢量。

*z*

`<number>` 类型，可以是 0 到 1 之间的数值，表示旋转轴 Z 坐标方向的矢量。

*a*

`<angle>` 类型，表示旋转角度。正的角度值表示顺时针旋转，负值表示逆时针旋转。

|  |  |
| --- | --- |
| 在ℝ2上的笛卡尔坐标 | 这种变换应用于 3D 空间，不可用于平面空间 |
| 在ℝℙ2上的齐次坐标 |
| 在ℝ3上的笛卡尔坐标 |

1
 +(1
 -cos(
 a))(

x
2

-1)

z
·sin(
 a)+
 x
y(1
 -cos(
 a))

-
y
·sin(
 a)
 +
x
z
·(1
 -cos(
 a))

-
z
·sin(
 a)
 +
x
y
·(1
 -cos(
 a))

1+(1-cos(a))(y2-1)

x
·sin(
 a)
 +
y
z
·(1
 -cos(
 a))

ysin(a) + xz(1-cos(a))
-xsin(a)+yz(1-cos(a))
1+(1-cos(a))(z2-1)
t

0
0
0
1

 |
| 在ℝℙ3上的齐次坐标 |  |

示例
--

### 绕 Y 轴旋转

#### HTML

```html
<div>Normal</div>
<div class="rotated">Rotated</div>
```

#### CSS

```css
body {
  perspective: 800px;
}

div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.rotated {
  transform: rotate3d(0, 1, 0, 60deg);
  background-color: pink;
}
```

#### 效果

### 围绕自定义轴旋转

#### HTML

```html
<div>Normal</div>
<div class="rotated">Rotated</div>
```

#### CSS

```css
body {
  perspective: 800px;
}

div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.rotated {
  transform: rotate3d(1, 2, -1, 192deg);
  background-color: pink;
}
```

#### 效果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 2 # funcdef-rotate3d |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


rotateX() - CSS：层叠样式表

rotateX()
=========

**`rotateX()`** 函数定义了一个转换，它可以让一个元素围绕横坐标 (水平轴) 旋转，而不会对其进行变形。它的结果是一个`<transform-function>`数据类型。

尝试一下
----

旋转轴围绕原点旋转，而这个原点通过`transform-origin` 属性来定义。

**备注：** `rotateX(a)` 相当于 `rotate3d(1, 0, 0, a)`.

**备注：** 与二维平面上的旋转不同，三维旋转的组合通常是不可交换的。换句话说，三维旋转的应用顺序，将会影响最终结果。

语法
--

**`rotateX()`** 引起的旋转量由`<angle>`指定。如果为正，则顺时针方向移动；如果为负，则逆时针方向移动。
```

rotateX(a)
```

### 参数值

`a`

`a` 是一个`<angle>` ，表示旋转的角度。正数角度表示顺时针旋转，负数则表示逆时针旋转。

| 笛卡儿坐标 ℝ2 | 齐次坐标 ℝℙ2 | 笛卡儿坐标 ℝ3 | 齐次坐标 ℝℙ3 |
| --- | --- | --- | --- |
|
 This transformation applies to the 3D space and can't be represented on
 the plane.
  |

1
0
0

0
cos(a)
-sin(a)

0
sin(a)
cos(a)

 |

1
0
0
0

0
cos(a)
-sin(a)
0

0
sin(a)
cos(a)
0

0
0
0
1

 |

示例
--

### HTML

```html
<div>Normal</div>
<div class="rotated">Rotated</div>
```

### CSS

```css
div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.rotated {
  transform: rotateX(45deg);
  background-color: pink;
}
```

### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 2 # funcdef-rotatex |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


scale() - CSS：层叠样式表

scale()
=======

CSS 函数 `scale()` 用于修改元素的大小。可以通过向量形式定义的缩放值来放大或缩小元素，同时可以在不同的方向设置不同的缩放值。

尝试一下
----

该变换通过一个二维向量确定在一个方向缩放的多少。如果缩放向量的两个坐标是相等的，那么所讲是均等的，或者说是各向同的，同时元素的形状是被保持的。这种情况下进行的是位似变换。

当坐标值处于区间 `[-1, 1]` 之外时，该变换将在相应的坐标方向上放大该元素，当处在区间之中时，该变换将在相应的坐标方向上缩小该元素。当值为 1 时将不进行任何处理，当为负数时，将进行*像素点反射*之后再进行大小的修改。

**备注：** `scale()` 仅适用于在欧几里德平面（二维平面）上的变换。如果需要进行空间中的缩放，必须使用 `scale3D()` 。

语法
--

```css
scale(sx)
```

或

```css
scale(sx, sy)
```

值
-

*sx*

`<number>`，表示缩放向量的横坐标。

*sy*

`<number>` ，表示缩放向量的纵坐标。如果未设置，则他的默认值被设置为 ***sx***。从而使得元素在保持原有形状下均等缩放

| ℝ2上的笛卡尔坐标变换 | ℝℙ2上的齐次坐标变换 | ℝ3上的笛卡尔坐标变换 | ℝℙ3上的齐次坐标变换 |
| --- | --- | --- | --- |
|

sx
0

0
sy

 |

sx
 0
0

0
 sy
0

0
0
1

 |

sx
 0
0

0
 sy
0

0
0
1

 |

sx
 0
0
0

0
sy
0
0

0
0
1
0

0
0
0
1

 |
| `[sx 0 0 sy 0 0]` |

示例
--

### 单一维度缩放

#### HTML

```html
<p>foo</p>
<p class="transformed">bar</p>
```

#### CSS

```css
p {
  width: 50px;
  height: 50px;
  background-color: teal;
}

.transformed {
  /\* 等同于变换：scaleX(2) scaleY(2);\*/
  transform: scale(2);
  background-color: blue;
}
```

#### 结果

### 在 X 和 Y 两个维度缩放并移动缩放中心

#### HTML

```html
<p>foo</p>
<p class="transformed">bar</p>
```

#### CSS

```css
p {
  width: 50px;
  height: 50px;
  background-color: teal;
}

.transformed {
  /\* 等同于 scaleX(2) scaleY(0.5) \*/
  transform: scale(2, 0.5);
  transform-origin: left;
  background-color: blue;
}
```

#### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-scale |


matrix3d() - CSS：层叠样式表

matrix3d()
==========

CSS 函数 **`matrix3d()`** 以 4x4 齐次矩阵的形式定义一个 3D 转换。其结果是一个 `<transform-function>` 数据类型。

语法
--

`matrix3d()` 函数由 16 个参数指定。这些参数以列为主的顺序进行描述。
```

matrix3d(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4)
```

### Values

*a1* *b1* *c1* *d1* *a2* *b2* *c2* *d2* *a3* *b3* *c3* *d3*

Are `<number>`s describing the linear transformation.

*a4* *b4* *c4 d4*

Are `<number>`s describing the translation to apply.

**备注：** Until Firefox 16, Gecko accepted a `<length>` value for *a4*, *b4* and *c4*.

| Cartesian coordinates on ℝ2 | Homogeneous coordinates on ℝℙ2 | Cartesian coordinates on ℝ3 | Homogeneous coordinates on ℝℙ3 |
| --- | --- | --- | --- |
|
 This transformation applies to the 3D space and can't be represented on
 the plane.
  |
 A generic 3D
 affine transformation
 can't be represented using a Cartesian-coordinate matrix, as
 translations are not linear transformations.
  |

a1
a2
a3
a4

b1
b2
b3
b4

c1
c2
c3
c4

d1
d2
d3
d4

 |

Matrix translation and scale example
------------------------------------

### HTML

```html
<div class="foo">
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos quaerat sit
  soluta, quisquam exercitationem delectus qui unde in facere necessitatibus aut
  quia porro dolorem nesciunt enim, at consequuntur aliquam esse?
</div>
```

### CSS

```css
html {
  width: 100%;
}
body {
  height: 100vh;
  /\* Centering content \*/
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
  align-content: center;
}
.foo {
  width: 50%;
  padding: 1em;
  color: white;
  background: #ff8c66;
  border: 2px dashed black;
  text-align: center;
  font-family: system-ui, sans-serif;
  font-size: 14px;
  /\* Setting up animation for better demonstration \*/
  animation: MotionScale 2s alternate linear infinite;
}

@keyframes MotionScale {
  from {
    /\*
 Identity matrix is used as basis here.
 The matrix below describes the
 following transformations:
 Translates every X point by -50px
 Translates every Y point by -100px
 Translates every Z point by 0
 Scales down by 10%
 \*/
    transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -50, -100, 0, 1.1);
  }
  50% {
    transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.9);
  }
  to {
    transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 50, 100, 0, 1.1);
  }
}
```

### Result

Specifications
--------------

| Specification |
| --- |
| CSS Transforms Module Level 2 # funcdef-matrix3d |

Browser compatibility
---------------------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

See also
--------

* `transform`
* `<transform-function>`
* Understanding the CSS Transforms Matrix


translate3d() - CSS：层叠样式表

translate3d()
=============

`translate3d()` CSS 函数在 3D 空间内移动一个元素的位置。这个移动由一个三维向量来表达，分别表示他在三个方向上移动的距离。

语法
--

```css
translate3d(tx, ty, tz)
```

常量
--

`tx`

是一个 `<length>` 代表移动向量的横坐标。

`ty`

是一个`<length>` 代表移动向量的纵坐标。

`tz`

是一个 `<length>` 代表移动向量的 z 坐标。它不能是`<percentage>` 值；那样的移动是没有意义的。

| Cartesian coordinates on ℝ2 | Homogeneous coordinates on ℝℙ2 | Cartesian coordinates on ℝ3 | Homogeneous coordinates on ℝℙ3 |
| --- | --- | --- | --- |
| 此变换适用于 3D 空间，并不适用于这两种情况。 | 平移不是ℝ3中的线性变换，不能使用笛卡尔坐标矩阵表示。 |

1
 0
0
tx

0
 1
0
ty

0
0
1
tz

0
0
0
1

 |

示例
--

### 单轴变化示例

#### HTML

```html
<p>foo</p>
<p class="transformed">bar</p>
<p>foo</p>
```

#### CSS

```css
p {
  width: 50px;
  height: 50px;
  background-color: teal;
}

.transformed {
  transform: perspective(500px) translate3d(10px, 0px, 0px);
  /\* equivalent to perspective(500px) translateX(10px)\*/
  background-color: blue;
}
```

#### 结果

### z 轴与 x 轴变化

#### HTML

```html
<p>foo</p>
<p class="transformed">bar</p>
<p>foo</p>
```

#### CSS

```css
p {
  width: 50px;
  height: 50px;
  background-color: teal;
}

.transformed {
  transform: perspective(500px) translate3d(10px, 0px, 100px);
  background-color: blue;
}
```

#### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 2 # funcdef-translate3d |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


rotateZ() - CSS：层叠样式表

rotateZ()
=========

**`rotateZ()`** 函数定义了一个转换，它可以让一个元素围绕横 Z 轴旋转，而不会对其进行变形。它的结果是一个`<transform-function>`数据类型。

尝试一下
----

旋转轴围绕原点旋转，而这个原点通过`transform-origin` 属性来定义。

**备注：** `rotateZ(a)` 相当于 `rotate(a)` or `rotate3d(0, 0, 1, a)`。

**备注：** 与二维平面上的旋转不同，三维旋转的组合通常是不可交换的。换句话说，三维旋转的应用顺序，将会影响最终结果。

语法
--

**`rotateZ()`** 引起的旋转量由`<angle>`指定。如果为正，则顺时针方向移动；如果为负，则逆时针方向移动。
```

rotateZ(a)
```

### 参数值

`a`

`a` 是一个`<angle>` ，表示旋转的角度。正数角度表示顺时针旋转，负数则表示逆时针旋转。

| 笛卡儿坐标 ℝ2 | 齐次坐标 ℝℙ2 | 笛卡儿坐标 ℝ3 | 齐次坐标 ℝℙ3 |
| --- | --- | --- | --- |
|
 This transformation applies to the 3D space and can't be represented on
 the plane.
  |

cos(a)
-sin(a)
0

sin(a)
cos(a)
0

0
0
1

 |

cos(a)
-sin(a)
0
0

sin(a)
cos(a)
0
0

0
0
1
0

0
0
0
1

 |

示例
--

### HTML

```html
<div>Normal</div>
<div class="rotated">Rotated</div>
```

### CSS

```css
div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.rotated {
  transform: rotateZ(45deg);
  background-color: pink;
}
```

### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 2 # funcdef-rotatez |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


translateX() - CSS：层叠样式表

translateX()
============

translateX() 函数表示在二维平面上水平方向移动元素。其结果的数据类型是`<transform-function>`。

![](/zh-CN/docs/Web/CSS/transform-function/translateX/transform-functions-translatex_2.png)

**备注：** `translateX(tx)`等同于 translate(tx, 0) 或者 translate3d(tx, 0, 0)。

语法
--

```css
translateX(t)
```

### 参数

`t`

代表了向量平移的横坐标长度`<length>`。

| ℝ2空间中的笛卡尔坐标 | 在ℝℙ2上的投影坐标 | 在ℝ3上的笛卡尔坐标 | 在ℝℙ3上的投影坐标 |
| --- | --- | --- | --- |
| 在ℝ2 空间中的平移并非线性变化，因此不能表示为笛卡尔坐标矩阵。 |

1
 0
t

0
 1
0

0
0
1

 |

1
 0
t

0
 1
0

0
0
1

 |

1
 0
0
t

0
 1
0
0

0
0
1
0

0
0
0
1

 |
| `[1 0 0 1 t 0]` |

示例
--

### HTML

```html
<div>Static</div>
<div class="moved">Moved</div>
<div>Static</div>
```

### CSS

```css
div {
  width: 60px;
  height: 60px;
  background-color: skyblue;
}

.moved {
  transform: translateX(10px); /\* 等同于 translate(10px) \*/
  background-color: pink;
}
```

### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-translatex |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


scaleX() - CSS：层叠样式表

scaleX()
========

**`scaleX()`** CSS 函数将每个元素点的横坐标修改为一个常数因子，除了该比例因子为 1，在这种情况下，函数是身份变换。缩放不是各向同性的，并且元素的角度不保守。

![](/zh-CN/docs/Web/CSS/transform-function/scaleX/scalex.png)

`scaleX(sx)` 是 `scale(sx, 1)` 或 `scale3d(sx, 1, 1)` 的一个速记/缩写。

**备注：** Note:

**`scaleX(-1)`** 定义一个 轴向对称性 (axial symmetry) ，它具有一个垂直轴通过原点 (由 `transform-origin` 属性规定)。

Syntax
------
```

scaleX(s)
```

Values
------

*s*

Is a `<number>` representing the scaling factor to apply on the abscissa of each point of the element.

| Cartesian coordinates on ℝ2 | Homogeneous coordinates on ℝℙ2 | Cartesian coordinates on ℝ3 | Homogeneous coordinates on ℝℙ3 |
| --- | --- | --- | --- |
|

s
0

0
1

 |

s
 0
0

0
 1
0

0
0
1

 |

s
 0
0

0
 1
0

0
0
1

 |

s
 0
0
0

0
 1
0
0

0
0
1
0

0
0
0
1

 |
| `[s 0 0 1 0 0]` |

Examples
--------

### Without changing the origin

#### HTML

```html
<p>foo</p>
<p class="transformed">bar</p>
```

#### CSS

```css
p {
  width: 50px;
  height: 50px;
  background-color: teal;
}

.transformed {
  transform: scaleX(2);
  background-color: blue;
}
```

#### Result

### Translating the origin of the transformation

#### HTML

```html
<p>foo</p>
<p class="transformed">bar</p>
```

#### CSS

```css
p {
  width: 50px;
  height: 50px;
  background-color: teal;
}

.transformed {
  transform: scaleX(2);
  transform-origin: left;
  background-color: blue;
}
```

#### Result

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-scalex |


translateY() - CSS：层叠样式表

translateY()
============

`translateY()` 在页面垂直移动元素，结果是 CSS 数据类型 `<transform-function>`。

![](/zh-CN/docs/Web/CSS/transform-function/translateY/translatey.png)

**备注：** `translateY(ty)` 对应 `translate(0, ty)` 或`translate3d(0, ty, 0)`。

语法
--

```css
/\* <length-percentage> values \*/
transform: translateY(200px);
transform: translateY(50%);
```

值
-

`<length>` 或 `<percentage>`

表示变换向量的纵坐标。百分比指的是盒子高度，盒子由属性 `transform-box` 定义。

| 笛卡尔坐标 ℝ2 | 在ℝℙ2上的同类坐标 | 在ℝ3上的笛卡尔坐标 | 在ℝℙ3同类坐标 |
| --- | --- | --- | --- |
| 在ℝ2 上的转换不会是线性变换但是并不能代表在笛卡尔坐标系统中使用矩阵 |

1
 0
0

0
 1
t

0
0
1

 |

1
 0
0

0
 1
t

0
0
1

 |

1
 0
0
0

0
 1
0
t

0
0
1
0

0
0
0
1

 |
| `[1 0 0 1 0 t]` |

### 语法
```

translateY(`<length-percentage>`)
```

示例
--

### HTML

```html
<div>Static</div>
<div class="moved">Moved</div>
<div>Static</div>
```

### CSS

```css
div {
  width: 60px;
  height: 60px;
  background-color: skyblue;
}

.moved {
  transform: translateY(10px);
  background-color: pink;
}
```

### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-translatey |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


skew() - CSS：层叠样式表

skew()
======

**`skew()`** 函数定义了一个元素在二维平面上的倾斜转换。它的结果是一个`<transform-function>` 数据类型

尝试一下
----

这种转换是一种剪切映射 (横切)，它在水平和垂直方向上将单元内的每个点扭曲一定的角度。每个点的坐标根据指定的角度以及到原点的距离，进行成比例的值调整；因此，一个点离原点越远，其增加的值就越大。

语法
--

`skew()` 函数指定一个或两个参数，它们表示在每个方向上应用的倾斜量。
```

skew(ax)

skew(ax, ay)
```

### 参数值

`ax`

`ax` 是一个 `<angle>`，表示用于沿横坐标扭曲元素的角度。

`ay`

`ay` 是一个 `<angle>` ，表示用于沿纵坐标扭曲元素的角度。如果未定义，则其默认值为 0，导致纯水平倾斜。

| 笛卡儿坐标 ℝ2 | 齐次坐标 ℝℙ2 | 笛卡儿坐标 ℝ3 | 齐次坐标 ℝℙ3 |
| --- | --- | --- | --- |
|

1
 tan(ax)

tan(ay)
 1

 |

1
 tan(ax)
0

tan(ay)
 1
0

0
0
1

 |

1
 tan(ax)
0

tan(ay)
 1
0

0
0
1

 |

1
 tan(ax)
0
0

tan(ay)
 1
0
0

0
0
1
0

0
0
0
1

 |
| `[1 tan(ay) tan(ax) 1 0 0]` |

示例
--

### 使用单个参数

#### HTML

```html
<div>Normal</div>
<div class="skewed">Skewed</div>
```

#### CSS

```css
div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.skewed {
  transform: skew(10deg); /\* Equal to skewX(10deg) \*/
  background-color: pink;
}
```

#### 结果

### 使用两个参数

#### HTML

```html
<div>Normal</div>
<div class="skewed">Skewed</div>
```

#### CSS

```css
div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.skewed {
  transform: skew(10deg, 10deg);
  background-color: pink;
}
```

#### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-skew |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


skewX() - CSS：层叠样式表

skewX()
=======

**`skewX()`** 函数定义了一个转换，该转换将元素倾斜到二维平面上的水平方向。它的结果是一个`<transform-function>`数据类型。

尝试一下
----

这种转换是一种剪切映射 (横切)，它在水平和垂直方向上将单元内的每个点扭曲一定的角度。每个点的坐标根据指定的角度以及到原点的距离，进行成比例的值调整；因此，一个点离原点越远，其增加的值就越大。

**备注：** `skewX(a)` 相当于 `skew(a)`。

语法
--

```css
skewX(a)
```

### 参数值

`a`

`a` 是一个 `<angle>`，表示用于沿横坐标扭曲元素的角度。

| 笛卡儿坐标 ℝ2 | 齐次坐标 ℝℙ2 | 笛卡儿坐标 ℝ3 | 齐次坐标 ℝℙ3 |
| --- | --- | --- | --- |
|

1
 tan(ay)

0
 1

 |

1
 tan(ay)
0

0
 1
0

0
0
1

 |

1
 tan(ay)
0

0
 1
0

0
0
1

 |

1
 tan(ay)
0
0

0
 1
0
0

0
0
1
0

0
0
0
1

 |
| `[1 0 tan(a) 1 0 0]` |

示例
--

### HTML

```html
<div>Normal</div>
<div class="skewed">Skewed</div>
```

### CSS

```css
div {
  width: 80px;
  height: 80px;
  background-color: skyblue;
}

.skewed {
  transform: skewX(10deg); /\* Equal to skew(10deg) \*/
  background-color: pink;
}
```

### 结果

规范
--

| Specification |
| --- |
| CSS Transforms Module Level 1 # funcdef-transform-skewx |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `transform`
* `<transform-function>`


区块格式化上下文 - Web 开发者指南

区块格式化上下文
========

**区块格式化上下文**（Block Formatting Context，BFC）是 Web 页面的可视 CSS 渲染的一部分，是块级盒子的布局过程发生的区域，也是浮动元素与其他元素交互的区域。

下列方式会创建块格式化上下文：

* 文档的根元素（`<html>`）。
* 浮动元素（即 `float` 值不为 `none` 的元素）。
* 绝对定位元素（`position` 值为 `absolute` 或 `fixed` 的元素）。
* 行内块元素（`display` 值为 `inline-block` 的元素）。
* 表格单元格（`display` 值为 `table-cell`，HTML 表格单元格默认值）。
* 表格标题（`display` 值为 `table-caption`，HTML 表格标题默认值）。
* 匿名表格单元格元素（`display` 值为 `table`（HTML 表格默认值）、`table-row`（表格行默认值）、`table-row-group`（表格体默认值）、`table-header-group`（表格头部默认值）、`table-footer-group`（表格尾部默认值）或 `inline-table`）。
* `overflow` 值不为 `visible` 或 `clip` 的块级元素。
* `display` 值为 `flow-root` 的元素。
* `contain` 值为 `layout`、`content` 或 `paint` 的元素。
* 弹性元素（`display` 值为 `flex` 或 `inline-flex` 元素的直接子元素），如果它们本身既不是弹性、网格也不是表格容器。
* 网格元素（`display` 值为 `grid` 或 `inline-grid` 元素的直接子元素），如果它们本身既不是弹性、网格也不是表格容器。
* 多列容器（`column-count` 或 `column-width` (en-US) 值不为 `auto`，且含有 `column-count: 1` 的元素）。
* `column-span` 值为 `all` 的元素始终会创建一个新的格式化上下文，即使该元素没有包裹在一个多列容器中（规范变更、Chrome bug）

格式化上下文影响布局，通常，我们会为定位和清除浮动创建新的 BFC，而不是更改布局，因为它将：

* 包含内部浮动。
* 排除外部浮动。
* 阻止外边距重叠。

**备注：** 弹性/网格容器（`display`：flex/grid/inline-flex/inline-grid）建立新的弹性/网格格式化上下文，除布局之外，它与区块格式化上下文类似。弹性/网格容器中没有可用的浮动子级，但排除外部浮动和阻止外边距重叠仍然有效。

示例
--

### 包含内部浮动

BFC 使得让浮动内容和周围的内容等高。

为了更好的理解 BFC，我们先看看下面这些内容。

在下面的例子中，我们让 `<div>` 元素浮动，并给它应用 `border` 效果。`<div>` 里的内容现在已经在浮动元素周围浮动起来了。由于浮动的元素比它旁边的元素高，所以 `<div>` 的边框穿出了浮动。正如我们在流内与流外指南中解释的，浮动脱离了文档流，所以 `<div>` 的 `background` 和 `border` 仅仅包含了内容，不包含浮动。

**使用 `overflow: auto`**

在创建包含浮动元素的 BFC 时，通常的做法是设置父元素 `overflow: auto` 或者其他除默认的 `overflow: visible` 以外的值。`<div>` 元素变成布局中的小型布局，任何子元素都会被包含进去。

使用 `overflow` 创建新的 BFC，是因为 `overflow` 属性会告诉浏览器应该怎样处理溢出的内容。如果使用它仅仅为了创建 BFC，你可能会遇到不希望出现的滚动条或阴影，需要注意。另外，对于后续的开发者，可能不清楚当时为什么使用 `overflow`，所以最好添加一些注释来解释为什么这样做。

**使用 `display: flow-root`**

一个新的 `display` 属性的值，它可以创建无副作用的 BFC。在父级块中使用 `display: flow-root` 可以创建新的 BFC。

给 `<div>` 元素设置 `display: flow-root` 属性后，`<div>` 中的所有内容都会参与 BFC，浮动的内容不会从底部溢出。

你可以从 `flow-root` 这个值的名字上看出来，它创建一个新的用于流式布局的上下文，行为如同 `root`（在浏览器中是 `<html>`）元素。

#### HTML

```html
<section>
  <div class="box">
    <div class="float">我是浮动的盒子！</div>
    <p>我是容器内的内容。</p>
  </div>
</section>
<section>
  <div class="box" style="overflow:auto">
    <div class="float">我是浮动的盒子！</div>
    <p>我是 <code>overflow:auto</code> 容器内部的内容。</p>
  </div>
</section>
<section>
  <div class="box" style="display:flow-root">
    <div class="float">我是浮动的盒子！</div>
    <p>我是 <code>display:flow-root</code> 容器内部的内容。</p>
  </div>
</section>
```

#### CSS

```css
section {
  height: 150px;
}
.box {
  background-color: rgb(224, 206, 247);
  border: 5px solid rebeccapurple;
}
.box[style] {
  background-color: aliceblue;
  border: 5px solid steelblue;
}
.float {
  float: left;
  width: 200px;
  height: 100px;
  background-color: rgba(255, 255, 255, 0.5);
  border: 1px solid black;
  padding: 10px;
}
```

### 排除外部浮动

下面的例子中，我们使用 `display: flow-root` 和浮动实现双列布局，因为正常文档流中建立的 BFC 不得与元素本身所在的块格式化上下文中的任何浮动的外边距重叠。

#### HTML

```html
<section>
  <div class="float">试试重新调整这个外部浮动元素的大小</div>
  <div class="box"><p>普通</p></div>
</section>
<section>
  <div class="float">试试重新调整这个外部浮动元素的大小</div>
  <div class="box" style="display:flow-root">
    <p><code>display:flow-root</code></p>
    <p></p>
  </div>
</section>
```

#### CSS

```css
section {
  height: 150px;
}
.box {
  background-color: rgb(224, 206, 247);
  border: 5px solid rebeccapurple;
}
.box[style] {
  background-color: aliceblue;
  border: 5px solid steelblue;
}
.float {
  float: left;
  overflow: hidden; /\* resize:both 所必需的样式 \*/
  resize: both;
  margin-right: 25px;
  width: 200px;
  height: 100px;
  background-color: rgba(255, 255, 255, 0.75);
  border: 1px solid black;
  padding: 10px;
}
```

与 `inline-block` 需要设置 `width: <percentage>` 不同的是，在示例中，我们不需要设置右侧 `div` 元素的宽度。

请注意，弹性盒子是在现代 CSS 中实现多列布局的更有效的方法。

### 防止外边距重叠

你可以创建新的 BFC 避免两个相邻元素之间的外边距重叠。

#### 外边距重叠示例

在这个示例中我们有两个相邻的 `<div>` 元素，每个元素在垂直方向上含有 `10px` 的外边距。由于外边距重叠作用，垂直方向上它们之间将具有 10 像素的间距，而不是所期望的 20 像素。

```html
<div class="blue"></div>
<div class="red"></div>
```

```css
.blue,
.red {
  height: 50px;
  margin: 10px 0;
}

.blue {
  background: blue;
}

.red {
  background: red;
}
```

#### 防止外边距重叠

在这个示例中，我们将第二个 `<div>` 包裹在另外一个 `<div>` 之中，以创建一个新的 BFC，防止外边距重叠。

```html
<div class="blue"></div>
<div class="outer">
  <div class="red"></div>
</div>
```

```css
.blue,
.red {
  height: 50px;
  margin: 10px 0;
}

.blue {
  background: blue;
}

.red {
  background: red;
}

.outer {
  overflow: hidden;
  background: transparent;
}
```

规范
--

| Specification |
| --- |
| CSS Display Module Level 3 # block-formatting-context |

参见
--

* `float`、`clear`
* CSS 重要概念：
    + CSS 语法
    + @ 规则
    + 注释
    + 优先级
    + 继承
    + 盒模型
    + 布局模式
    + 视觉格式化模型
    + 外边距合并
    + 值
        - 初始值
        - 计算值
        - 应用值
        - 实际值
    + 属性值定义语法
    + 简写属性
    + 可替换元素


XML 简介 - XML (可扩展标记语言)

XML 简介
======

XML（Extensible Markup Language）是一种类似于 HTML，但是没有使用预定义标记的语言。因此，可以根据自己的设计需求定义专属的标记。这是一种强大将数据存储在一个可以存储、搜索和共享的格式中的方法。最重要的是，因为 XML 的基本格式是标准化的，如果你在本地或互联网上跨系统或平台共享或传输 XML，由于标准化的 XML 语法，接收者仍然可以解析数据。

有许多基于 XML 的语言，包括 XHTML、MathML、SVG、RSS 和 RDF (en-US)。你也可以创建自己的。

构建 XML 文档
---------

XML 和基于 XML 的语言的整个结构是建立在标签之上的。

### XML 声明

XML——声明并非是一种标签，其用于传输文档的元数据。

```html
<?xml version="1.0" encoding="UTF-8"?>
```

#### 属性

`version`

当前文档使用的版本。

`encoding`

当前文档使用的编码。

### 注释

```html
<!-- Comment -->
```

“标准的”XML（有效且格式良好）
-----------------

### 设计规则的标准

判定一个 XML 文档正确的标准是：

* 文档必须是一个格式良好的文档。
* 文档遵循 XML 所有的语法规则并且有效。
* 文档遵循特定语义的规则，这些规则通常规定在 XML 或 DTD 规范中（**文档类型定义**）。
### 示例

```xml
<?xml version="1.0" encoding="UTF-8"?>
<message>
    <warning>
        Hello World
    <!--missing </warning> -->
</message>
```

现在让我们看看同一文档的更正版本：

```xml
<?xml version="1.0" encoding="UTF-8"?>
<message>
    <warning>
         Hello World
    </warning>
</message>
```

一个包含未定义标签的文档是无效的。例如，如果我们没有定义 `<warning>` 标签，这个文件将是无效的。

大多数浏览器都提供了可以识别格式不正确的 XML 文档的调试器。

实体
--

像 HTML 一样，XML 为一些特别预留的符号定义了一些方法，称为实体（entities），例如用于标记的大于号。下面是五个你必须知道的符号：

| 实体 | 符号 | 描述 |
| --- | --- | --- |
| < | < | 小于符号 |
| > | > | 大于符号 |
| & | & | 和 |
| " | " | 一个双引号 |
| ' | ' | 一个单引号 |

这里只有五个声明的实体，更多的方法可以借助文档类型定义 (en-US)引入。例如，为了创建一个 `&warning;` 实体，你可以这样做：

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE body [
 <!ENTITY warning "Warning: Something bad happened... please refresh and try again.">
]>
<body>
  <message> &warning; </message>
</body>
```

你可以用数字和一些符号去表示一些特殊字符；例如，© 代表符号“©”。

展示 XML
------

XML 通常被用作描述，但是还有很多方法去展示 XML 数据。如果你没有定义让 XML 展示的特别方式，那么原始的 XML 会被展现在浏览器上。

一个展示 XML 输出的方法是将 CSS 应用于文档，用 `xml-stylesheet` 去处理指令。
```

<?xml-stylesheet type="text/css" href="stylesheet.css"?>
```

当然，也有其他更有效的方法去展示 XML，**Extensible Stylesheet Language Transformations**（XSLT）可以将 XML 转换为其他语言（例如 HTML）。这使得 XML 非常地有力。

```xml
<?xml-stylesheet type="text/xsl" href="transform.xsl"?>
```

推荐
--

这篇文章明显是一篇非常简略的 XML 介绍，其中有一些例子和参考来帮助你入门。关于 XML 的更多细节，你可以在 Web 中查找更深入的文章。

学习超文本标记语言（HTML）将帮助你更好的理解 XML。

参见
--

* XML.com
* 扩展标记语言（XML）@ W3.org
* 使用 XML: A List Apart


解析或序列化 XML - XML (可扩展标记语言)

解析或序列化 XML
==========

有时，你可能需要解析 XML 内容，并把它转换为 DOM 树，或者，反过来将现有的 DOM 树序列化成 XML。在本文中，我们将了解 web 平台提供的对象，以便轻松地完成序列化和解析 XML 的常见任务。

`XMLSerializer`

序列化 DOM 树，把它们转换成包含 XML 的字符串。

`DOMParser`

通过解析一个包含 XML 的字符串来构建 DOM 树，返回一个基于输入数据的适当的 `XMLDocument` 或者是 `Document`。

`XMLHttpRequest`

从一个 URL 中加载内容；XML 的内容被作为一个带有内建 XML 的 DOM 树的 XML `Document` 对象返回。

XPath

一种创建包含 XML 文档特定部分的地址，并基于这些地址进行 XML 节点定位的字符串的技术。

创建一个 XML 文档
-----------

使用以下方法来创建一个 XML 文档，该文档是 `Document` 的一个实例。

### 把字符串解析成 DOM 树

下面这个例子使用 `DOMParser` 把字符串中的 XML 片段转换为 DOM 树：

```js
const xmlStr = '<q id="a"><span id="b">hey!</span></q>';
const parser = new DOMParser();
const doc = parser.parseFromString(xmlStr, "application/xml");
// 打印根元素的名称或错误信息
const errorNode = doc.querySelector("parsererror");
if (errorNode) {
  console.log("error while parsing");
} else {
  console.log(doc.documentElement.nodeName);
}
```

### 把可寻址的 URL 资源解析成 DOM 树

#### 使用 XMLHttpRequest

下列示例代码读取一个可寻址的 URL 资源文件，并将其解析成 DOM 树：

```js
const xhr = new XMLHttpRequest();

xhr.onload = () => {
  dump(xhr.responseXML.documentElement.nodeName);
};

xhr.onerror = () => {
  dump("Error while getting XML.");
};

xhr.open("GET", "example.xml");
xhr.responseType = "document";
xhr.send();
```

在 `xhr` 对象 `responseXML` 字段中返回的值是一个通过解析 XML 构造的 `Document`。

如果这个文档是 HTML，上面实例代码将会返回一个 `Document`。如果它是 XML，那获取的结果对象实际上是一个 `XMLDocument`。这两种类型实质上是一样的，不同点大部分是历史遗留的，尽管区分它们也会有一些实际好处。

**备注：** 事实上，`HTMLDocument` 也是一个接口，但是它不必是一个独立的类型。在一些浏览器上它是，但在另外一些浏览器上它仅仅是 `Document` 接口的别名。

序列化一个 XML 文档
------------

给定一个 `Document`，你可以使用 `XMLSerializer.serializeToString()` (en-US) 方法把文档的 DOM 树序列化为 XML。

使用下面的方法来序列化在先前章节中创建的 XML 文档内容。

### 把 DOM 树序列化成字符串

首先，使用如何创建一个 DOM 树中的方法构建一个 DOM 树。也可以使用从 `XMLHttpRequest` 中获得的 DOM 树。

为了序列化 DOM 树的 `doc` 为 XML 文档，调用 `XMLSerializer.serializeToString()` (en-US)：

```js
const serializer = new XMLSerializer();
const xmlStr = serializer.serializeToString(doc);
```

### 序列化 HTML 文档

如果你手上的 DOM 是一个 HTML 文档，你可以使用 `serializeToString()` 将其序列化；但是也有一个更简单的选择：直接用 `Element.innerHTML` 属性（如果你仅仅想得到指定节点的后代的话）或 `Element.outerHTML` 属性（如果你想得到节点本身及它所有的后代的话）。

```js
const docInnerHtml = document.documentElement.innerHTML;
```

因此，`docInnerHtmlL` 是一个包含 HTML 内容的文档的字符串，换句话来说，也是 `<body>` 元素的内容。

你可以使用以下代码得到 `<body>` *和*它的后代对应的 HTML：

```js
const docOuterHtml = document.documentElement.outerHTML;
```

参见
--

* XPath
* `XMLHttpRequest`
* `Document`、`XMLDocument` 和 `HTMLDocument`


正则表达式（regexp）

正则表达式（regexp）
=============

**XSLT/XPath Reference**: XSLT elements, EXSLT functions, XPath functions, XPath axes

EXSLT 正则表达式包使用 JavaScript 风格的正则表达式来提供测试、匹配和替换文本的功能。该正则表达式包的命名空间是 `http://exslt.org/regular-expressions`。


background_color - Web App Manifest

background\_color
=================

**实验性:** **这是一项实验性技术**
在将其用于生产之前，请仔细检查浏览器兼容性表格。

|  |  |
| --- | --- |
| 类型 | `String` |
| 强制 | 否 |

`background_color` 成员为应用程序页面定义了一个占位背景颜色，以便在其样式表加载之前显示。当清单在样式表加载前可用时，用户代理会使用这个值来绘制快捷方式的背景颜色。

因此，`background_color` 应与 `background-color` 网站样式表中的 CSS 属性匹配，以在启动 Web 应用程序和加载网站内容之间进行平滑过渡。

**备注：** 该 `background_color` 成员仅用于在从网络或存储介质加载主样式表时改善用户体验。当渐进式 Web 应用程序样式表可用时，用户代理不会将 `background-color` 用作 CSS 属性。

实例
--

```json
"background\_color": "red"
```

规范
--

| Specification |
| --- |
| Web Application Manifest # background\_color-member |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.


HTTP 客户端提示（Clinet Hint） - HTTP

HTTP 客户端提示（Clinet Hint）
=======================

**客户端提示**是一组 HTTP 请求标头字段，服务器可以主动地用它来获取关于设备、网络、用户以及用户代理指定的首选项的信息。然后，服务器可以根据客户端选择提供的信息来确定发送哪些资源。

“提示”（hint）标头集在 HTTP 标头话题中列出并总结如下。

概述
--

服务器必须宣称它支持客户端提示，使用 `Accept-CH` 标头去表明它乐意去接收提示。当支持客户端提示的客户端接收到 `Accept-CH` 标头时，它可以选择在它的后续请求中附加部分或者列出的全部客户端提示标头。

例如，在以下发出带有 `Accept-CH` 标头的响应之后，客户端提示可能会在所有后续的请求中附加 `Width` (en-US)、`Downlink` (en-US) 和 `Sec-CH-UA` 标头。

```http
Accept-CH: Width, Downlink, Sec-CH-UA
```

这种方法是有效的，因为服务器仅能请求它能有效处理的信息。它也是相对于“隐私保护的”，因为这是由客户端决定它可以安全地共享哪些信息。

有一小部分低熵客户端提示标头，即便没有请求，也可以由客户端发送。

**备注：** 也可以在 HTML 中使用带有 `http-equiv` 属性的 `<meta>` 元素指定客户端提示。

```html
<meta http-equiv="Accept-CH" content="Width, Downlink, Sec-CH-UA" />
```

缓存和客户端提示
--------

决定在响应中发送哪些资源的客户端提示，通常也应被包含在受影响响应的 `Vary` 标头中。这确保为提示标头的每个不同值缓存不同的资源。

```http
Vary: Accept, Width, ECT
```

对于值变化很大的客户端提示标头时，最好不要指定 `Vary` 或者使用其他的策略，因为这会使资源无法有效缓存。（为每个唯一值创建新的缓存条目。）这尤其适用于网络客户端提示，像 `Downlink` (en-US) 和 `RTT` (en-US)。更多信息请参见 HTTP 缓存 > Vary 响应。

提示（hint）的生命周期
-------------

服务器指定它在 `Accept-Ch` 响应标头中感兴趣的客户端提示标头。用户代理将请求的客户端提示标头，或者至少它想与服务器共享的部分客户端提示附加到当前浏览器会话中的所有后续请求。

换句话说，对一组特定提示的请求，在浏览器关闭之前都不会过期。

服务器可以通过重新发送带有新列表的 `Accept-CH` 响应标头，来替换它感兴趣接收的客户端提示集。例如，要停止请求任何提示，它将发送空列表的 `Accept-CH`。

低熵提示
----

客户端提示大致分为高熵提示和低熵提示。

低熵提示是那些不会泄漏太多可用于为用户创建指纹识别 (en-US)信息的提示。默认情况下，根据权限策略，它们可能为每个客户端请求发送，这与服务器 `Accept-CH` 响应标头无关。这些提示包括：`Save-Data`、`Sec-CH-UA`、`Sec-CH-UA-Mobile` (en-US)、`Sec-CH-UA-Platform` (en-US)。

高熵提示是那些可能泄漏太多可用于用户指纹识别信息的提示，因此使用这样一种方式进行控制，即用户代理可以决定是否提供这些信息。该决定可能基于用户首选项、权限请求或者权限策略。所有不是低熵提示的客户端提示都是高熵提示。

重要客户端提示
-------

*重要客户端提示*是那些应用响应可能显著改变渲染的页面的提示，可能会以一种不和谐或者会影响可用性的方式出现，因此必须在渲染页面之前应用。例如，`Sec-CH-Prefers-Reduced-Motion` 通常被视作重要提示，因为它可能会显著影响动画的行为，并且也因为选择此首选项的用户需要去设置它。

服务器可以使用 `Critical-CH` (en-US) 响应标头和 `Accept-CH` 去指定已接受的客户端提示也是重要客户端提示（`Critical-CH` 中的标头也必须出现在 `Accept-CH`）。接收到有着 `Critical-CH` 响应的用户代理必须检查指示的标头是否已经在源请求中发送。即使未包含在第一个请求中，或者服务器配置发生改变，这种方法都可以确保始终使用重要客户端提示设置客户端首选项。

例如，在这种情况下，服务器通过 `Accept-CH` 告诉客户端，它接受 `Sec-CH-Prefers-Reduced-Motion`，而 `Critical-CH` (en-US) 用于指定 `Sec-CH-Prefers-Reduced-Motion`，这被视为一个重要客户端提示：

```http
HTTP/1.1 200 OK
Content-Type: text/html
Accept-CH: Sec-CH-Prefers-Reduced-Motion
Vary: Sec-CH-Prefers-Reduced-Motion
Critical-CH: Sec-CH-Prefers-Reduced-Motion
```

**备注：** 我们还在 `Vary` 标头中指定了 `Sec-CH-Prefers-Reduced-Motion`，以向浏览器表明，即使 URL 保持相同，只要基于此标头的值不同，提供的值也不同，所以浏览器不应该使用已存在的缓存响应，而是应该单独地缓存这个响应。在 `Accept-CH` 中列出的标头也应该出现在 `Accept-CH` 和 `Vary` 标头中。

由于 `Sec-CH-Prefers-Reduced-Motion` 是源请求中的不存在的重要提示，因此客户端会自动地重试请求——这次通过 `Sec-CH-Prefers-Reduced-Motion` 告诉服务器它有一个用户首选项要减少动画。

```http
GET / HTTP/1.1
Host: example.com
Sec-CH-Prefers-Reduced-Motion: "reduce"
```

提示类型
----

### 用户代理客户端提示

用户代理（UA）标头允许服务器允许服务器基于用户代理（浏览器）、操作系统以及设备去改变响应。标头包括：`Sec-CH-UA`、`Sec-CH-UA-Arch`、`Sec-CH-UA-Bitness`、`Sec-CH-UA-Full-Version-List`、`Sec-CH-UA-Full-Version`、`Sec-CH-UA-Mobile` (en-US)、`Sec-CH-UA-Model` (en-US)、`Sec-CH-UA-Platform` (en-US) 和 `Sec-CH-UA-Platform-Version` (en-US)。

用户代理客户端提示 API (en-US) 可以在网页的 JavaScript 中使用客户端提示。

**备注：** 服务器当前通过解析 `User-Agent` 标头获得大部分相同的信息。由于历史原因，该标头包含许多不相关的信息，以及用于识别*特定用户*的信息。UA 客户端提示提供更有效的和隐私保护的方式来获取所需要的信息。它们最终有望取代这种旧的方式。

### 用户首选项媒体特性客户端提示

用户首选项媒体特性客户端提示允许服务器基于用户代理对 CSS 媒体特性（例如配色方案或者减少动态效果）的首选项来改变响应。标头包括：`Sec-CH-Prefers-Reduced-Motion` (en-US)、`Sec-CH-Prefers-Color-Scheme` (en-US)。

### 设备客户端提示

设备客户端提示允许服务器基于设备型号（包括可用内存和屏幕属性）来改变响应。标头包括：`Device-Memory`、`DPR`、`Width` (en-US)、`Viewport-Width` (en-US)。

### 网络客户端提示

网络客户端提示允许服务器基于用户的选择、网络带宽和传输延迟来改变响应。标头包括：`Save-Data`、`Downlink` (en-US)、`ECT` (en-US)、`RTT` (en-US)。

参见
--

* 客户端提示标头
* `Vary` HTTP 标头
* 客户端提示的基础架构
* 用户代理客户端提示 API (en-US)
* 使用用户代理客户端提示改善用户隐私和开发人员体验（web.dev）


权限策略 - HTTP

权限策略
====

**权限策略**为网站开发人员提供了明确声明哪些特性可以或不可以在网站上使用的机制。定义一组“策略”，限制网站代码可以访问哪些 API，或者修改浏览器对某些特性的默认行为。这使你能够执行最佳做法（即使是在代码库的发展过程中），也可以更安全地编排第三方内容。

权限策略与内容安全策略相似，但是它控制的是特性而非安全行为。

使用权限策略可以做什么：

* 改变手机和第三方视频自动播放的默认行为。
* 限制网站使用相机、麦克风、扬声器等敏感设备。
* 允许 iframe 使用全屏 API。
* 如果项目在视口中不可见，则停止对其进行脚本处理，以提高性能。

**备注：** 权限策略曾经被称为特性策略（Feature Policy）。名字已经改变，HTTP 头的语法也发生了变化，如果你过去使用过特性策略，请记住这一点，并检查浏览器的支持表。`<iframe allow=" ... ">` 的语法是保持不变的。

概念和用法
-----

网络提供的特性和 API，如果被滥用，可能会有隐私或安全风险。在这种情况下，你可能希望严格限制特性在网站上的使用方式。在每一种情况下，都应该有一种直观的或非破坏性的方法，让网络开发者检测和处理禁用某项特性的情况。

一些方法包括：

* 对于需要用户权限授予的 JavaScript API，会返回“权限拒绝”。
* 提供某些特性的 JavaScript API 返回 `false` 值或抛出一个错误。
* API 甚至没有对外暴露，好像它们不存在一样。
* 控制特性行为的选项有不同的默认值。

**备注：** 新引入的特性可能有一个明确的 API 来提示状态。后来与权限策略整合的现有特性通常会使用现有机制。

权限策略允许你控制哪些源可以使用哪些特性，无论是在顶层页面还是在嵌入的 `<iframe>` 中。其目的是为良好的用户体验执行最佳实践，并对*敏感*或*强大*的特性（指在执行相关代码之前需要用户明确许可使用的特性）提供细化控制。

权限策略提供两种指定策略的方法：

* `Permissions-Policy` HTTP 标头，控制收到的响应和页面内任何嵌入的内容（包括 `<iframe>`）。
* `<iframe>` 的 `allow` 属性，控制在特定 `<iframe>` 中使用的特性。

这些是独立的，但又是相关的——详见嵌入式内容的继承策略。

**备注：** 脚本可以通过位于 `FeaturePolicy` (en-US) 对象的 `Document.featurePolicy` (en-US) 或 `HTMLIFrameElement.featurePolicy` (en-US) 以编程方式查询权限策略信息。

为了控制每个特性，会撰写包含以下这些方面的策略：

* **指令**确定了要控制的特性名称。见不同的可用指令列表。
* **允许列表**是该特性应该被控制的源列表。你可以为所有或特定的源启用一种特性，或者阻止它在所有源中的使用。

请看下面的多个示例。

与权限 API 的关系
-----------

权限策略和权限 API (en-US)密切相关，但又有所不同。由这两种技术控制其权限的特性是重叠的。

* 权限策略允许服务器设置某项特性是否可以在特定的文档中使用（或在文档中嵌入`<frame>`）。这些被称为**策略控制的**特性————见权限策略指令列表。
* 权限 API 根据用户授予的权限对特性的访问进行把关。这些特性被记录在权限注册表中。

每种特性使用的识别字符串在两者之间保持一致，例如，`geolocation` 代表地理位置 API。权限注册表中的大多数 API 特性也有相应的权限策略指令。一个例外是通知 API。

一般来说，当权限策略阻止使用一个强大的特性时，用户甚至不会被要求获得使用权限，而权限 API `query()` (en-US) 方法将返回 `state` (en-US) 值为 `denied`。

参见权限 > 与权限策略规范的关系。

允许列表
----

一个允许列表是一系列源的列表，它采取一个或多个包含在括号中的下列值，用空格隔开：

* `*`：该特性将被允许在本文档和所有嵌套浏览上下文（`<iframe>`）中使用，无论其源如何。
* `()`（空允许列表）：该特性在顶层和嵌套浏览环境中被禁用。等价的 `<iframe>` 的 `allow` 属性值是 `'none'`。
* `self`：该特性只允许在本文档和同一来源的所有嵌套浏览环境（`<iframe>`）中使用。在嵌套浏览的情况下，该特性不允许出现在跨源文件中。`self` 可以被认为是 `https://your-site.example.com` 的简写。对应的 `<iframe>` 的 `allow` 属性值是 `self`。
* `src`：只要载入该框架的文件与该框架 src 属性中的 URL 来源相同，该特性在该 `<iframe>` 中就被允许。这个值只用于 `<iframe>` 的 `allow` 属性，并且是 `<iframe>` 的*默认* `allowlist` 值。
* `"<origin>"`：该特性允许用于特定的源（如 `"https://a.example.com"`）。源应该用空格隔开。请注意，`<iframe>` allow 属性中的源是不加引号的。

值 `*` 和 `()` 只能单独使用，而 `self` 和 `src` 可以与一个或多个源结合使用。

**备注：** 指令有一个默认的允许列表，对于 `Permissions-Policy` HTTP 标头来说，它总是 `*`'、`self` 或 `none` 之一，如果它们没有明确地列在策略中，则管理默认行为。这些都是在各个指令参考页中指定的。对于 `<iframe>` 的 `allow` 属性，默认行为总是 `src`。

在支持的情况下，你可以在权限策略源中包括通配符。这意味着你不必在允许列表中明确指定几个不同的子域，而是可以用通配符在一个单一的源中指定它们。

所以你可以指定

```http
("https://example.com" "https://*.example.com")
```

而不是

```http
("https://example.com" "https://a.example.com" "https://b.example.com" "https://c.example.com")
```

**备注：** `"https://*.example.com"` 不匹配 `"https://example.com"`。

允许列表示例：

* `*`
* `()`
* `(self)`
* `(src)`
* `("https://a.example.com")`
* `("https://a.example.com" "https://b.example.com")`
* `(self "https://a.example.com" "https://b.example.com")`
* `(src "https://a.example.com" "https://b.example.com")`
* `("https://*.example.com")`
权限特性标头语法
--------

一般的语法看起来像这样：

```http
Permissions-Policy: <directive>=<allowlist>
```

例如，要阻止对地理位置信息的访问，你可以这样做：

```http
Permissions-Policy: geolocation=()
```

或允许一部分源访问，你可以这样做：

```http
Permissions-Policy: geolocation=(self "https://a.example.com" "https://b.example.com")
```

通过发送带有逗号分隔的策略列表的标头，或者通过为每个策略发送单独的标头，可以同时控制几种特性。

例如，以下这些是等价的：

```http
Permissions-Policy: picture-in-picture=(), geolocation=(self https://example.com), camera=\*;

Permissions-Policy: picture-in-picture=()
Permissions-Policy: geolocation=(self https://example.com)
Permissions-Policy: camera=\*
```

iframe 语法
---------

对于一个 `<iframe>` 来说，其允许的源也必须在父页面的允许列表中。由于这种继承行为，最好在 HTTP 头中指定最广泛的可接受的特性支持，然后在每个 `<iframe>` 中指定你需要的支持子集。

一般的语法看起来像这样：

```html
<iframe src="<origin>" allow="<directive> <allowlist>"></iframe>
```

例如，要阻止对地理位置信息的访问，你可以这样做：

```html
<iframe src="https://example.com" allow="geolocation 'none'"></iframe>
```

要将一种策略运用到当前的源和其他的源中，你可以这样做：

```html
<iframe
 src="https://example.com"
 allow="geolocation 'self' https://a.example.com https://b.example.com"></iframe>
```

这很重要：默认情况下，如果一个 `<iframe>` 导航到另一个源，策略就不会应用到 `<iframe>` 导航到的源。通过在 `allow` 属性中列出 `<iframe>` 导航到的源，应用于原始 `<iframe>` 的许可策略将被应用于 `<iframe>` 导航到的源。

通过在 `allow` 属性中包含一个分号分隔的策略指令列表，可以同时控制多个特性。

```html
<iframe
 src="https://example.com"
 allow="geolocation 'self' https://a.example.com https://b.example.com; fullscreen 'none'"></iframe>
```

值得特别一提的是 `src` 值。我们在上面提到，使用这个 allowlist 值意味着相关的特性在这个 `<iframe>` 中是被允许的，只要加载到其中的文档与它的 src 属性中的 URL 来自同一来源。这个值是 `allow` 中所列特性的*默认* `allowlist` 值，所以下面这些是等价的：

```html
<iframe src="https://example.com" allow="geolocation 'src'">
  <iframe src="https://example.com" allow="geolocation"></iframe
></iframe>
```

**备注：** 你会注意到，`<iframe>` 策略的语法与 `Permissions-Policy` 标头的语法有些不同。前者仍然使用与旧的特性策略规范相同的语法，它被权限策略所取代。

嵌入式内容的继承策略
----------

脚本会继承其浏览上下文的策略，而不管其源如何。这意味着顶层的脚本会继承主文件的策略。

所有 `<iframe>` 都继承其父页的策略。如果 `<iframe>` 有一个 `allow` 属性，*并且*父页面有一个 `Permissions-Policy` 标头，父页面和 `allow` 属性的策略将被合并，使用最严格的子集。对于一个 `<iframe>` 来说，要启用一种特性，其源必须是在父页和 `allow` 属性的允许列表中。

在一个策略中禁用一个特性是单向的切换。如果子框架被它的父框架禁用了一个特性，那么这个子框架就不能重新启用它，这个子框架的任何后代也不能。

示例
--

### 结合使用 HTTP 标头和 `<iframe>` 策略

例如，假设我们想在我们自己的源和来自我们信任的广告网络的嵌入式内容中启用地理位置的使用。我们可以这样设置页面范围内的许可策略：

```http
Permissions-Policy: geolocation=(self https://trusted-ad-network.com)
```

在我们的广告 `<iframe>` 中，我们可以这样设置 `https://trusted-ad-network.com` 源的访问权限：

```html
<iframe src="https://trusted-ad-network.com" allow="geolocation"></iframe>
```

如果一个不同的源最终被加载到 `<iframe>`，它将无法访问地理定位：

```html
<iframe src="https://rogue-origin-example.com" allow="geolocation"></iframe>
```

规范
--

| Specification |
| --- |
| Permissions Policy # permissions-policy-http-header-field |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* `Permissions-Policy` HTTP 标头
* iframe 的 allow 属性
* 使用权限特性控制浏览器特性：使用指南，其中还包含几个演示链接。
* chromestatus.com 上的权限/特性策略
* 隐私、权限和信息安全 (en-US)


Proxy servers and tunneling - HTTP

Proxy servers and tunneling
===========================

当访问不同的网站时，代理服务器和 HTTP 管道帮助访问万维网 . 一个代理可以是用户的本地计算机，或者介于用户计算机和终端服务器之间的地方。本章节概述一些关于代理和相关配置选项的基础知识。

有两种代理：**正向代理** (如：管道、网关) 和**反向代理** (用于控制和保证服务器的负载均衡、认证、加密和缓存)。

正向代理
----

正向代理，也可以叫“网关”或者仅仅为一个或多个客户端提供代理服务的“代理”。在互联网上像这样的代理不计其数。他们存储并转发网络服务（如 DNS，网页）以减少和控制大家所使用的带宽。

正向代理可以是匿名代理，并允许用户在浏览 web 或者使用其他服务时隐藏自己的 IP。 TOR (洋葱路由), 匿名地在多个代理间路由因特网。

反向代理
----

顾名思义，反向代理所做的事情与正向代理相反：正向代理代表客户端（或者发送请求的主机），而反向代理代表服务器。正向代理可以隐藏客户端的身份，而反向代理可以隐藏服务器的身份。反向代理的用处很多，例如：

* 负载均衡：在多个服务器之间分发负载，
* 缓存静态内容：缓存图片等静态内容，为服务器分担压力，
* 压缩：压缩和优化内容以加快传输的速度。
通过代理转发客户端消息
-----------

代理可以将请求地址设置为自身的 IP，这可以隐匿客户端的身份。然而，这也会导致原始请求的部分信息丢失。发起请求的客户端的 IP 地址可以用来调试、统计或者生成基于位置的内容。通常使用如下 HTTP 头来暴露这部分信息：

标准头部：

`Forwarded`

Contains information from the client-facing side of proxy servers that is altered or lost when a proxy is involved in the path of the request.

Or the de-facto standard versions:

`X-Forwarded-For`
非标准

Identifies the originating IP addresses of a client connecting to a web server through an HTTP proxy or a load balancer.

`X-Forwarded-Host`
非标准

Identifies the original host requested that a client used to connect to your proxy or load balancer.

`X-Forwarded-Proto`
非标准

identifies the protocol (HTTP or HTTPS) that a client used to connect to your proxy or load balancer.

To provide information about the proxy itself (not about the client connecting to it), the `Via` header can be used.

`Via`

Added by proxies, both forward and reverse proxies, and can appear in the request headers and the response headers.

HTTP tunneling
--------------

Tunneling transmits private network data and protocol information through public network by encapsulating the data. HTTP tunneling is using a protocol of higher level (HTTP) to transport a lower level protocol (TCP).

The HTTP protocol specifies a request method called `CONNECT`. It starts two-way communications with the requested resource and can be used to open a tunnel. This is how a client behind an HTTP proxy can access websites using SSL (i.e. HTTPS, port 443). Note, however, that not all proxy servers support the `CONNECT` method or limit it to port 443 only.

See also the HTTP tunnel article on Wikipedia.

Proxy Auto-Configuration (PAC)
------------------------------

A Proxy Auto-Configuration (PAC) file is a JavaScript function that determines whether web browser requests (HTTP, HTTPS, and FTP) go directly to the destination or are forwarded to a web proxy server. The JavaScript function contained in the PAC file defines the function:

The auto-config file should be saved to a file with a `.pac` filename extension:
```

proxy.pac
```

And the MIME type set to:
```

application/x-ns-proxy-autoconfig
```

The file consists of a function called `FindProxyForURL`. The example below will work in an environment where the internal DNS server is set up so that it can only resolve internal host names, and the goal is to use a proxy only for hosts that aren't resolvable:

```js
function FindProxyForURL(url, host) {
  if (isResolvable(host)) return "DIRECT";
  else return "PROXY proxy.mydomain.com:8080";
}
```

参见代理自动配置（PAC）以获取更多示例。

参见
--

* `CONNECT`
* 维基百科上的代理服务器


典型的 HTTP 会话 - HTTP

典型的 HTTP 会话
===========

在像 HTTP 这样的客户端——服务器（Client-Server）协议中，会话分为三个阶段：

1. 客户端建立一条 TCP 连接（如果传输层不是 TCP，也可以是其他适合的连接）。
2. 客户端发送请求并等待应答。
3. 服务器处理请求并送回应答，回应包括一个状态码和对应的数据。

从 HTTP/1.1 开始，连接在完成第三阶段后不再关闭，客户端可以再次发起新的请求。这意味着第二步和第三步可以连续进行数次。

建立连接
----

在客户端——服务器协议中，连接是由客户端发起建立的。在 HTTP 中打开连接意味着在底层传输层启动连接，通常是 TCP。

使用 TCP 时，HTTP 服务器的默认端口号是 80，另外还有 8000 和 8080 也很常用。页面的 URL 会包含域名和端口号，但当端口号为 80 时可以省略。前往标识互联网上的内容获取更多内容。

**备注：** 客户端——服务器模型不允许服务器在没有显式请求时发送数据给客户端。为了解决这个问题，Web 开发者们使用了许多技术：例如，使用 `XMLHTTPRequest` 或 `Fetch` API 周期性地请求服务器，使用 HTML WebSocket API，或其他类似协议。

发送客户端请求
-------

一旦连接建立，用户代理就可以发送请求（用户代理通常是 Web 浏览器，但也可以是其他的，例如爬虫）。客户端请求由一系列文本指令组成，并使用 CRLF 分隔（回车，然后是换行），它们被划分为三个块：

1. 第一行包括请求方法及请求参数：
    * 文档路径，不包括协议和域名的绝对路径 URL
    * 使用的 HTTP 协议版本
2. 接下来的行每一行都表示一个 HTTP 标头，为服务器提供关于所需数据的信息（例如语言，或 MIME 类型），或是一些改变请求行为的数据（例如当数据已经被缓存，就不再应答）。这些 HTTP 标头形成一个以空行结尾的块。
3. 最后一块是可选数据块，包含更多数据，主要被 POST 方法所使用。
### 请求示例

访问 developer.mozilla.org（`https://developer.mozilla.org/`）的根页面，并告诉服务器用户代理倾向于该页面使用法语展示：

```http
GET / HTTP/1.1
Host: developer.mozilla.org
Accept-Language: fr
```

注意最后的空行，它把标头与数据块分隔开。由于在 HTTP 标头中没有 `Content-Length`，数据块是空的，所以服务器可以在收到代表标头结束的空行后就开始处理请求。

例如，发送表单的结果：

```http
POST /contact\_form.php HTTP/1.1
Host: developer.mozilla.org
Content-Length: 64
Content-Type: application/x-www-form-urlencoded

name=Joe%20User&request=Send%20me%20one%20of%20your%20catalogue
```

### 请求方法

HTTP 定义了一组请求方法用来指定对目标资源的行为。它们一般是名词，但这些请求方法有时会被叫做 HTTP 动词。最常用的请求方法是 `GET` 和 `POST`：

* `GET` 方法请求指定的资源。`GET` 请求应该只被用于获取数据。
* `POST` 方法向服务器发送数据，因此会改变服务器状态。这个方法常在 HTML 表单中使用。
服务器响应结构
-------

当收到用户代理发送的请求后，Web 服务器就会处理它，并最终送回一个响应。与客户端请求很类似，服务器响应由一系列文本指令组成，并使用 CRLF 分隔，它们被划分为三个不同的块：

1. 第一行是*状态行*，包括使用的 HTTP 协议版本，然后是一个状态码（及其人类可读的描述文本）。
2. 接下来每一行都表示一个 HTTP 标头，为客户端提供关于所发送数据的一些信息（如类型、数据大小、使用的压缩算法、缓存指示）。与客户端请求的头部块类似，这些 HTTP 标头组成一个块，并以一个空行结束。
3. 最后一块是数据块，包含了响应的数据（如果有的话）。
### 响应示例

成功的网页响应：

```http
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 55743
Connection: keep-alive
Cache-Control: s-maxage=300, public, max-age=0
Content-Language: en-US
Date: Thu, 06 Dec 2018 17:37:18 GMT
ETag: "2e77ad1dc6ab0b53a2996dfd4653c1c3"
Server: meinheld/0.6.1
Strict-Transport-Security: max-age=63072000
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Vary: Accept-Encoding,Cookie
Age: 7

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>A simple webpage</title>
</head>
<body>
  <h1>Simple HTML webpage</h1>
  <p>Hello, world!</p>
</body>
</html>
```

请求资源已被永久移动的网页响应：

```http
HTTP/1.1 301 Moved Permanently
Server: Apache/2.4.37 (Red Hat)
Content-Type: text/html; charset=utf-8
Date: Thu, 06 Dec 2018 17:33:08 GMT
Location: https://developer.mozilla.org/ （目标资源的新地址，服务器期望用户代理去访问它）
Keep-Alive: timeout=15, max=98
Accept-Ranges: bytes
Via: Moz-Cache-zlb05
Connection: Keep-Alive
Content-Length: 325 (如果用户代理无法转到新地址，就显示一个默认页面)

<!DOCTYPE html>… (包含一个网站自定义页面，帮助用户找到丢失的资源)
```

请求资源不存在的网页响应：

```http
HTTP/1.1 404 Not Found
Content-Type: text/html; charset=utf-8
Content-Length: 38217
Connection: keep-alive
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
Content-Language: en-US
Date: Thu, 06 Dec 2018 17:35:13 GMT
Expires: Thu, 06 Dec 2018 17:35:13 GMT
Server: meinheld/0.6.1
Strict-Transport-Security: max-age=63072000
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Vary: Accept-Encoding,Cookie
X-Cache: Error from cloudfront
<!DOCTYPE html>… (包含一个站点自定义 404 页面，帮助用户找到丢失的资源)
```

### 响应状态码

HTTP 响应状态码用来表示一个 HTTP 请求是否成功完成。响应被分为 5 种类型：信息型响应，成功响应，重定向，客户端错误和服务端错误。

* `200`：OK。请求成功。
* `301`：Moved Permanently。请求资源的 URI 已被改变。
* `404`：Not Found。服务器无法找到请求的资源。
参见
--

* 识别互联网上的内容
* HTTP 标头（header）
* HTTP 请求方法
* HTTP 响应状态码


跨源资源共享（CORS） - HTTP

跨源资源共享（CORS）
============

**跨源资源共享**（CORS，或通俗地译为跨域资源共享）是一种基于 HTTP 头的机制，该机制通过允许服务器标示除了它自己以外的其他源（域、协议或端口），使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求，该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中，浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

跨源 HTTP 请求的一个例子：运行在 `https://domain-a.com` 的 JavaScript 代码使用 `XMLHttpRequest` 来发起一个到 `https://domain-b.com/data.json` 的请求。

出于安全性，浏览器限制脚本内发起的跨源 HTTP 请求。例如，`XMLHttpRequest` 和 Fetch API 遵循同源策略。这意味着使用这些 API 的 Web 应用程序只能从加载应用程序的同一个域请求 HTTP 资源，除非响应报文包含了正确 CORS 响应头。

![CORS 机制的图表表示](/zh-CN/docs/Web/HTTP/CORS/cors_principle.png)

CORS 机制允许 Web 应用服务器进行跨源访问控制，从而使跨源数据传输得以安全进行。现代浏览器支持在 API 容器中（例如 `XMLHttpRequest` 或 Fetch）使用 CORS，以降低跨源 HTTP 请求所带来的风险。

什么情况下需要 CORS？
-------------

这份跨源共享标准允许在下列场景中使用跨站点 HTTP 请求：

* 前文提到的由 `XMLHttpRequest` 或 Fetch API 发起的跨源 HTTP 请求。
* Web 字体（CSS 中通过 `@font-face` 使用跨源字体资源），因此，网站就可以发布 TrueType 字体资源，并只允许已授权网站进行跨站调用。
* WebGL 贴图。
* 使用 `drawImage()` 将图片或视频画面绘制到 canvas。
* 来自图像的 CSS 图形 (en-US)。

本文概述了跨源资源共享机制及其所涉及的 HTTP 标头。

功能概述
----

跨源资源共享标准新增了一组 HTTP 标头字段，允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外，规范要求，对那些可能对服务器数据产生副作用的 HTTP 请求方法（特别是 `GET` 以外的 HTTP 请求，或者搭配某些 MIME 类型的 `POST` 请求），浏览器必须首先使用 `OPTIONS` 方法发起一个预检请求（preflight request），从而获知服务端是否允许该跨源请求。服务器确认允许之后，才发起实际的 HTTP 请求。在预检请求的返回中，服务器端也可以通知客户端，是否需要携带身份凭证（例如 Cookie 和 HTTP 认证相关数据）。

CORS 请求失败会产生错误，但是为了安全，在 JavaScript 代码层面*无法*获知到底具体是哪里出了问题。你只能查看浏览器的控制台以得知具体是哪里出现了错误。

接下来的内容将讨论相关场景，并剖析该机制所涉及的 HTTP 标头字段。

若干访问控制场景
--------

这里，我们使用三个场景来解释跨源资源共享机制的工作原理。这些例子都使用在任意所支持的浏览器上都可以发出跨域请求的 `XMLHttpRequest` 对象。

### 简单请求

某些请求不会触发 CORS 预检请求。在废弃的 CORS 规范中称这样的请求为*简单请求*，但是目前的 Fetch 规范（CORS 的现行定义规范）中不再使用这个词语。

其动机是，HTML 4.0 中的 `<form>` 元素（早于跨站 `XMLHttpRequest` 和 `fetch`）可以向任何来源提交简单请求，所以任何编写服务器的人一定已经在保护跨站请求伪造攻击（CSRF）。在这个假设下，服务器不必选择加入（通过响应预检请求）来接收任何看起来像表单提交的请求，因为 CSRF 的威胁并不比表单提交的威胁差。然而，服务器仍然必须提供 `Access-Control-Allow-Origin` 的选择，以便与脚本*共享*响应。

若请求**满足所有下述条件**，则该请求可视为*简单请求*：

* 使用下列方法之一：
    + `GET`
    + `HEAD`
    + `POST`
* 除了被用户代理自动设置的标头字段（例如 `Connection`、`User-Agent` 或其他在 Fetch 规范中定义为禁用标头名称的标头），允许人为设置的字段为 Fetch 规范定义的对 CORS 安全的标头字段集合。该集合为：
    + `Accept`
    + `Accept-Language`
    + `Content-Language`
    + `Content-Type`（需要注意额外的限制）
    + `Range`（只允许简单的范围标头值 如 `bytes=256-` 或 `bytes=127-255`）
* `Content-Type` 标头所指定的媒体类型的值仅限于下列三者之一：
    + `text/plain`
    + `multipart/form-data`
    + `application/x-www-form-urlencoded`
* 如果请求是使用 `XMLHttpRequest` 对象发出的，在返回的 `XMLHttpRequest.upload` 对象属性上没有注册任何事件监听器；也就是说，给定一个 `XMLHttpRequest` 实例 `xhr`，没有调用 `xhr.upload.addEventListener()`，以监听该上传请求。
* 请求中没有使用 `ReadableStream` 对象。

**备注：** WebKit Nightly 和 Safari Technology Preview 为 `Accept`、`Accept-Language` 和 `Content-Language` 标头字段的值添加了额外的限制。如果这些标头字段的值是“非标准”的，WebKit/Safari 就不会将这些请求视为“简单请求”。WebKit/Safari 并没有在文档中列出哪些值是“非标准”的，不过我们可以在这里找到相关讨论：

* Require preflight for non-standard CORS-safelisted request headers Accept, Accept-Language, and Content-Language
* Allow commas in Accept, Accept-Language, and Content-Language request headers for simple CORS
* Switch to a blacklist model for restricted Accept headers in simple CORS requests

其他浏览器并不支持这些额外的限制，因为它们不属于规范的一部分。

比如说，假如站点 `https://foo.example` 的网页应用想要访问 `https://bar.other` 的资源。`foo.example` 的网页中可能包含类似于下面的 JavaScript 代码：

```js
const xhr = new XMLHttpRequest();
const url = "https://bar.other/resources/public-data/";

xhr.open("GET", url);
xhr.onreadystatechange = someHandler;
xhr.send();
```

此操作实行了客户端和服务器之间的简单交换，使用 CORS 标头字段来处理权限：

![简单 GET 请求的示意图](/zh-CN/docs/Web/HTTP/CORS/simple-req.png)

以下是浏览器发送给服务器的请求报文：

```http
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,\*/\*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
```

请求标头字段 `Origin` 表明该请求来源于 `http://foo.example`。

让我们来看看服务器如何响应：

```http
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2
Access-Control-Allow-Origin: \*
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

[…XML Data…]
```

本例中，服务端返回的 `Access-Control-Allow-Origin` 标头的 `Access-Control-Allow-Origin: *` 值表明，该资源可以被**任意**外源访问。

```http
Access-Control-Allow-Origin: \*
```

使用 `Origin` 和 `Access-Control-Allow-Origin` 就能完成最简单的访问控制。如果 `https://bar.other` 的资源持有者想限制他的资源*只能*通过 `https://foo.example` 来访问（也就是说，非 `https://foo.example` 域无法通过跨源访问访问到该资源），他可以这样做：

```http
Access-Control-Allow-Origin: https://foo.example
```

**备注：** 当响应的是附带身份凭证的请求时，服务端**必须**明确 `Access-Control-Allow-Origin` 的值，而不能使用通配符“`*`”。

### 预检请求

与简单请求不同，“需预检的请求”要求必须首先使用 `OPTIONS` 方法发起一个预检请求到服务器，以获知服务器是否允许该实际请求。"预检请求“的使用，可以避免跨域请求对服务器的用户数据产生未预期的影响。

如下是一个需要执行预检请求的 HTTP 请求：

```js
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://bar.other/resources/post-here/");
xhr.setRequestHeader("X-PINGOTHER", "pingpong");
xhr.setRequestHeader("Content-Type", "application/xml");
xhr.onreadystatechange = handler;
xhr.send("<person><name>Arun</name></person>");
```

上面的代码使用 `POST` 请求发送一个 XML 请求体，该请求包含了一个非标准的 HTTP `X-PINGOTHER` 请求标头。这样的请求标头并不是 HTTP/1.1 的一部分，但通常对于 web 应用很有用处。另外，该请求的 `Content-Type` 为 `application/xml`，且使用了自定义的请求标头，所以该请求需要首先发起“预检请求”。

![](/zh-CN/docs/Web/HTTP/CORS/preflight_correct.png)

**备注：** 如下所述，实际的 `POST` 请求不会携带 `Access-Control-Request-*` 标头，它们仅用于 `OPTIONS` 请求。

下面是服务端和客户端完整的信息交互。首次交互是*预检请求/响应*：

```http
OPTIONS /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,\*/\*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: https://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
```

从上面的报文中，我们看到，第 1 - 10 行使用 `OPTIONS` 方法发送了预检请求，浏览器根据上面的 JavaScript 代码片断所使用的请求参数来决定是否需要发送，这样服务器就可以回应是否可以接受用实际的请求参数来发送请求。OPTIONS 是 HTTP/1.1 协议中定义的方法，用于从服务器获取更多信息，是安全的方法。该方法不会对服务器资源产生影响。注意 OPTIONS 预检请求中同时携带了下面两个标头字段：

```http
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
```

标头字段 `Access-Control-Request-Method` 告知服务器，实际请求将使用 `POST` 方法。标头字段 `Access-Control-Request-Headers` 告知服务器，实际请求将携带两个自定义请求标头字段：`X-PINGOTHER` 与 `Content-Type`。服务器据此决定，该实际请求是否被允许。

第 12 - 21 行为预检请求的响应，表明服务器将接受后续的实际请求方法（`POST`）和请求头（`X-PINGOTHER`）。重点看第 15 - 18 行：

```http
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
```

服务器的响应携带了 `Access-Control-Allow-Origin: https://foo.example`，从而限制请求的源域。同时，携带的 `Access-Control-Allow-Methods` 表明服务器允许客户端使用 `POST` 和 `GET` 方法发起请求（与 `Allow` 响应标头类似，但该标头具有严格的访问控制）。

标头字段 `Access-Control-Allow-Headers` 表明服务器允许请求中携带字段 `X-PINGOTHER` 与 `Content-Type`。与 `Access-Control-Allow-Methods` 一样，`Access-Control-Allow-Headers` 的值为逗号分割的列表。

最后，标头字段 `Access-Control-Max-Age` 给定了该预检请求可供缓存的时间长短，单位为秒，默认值是 5 秒。在有效时间内，浏览器无须为同一请求再次发起预检请求。以上例子中，该响应的有效时间为 86400 秒，也就是 24 小时。请注意，浏览器自身维护了一个最大有效时间，如果该标头字段的值超过了最大有效时间，将不会生效。

预检请求完成之后，发送实际请求：

```http
POST /doc HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,\*/\*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: https://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: https://foo.example
Pragma: no-cache
Cache-Control: no-cache

<person><name>Arun</name></person>

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some XML payload]
```

#### 预检请求与重定向

并不是所有浏览器都支持预检请求的重定向。如果一个预检请求发生了重定向，一部分浏览器将报告错误：

>
>
>  The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight.
>  Request requires preflight, which is disallowed to follow cross-origin redirects.
>
>
>
>

CORS 最初要求浏览器具有该行为，不过在后续的修订中废弃了这一要求。但并非所有浏览器都实现了这一变更，而仍然表现出最初要求的行为。

在浏览器的实现跟上规范之前，有两种方式规避上述报错行为：

* 在服务端去掉对预检请求的重定向；
* 将实际请求变成一个简单请求。

如果上面两种方式难以做到，我们仍有其他办法：

1. 发出一个简单请求（使用 `Response.url` 或 `XMLHttpRequest.responseURL`）以判断真正的预检请求会返回什么地址。
2. 发出另一个请求（*真正*的请求），使用在上一步通过 `Response.url` 或 `XMLHttpRequest.responseURL` 获得的 URL。

不过，如果请求是由于存在 `Authorization` 字段而引发了预检请求，则这一方法将无法使用。这种情况只能由服务端进行更改。

### 附带身份凭证的请求

**备注：** 当发出跨源请求时，第三方 cookie 策略仍将适用。无论如何改变本章节中描述的服务器和客户端的设置，该策略都会强制执行。

`XMLHttpRequest` 或 Fetch 与 CORS 的一个有趣的特性是，可以基于 HTTP cookies 和 HTTP 认证信息发送身份凭证。一般而言，对于跨源 `XMLHttpRequest` 或 Fetch 请求，浏览器**不会**发送身份凭证信息。如果要发送凭证信息，需要设置 `XMLHttpRequest` 对象的某个特殊标志位，或在构造 `Request` 对象时设置。

本例中，`https://foo.example` 的某脚本向 `https://bar.other` 发起一个 GET 请求，并设置 Cookies。在 `foo.example` 中可能包含这样的 JavaScript 代码：

```js
const invocation = new XMLHttpRequest();
const url = "https://bar.other/resources/credentialed-content/";

function callOtherDomain() {
  if (invocation) {
    invocation.open("GET", url, true);
    invocation.withCredentials = true;
    invocation.onreadystatechange = handler;
    invocation.send();
  }
}
```

第 7 行将 `XMLHttpRequest` 的 `withCredentials` 标志设置为 `true`，从而向服务器发送 Cookies。因为这是一个简单 `GET` 请求，所以浏览器不会对其发起“预检请求”。但是，如果服务器端的响应中未携带 `Access-Control-Allow-Credentials``: true`，浏览器将**不会**把响应内容返回给请求的发送者。

![](/zh-CN/docs/Web/HTTP/CORS/cred-req-updated.png)

客户端与服务器端交互示例如下：

```http
GET /resources/credentialed-content/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,\*/\*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Referer: https://foo.example/examples/credential.html
Origin: https://foo.example
Cookie: pageAccess=2

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

[text/plain payload]
```

即使第 10 行指定了 Cookie 是属于 `https://bar.other` 的内容的，但是，如果 `https://bar.other` 的响应中缺失 `Access-Control-Allow-Credentials``: true`（第 16 行），则响应内容会被忽略，不会提供给 web 内容。

#### 预检请求和凭据

CORS 预检请求不能包含凭据。预检请求的*响应*必须指定 `Access-Control-Allow-Credentials: true` 来表明可以携带凭据进行实际的请求。

**备注：** 一些企业认证服务要求在预检请求时发送 TLS 客户端证书，这违反了 Fetch 的规范。

Firefox 87 允许通过在设置中设定 `network.cors_preflight.allow_client_cert` 为 `true`（Firefox bug 1511151）来允许这种不规范的行为。基于 chromium 的浏览器目前总是在 CORS 预检请求中发送 TLS 客户端证书（Chrome bug 775438）。

#### 附带身份凭证的请求与通配符

在响应附带身份凭证的请求时：

* 服务器**不能**将 `Access-Control-Allow-Origin` 的值设为通配符“`*`”，而应将其设置为特定的域，如：`Access-Control-Allow-Origin: https://example.com`。
* 服务器**不能**将 `Access-Control-Allow-Headers` 的值设为通配符“`*`”，而应将其设置为标头名称的列表，如：`Access-Control-Allow-Headers: X-PINGOTHER, Content-Type`
* 服务器**不能**将 `Access-Control-Allow-Methods` 的值设为通配符“`*`”，而应将其设置为特定请求方法名称的列表，如：`Access-Control-Allow-Methods: POST, GET`

对于附带身份凭证的请求（通常是 `Cookie`），

这是因为请求的标头中携带了 `Cookie` 信息，如果 `Access-Control-Allow-Origin` 的值为“`*`”，请求将会失败。而将 `Access-Control-Allow-Origin` 的值设置为 `https://example.com`，则请求将成功执行。

另外，响应标头中也携带了 `Set-Cookie` 字段，尝试对 Cookie 进行修改。如果操作失败，将会抛出异常。

#### 第三方 cookie

注意在 CORS 响应中设置的 cookie 适用一般性第三方 cookie 策略。在上面的例子中，页面是在 `foo.example` 加载，但是第 19 行的 cookie 是被 `bar.other` 发送的，如果用户设置其浏览器拒绝所有第三方 cookie，那么将不会被保存。

请求中的 cookie（第 10 行）也可能在正常的第三方 cookie 策略下被阻止。因此，强制执行的 cookie 策略可能会使本节描述的内容无效（阻止你发出任何携带凭据的请求）。

Cookie 策略受 SameSite 属性控制。

HTTP 响应标头字段
-----------

本节列出了服务器为访问控制请求返回的 HTTP 响应头，这是由跨源资源共享规范定义的。上一小节中，我们已经看到了这些标头字段在实际场景中是如何工作的。

### Access-Control-Allow-Origin

响应标头中可以携带一个 `Access-Control-Allow-Origin` 字段，其语法如下：

```http
Access-Control-Allow-Origin: <origin> | \*
```

`Access-Control-Allow-Origin` 参数指定了单一的源，告诉浏览器允许该源访问资源。或者，对于**不需要携带**身份凭证的请求，服务器可以指定该字段的值为通配符“`*`”，表示允许来自任意源的请求。

例如，为了允许来自 `https://mozilla.org` 的代码访问资源，你可以指定：

```http
Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin
```

如果服务端指定了具体的单个源（作为允许列表的一部分，可能会根据请求的来源而动态改变）而非通配符“`*`”，那么响应标头中的 `Vary` 字段的值必须包含 `Origin`。这将告诉客户端：服务器对不同的 `Origin` 返回不同的内容。

### Access-Control-Expose-Headers

译者注：在跨源访问时，`XMLHttpRequest` 对象的 `getResponseHeader()` 方法只能拿到一些最基本的响应头，Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma，如果要访问其他头，则需要服务器设置本响应头。

`Access-Control-Expose-Headers` 头将指定标头放入允许列表中，供浏览器的 JavaScript 代码（如 `getResponseHeader()`）获取。

```http
Access-Control-Expose-Headers: <header-name>[, <header-name>]\*
```

例如：

```http
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
```

这样浏览器就能够通过 `getResponseHeader` 访问 `X-My-Custom-Header` 和 `X-Another-Custom-Header` 响应头了。

### Access-Control-Max-Age

`Access-Control-Max-Age` 头指定了 preflight 请求的结果能够被缓存多久，请参考本文在前面提到的 preflight 例子。

```http
Access-Control-Max-Age: <delta-seconds>
```

`delta-seconds` 参数表示 preflight 预检请求的结果在多少秒内有效。

### Access-Control-Allow-Credentials

`Access-Control-Allow-Credentials` 头指定了当浏览器的 `credentials` 设置为 true 时是否允许浏览器读取 response 的内容。当用在对 preflight 预检测请求的响应中时，它指定了实际的请求是否可以使用 `credentials`。请注意：简单 `GET` 请求不会被预检；如果对此类请求的响应中不包含该字段，这个响应将被忽略掉，并且浏览器也不会将相应内容返回给网页。

```http
Access-Control-Allow-Credentials: true
```

上文已经讨论了附带身份凭证的请求。

### Access-Control-Allow-Methods

`Access-Control-Allow-Methods` 标头字段指定了访问资源时允许使用的请求方法，用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。

```http
Access-Control-Allow-Methods: <method>[, <method>]\*
```

有关预检请求的示例已在上方给出，包含了将此请求头发送至浏览器的示例。

### Access-Control-Allow-Headers

`Access-Control-Allow-Headers` 标头字段用于预检请求的响应。其指明了实际请求中允许携带的标头字段。这个标头是服务器端对浏览器端 `Access-Control-Request-Headers` 标头的响应。

```http
Access-Control-Allow-Headers: <header-name>[, <header-name>]\*
```

HTTP 请求标头字段
-----------

本节列出了可用于发起跨源请求的标头字段。请注意，这些标头字段无须手动设置。当开发者使用 `XMLHttpRequest` 对象发起跨源请求时，它们已经被设置就绪。

### Origin

`Origin` 标头字段表明预检请求或实际跨源请求的源站。

```http
Origin: <origin>
```

origin 参数的值为源站 URL。它不包含任何路径信息，只是服务器名称。

**备注：** `origin` 的值可以为 `null`。

注意，在所有访问控制请求中，`Origin` 标头字段**总是**被发送。

### Access-Control-Request-Method

`Access-Control-Request-Method` 标头字段用于预检请求。其作用是，将实际请求所使用的 HTTP 方法告诉服务器。

```http
Access-Control-Request-Method: <method>
```

相关示例见这里。

### Access-Control-Request-Headers

`Access-Control-Request-Headers` 标头字段用于预检请求。其作用是，将实际请求所携带的标头字段（通过 `setRequestHeader()` 等设置的）告诉服务器。这个浏览器端标头将由互补的服务器端标头 `Access-Control-Allow-Headers` 回答。

```http
Access-Control-Request-Headers: <field-name>[, <field-name>]\*
```

相关示例见这里。

规范
--

| Specification |
| --- |
| Fetch Standard # http-access-control-allow-origin |

浏览器兼容性
------

BCD tables only load in the browser with JavaScript enabled. Enable JavaScript to view data.

参见
--

* CORS 错误
* 启用 CORS：如何在服务器中添加 CORS 支持
* `XMLHttpRequest`
* Fetch API
* 它会 CORS 吗？——交互的 CORS 解释器和生成器
* 如何不带 CORS 的运行 Chrome 浏览器
* 在所有（现代）浏览器中使用 CORS
* Stack Overflow 面对常见问题的解答:
    + 如何避免 CORS 预检请求
    + 如何利用 CORS 代理避免“*No Access-Control-Allow-Origin header*”
    + 如何修复“*Access-Control-Allow-Origin header must not be the wildcard*”


Configuring servers for Ogg media - HTTP

Configuring servers for Ogg media
=================================

HTML `<audio>` 和 `<video>` 标签无需用户安装任何插件或软件即可播放媒体文件。本指南包括了一些服务器配置修改，这些修改对于 web 服务器提供 Ogg 媒体文件是必要的。这些信息在遇到服务器未配置为可识别的其他媒体类型文件时也可能提供帮助。

为媒体提供正确的 MIME 类型
----------------

`*.ogg` 和 `*.ogv` 文件包含了视频 (也可能带有音频)，应该和 `video/ogg` MIME 类型一起提供。 `*.oga` 和 `*.ogg` 只含音频的文件应该与 `audio/ogg` MIME 类型一起提供。

如果不确定 Ogg 文件是否包含音频或视频，可以和 MIME 类型 `application/ogg`一起提供，浏览器会将其视为视频文件。

默认情况下，大多数服务器不为 Ogg 媒体提供正确的 MIME 类型，因此可能需要为此添加适当的配置。

对于 Apache，可添加如下配置：
```

AddType audio/ogg .oga
AddType video/ogg .ogv
AddType application/ogg .ogg
```

你可以在 guide to media types and formats on the web 找到有关可能的媒体文件类型以及其中使用的编解码器的特定信息。特别是，关于配置媒体服务器正确托管媒体 media container formats 特别有用。

Handle HTTP 1.1 byte range requests correctly
---------------------------------------------

In order to support seeking and playing back regions of the media that aren't yet downloaded, Gecko uses HTTP 1.1 byte-range requests to retrieve the media from the seek target position. In addition, Gecko uses byte-range requests to seek to the end of the media (assuming you serve the `Content-Length` header) in order to determine the duration of the media.

Your server should accept the `Accept-Ranges``: bytes` HTTP header if it can accept byte-range requests. It must return `206``: Partial content` to all byte range requests; otherwise, browsers can't be sure you actually support byte range requests.

Your server must also return `206: Partial Content` for the request `Range: bytes=0-` as well.

Include regular key frames
--------------------------

When the browser seeks through Ogg media to a specified time, it has to seek to the nearest key frame before the seek target, then download and decode the video from there until the requested target time. The farther apart your key frames are, the longer this takes, so it's helpful to include key frames at regular intervals.

By default, `ffmpeg2theora` uses one key frame every 64 frames (or about every 2 seconds at 30 frames per second), which works pretty well.

**备注：** Of course, the more key frames you use, the larger your video file is, so you may need to experiment a bit to get the right balance between file size and seek performance.

Consider using the preload attribute
------------------------------------

The HTML `<audio>` and `<video>` elements provide the `preload` attribute, which tells the browser to attempt to download the entire media when the page loads. Without `preload`, the browser only downloads enough of the media to display the first video frame, and to determine the media's duration.

`preload` is off by default, so if getting to video is the point of your web page, your users may appreciate it if you include `preload` in your video elements. using `preload="metadata"` will preload the media file's metadata and possibly the first few frames of video. Setting `payload` to `auto` tells the browser to automatically begin downloading the media as soon as the page is loaded, under the assumption that the user will play it.

Configuration for older Firefox versions
----------------------------------------

### Serve X-Content-Duration headers

**备注：** As of Firefox 41, the `X-Content-Duration` header is no longer supported. See Firefox bug 1160695 for more details.

The Ogg format doesn't encapsulate the duration of media, so for the progress bar on the video controls to display the duration of the video, Gecko needs to determine the length of the media using other means.

There are two ways Gecko can do this. The best way is to offer an `X-Content-Duration` header when serving Ogg media files. This header provides the duration of the video in seconds (**not** in HH:MM:SS format) as a floating-point value.

For example, if the video is 1 minute and 32.6 seconds long, this header would be:
```

X-Content-Duration: 92.6
```

If your server provides the `X-Content-Duration` header when serving Ogg media, Gecko doesn't have to do any extra HTTP requests to seek to the end of the file to calculate its duration. This makes the entire process much more efficient as well as more accurate.

As an inferior alternative, Gecko can estimate the video length based on the Content-Length. See next point.

### Don't use HTTP compression for media files

One common way to reduce the load on a web server is to use gzip or deflate compression when serving to a supporting web browser.

Although it's unlikely, it's possible the browser may advertise that it supports HTTP compression (gzip/deflate) using the `Accept-Encoding: gzip,deflate` header when requesting media files. Your server should be configured to not do so. The data in media files is already compressed, so you won't get any real benefit from compression, and the use of compression makes it impossible for the browser to properly seek the video or determine its duration.

Another probelm with allowing HTTP compression for media streaming: Apache servers don't send the `Content-Length` response header if gzip encoding is used.

### Getting the duration of Ogg media

You can use the `oggz-info` tool to get the media duration; this tool is included with the `oggz-tools` package. The output from `oggz-info` looks like this:
```

 $ oggz-info /g/media/bruce_vs_ironman.ogv
 Content-Duration: 00:01:00.046

 Skeleton: serialno 1976223438
         4 packets in 3 pages, 1.3 packets/page, 27.508% Ogg overhead
         Presentation-Time: 0.000
         Basetime: 0.000

 Theora: serialno 0170995062
         1790 packets in 1068 pages, 1.7 packets/page, 1.049% Ogg overhead
         Video-Framerate: 29.983 fps
         Video-Width: 640
         Video-Height: 360

 Vorbis: serialno 0708996688
         4531 packets in 167 pages, 27.1 packets/page, 1.408% Ogg overhead
         Audio-Samplerate: 44100 Hz
         Audio-Channels: 2
```

Note that you can't simply serve up the reported Content-Duration line reported by `oggz-info`, because it's reported in HH:MM:SS format. You'll need to convert it to seconds only, then serve that as your `X-Content-Duration` value. Just parse out the HH, MM, and SS into numbers, then do (HH\*3600)+(MM\*60)+SS to get the value you should report.

It's important to note that it appears that `oggz-info` makes a read pass of the media in order to calculate its duration, so it's a good idea to store the duration value in order to avoid lengthy delays while the value is calculated for every HTTP request of your Ogg media.

See also
--------

* Guide to media types and formats on the web
* Video and audio content
* The "codecs" parameter in common media types (en-US)


HTTP 的重定向 - HTTP

HTTP 的重定向
=========

**URL 重定向**（也称为 *URL 转发*）是一种为页面、表单或者整个 Web 站点/应用提供多个 URL 地址的技术。HTTP 对此操作有一种特殊类型的响应，称为 ***HTTP 重定向***（HTTP redirect）。

重定向可实现许多目标：

* 站点维护或停机期间的临时重定向。
* 永久重定向将在更改站点的 URL 后，保留现有的链接/书签、上传文件时表示进度的页面等。
原理
--

在 HTTP 协议中，重定向操作由服务器向请求发送特殊的重定向响应而触发。重定向响应包含以 `3` 开头的状态码，以及 `Location` 标头，其保存着重定向的 URL。

浏览器在接收到重定向时，它们会立刻加载 `Location` 标头中提供的新 URL。除了额外的往返操作中会有一小部分性能损失之外，重定向操作对于用户来说是不可见的。

![初始请求从客户端发送到服务器。服务器以 301:moved permanently 响应，并带有重定向的 URL。客户端对服务器返回的新 URL 发出 GET 请求，服务端返回 200 OK 响应。](/zh-CN/docs/Web/HTTP/Redirections/httpredirect.png)

不同类型的重定向映射可以划分为三个类别：

1. 永久重定向
2. 临时重定向
3. 特殊重定向
### 永久重定向

这种重定向操作是永久性的。它表示原 URL 不应再被使用，而选用新的 URL 替换它。搜索引擎机器人、RSS 阅读器以及其他爬虫将更新资源原始的 URL。

| 状态码 | 状态文本 | 处理方法 | 典型应用场景 |
| --- | --- | --- | --- |
| `301` | `Moved Permanently` | `GET` 方法不会发生变更。其他方法有可能会变更为 `GET` 方法。[1] | 网站重构。 |
| `308` | `Permanent Redirect` | 方法和消息主体都不发生变化。 | 使用用于非 GET 链接/操作重组网站。 |

[1] 该规范无意使方法发生改变，但在实际应用中用户代理会更改其方法。`308` 状态码被创建用来消除在使用非 `GET` 方法时行为的歧义。

### 临时重定向

有时候请求的资源无法从其标准地址访问，但是却可以从另外的地方访问。在这种情况下，可以使用临时重定向。

搜索引擎和其他爬虫不会记录新的、临时的 URL。在创建、更新或者删除资源的时候，临时重定向也可以用于显示临时性的进度页面。

| 状态码 | 状态文本 | 处理方法 | 典型应用场景 |
| --- | --- | --- | --- |
| `302` | `Found` | `GET` 方法不会发生变更。其他方法有可能会变更为 `GET` 方法。[2] | 由于不可预见的原因该页面暂不可用。 |
| `303` | `See Other` | `GET` 方法不会发生变更，其他方法会*变更*为 `GET` 方法（消息主体丢失）。 | 用于 `PUT` 或 `POST` 请求完成之后重定向，来防止由于页面刷新导致的操作的重复触发。 |
| `307` | `Temporary Redirect` | 方法和消息主体都不发生变化。 | 由于不可预见的原因该页面暂不可用。当站点支持非 `GET` 方法的链接或操作的时候，该状态码优于 302 状态码。 |

[2] 该规范无意使方法发生改变，但在实际应用中用户代理会改变其方法。`307` 状态码被创建用来消除在使用非 `GET` 方法时行为的歧义。

### 特殊重定向

`304`（Not Modified）会使页面跳转到本地的缓存副本中（可能已过时），而 `300`（Multiple Choice）则是一种手动重定向：将消息主体以 Web 页面形式呈现在浏览器中，列出了可能的重定向链接，用户可以从中进行选择。

| 状态码 | 状态文本 | 典型应用场景 |
| --- | --- | --- |
| `300` | `Multiple Choice` | 不常用：所有的选项在消息主体的 HTML 页面中列出。鼓励在 `Link` 标头中加入机器可读的 `rel=alternate` |
| `304` | `Not Modified` | 发送用于重新验证的条件请求。表示缓存的响应仍然是新的并且可以使用。 |

指定重定向的其他方式
----------

HTTP 重定向不是定义重定向的唯一方法。还有两个：

1. 借助 HTML 的 `<meta>` 元素的 HTML 重定向机制
2. 借助 DOM 的 JavaScript 重定向机制。
### HTML 重定向机制

HTTP 重定向是创建重定向的最佳方式，但是有时候你并不能控制服务器。针对这些特定的应用情景，可以尝试在页面的 `<head>` 中添加一个 `<meta>` 元素，并将其 `http-equiv` 属性的值设置为 `refresh`。当显示页面的时候，浏览器会检测该元素，然后跳转到指定的页面。

```html
<head>
  <meta http-equiv="Refresh" content="0; URL=http://example.com/" />
</head>
```

`content` 属性的值开头是一个数字，指示浏览器在等待该数字表示的秒数之后再进行跳转。建议始终将其设置为 `0` 来获取更好的无障碍体验。

显然，该方法仅适用于 HTML 页面（或类似的页面），然而并不能应用于图片或者其他类型的内容。

### JavaScript 重定向机制

在 JavaScript 中，重定向机制的原理是设置 `window.location` 的属性值，然后加载新的页面。

```js
window.location = "https://example.com/";
```

与 HTML 重定向机制类似，这种方式并不适用于所有类型的资源，并且显然只有在执行 JavaScript 的客户端上才能使用。另外一方面，它也提供了更多的可能性：比如在只有满足了特定的条件的情况下才可以触发重定向机制的场景。

### 优先级

由于存在上述三种 URL 重定向机制，那么在多种方法同时设定的情况下，哪种方法会首先起作用呢？

1. HTTP 协议的重定向机制永远最先触发——它们甚至在没有传输页面的情况下就已经存在。
2. HTML 的重定向机制 (`<meta>`) 会在没有任何 HTTP 协议重定向的情况下触发。
3. JavaScript 的重定向机制总是作为最后诉诸的手段，并且只有在客户端开启了 JavaScript 的情况下才起作用。

如果可能，请采用 HTTP 协议的重定向机制，而不要使用 `<meta>` 元素重定向。假如开发人员修改了 HTTP 重定向，而忘记修改 HTML 页面的重定向，那么二者就会不一致，最终结果或者出现无限循环，或者导致其他噩梦的发生。

应用场景
----

有以下几种应用场景可以使用重定向机制，但是需要注意应该尽可能地限制其使用数量，因为每一次重定向都会带来性能上的开销。

### 域名别称

理想情况下，一项资源只有一个访问位置，也就是只有一个 URL。但是由于种种原因，需要为资源设定不同的名称：

扩大站点的用户覆盖面

一个常见的场景是，假如站点位于 `www.example.com` 域名下，那么通过 `example.com` 也应该可以访问到。这种情况下，可以建立从 `example.com` 的页面到 `www.example.com` 的重定向。此外还可以提供你域名常见的同义词，或者该域名容易导致的拼写错误的别称来重定向到你的网站。

迁移到新的域名

例如，公司改名后，你希望用户在搜索旧名称的时候，依然可以访问到应用了新名称的站点。

强制使用 HTTPS

对你网站的 `http://` 版本的请求将重定向到你网站的 `https://` 版本。

### 保持链接有效

当你重构 Web 站点的时候，资源的 URL 会发生改变。即便是你更新站点内部的链接来匹配新的 URL，也无法控制被外部资源使用的 URL。

你并不想因此而使旧链接失效，因为它们会为你带来宝贵的用户并且帮助优化你的 SEO，所以需要建立从旧链接到新链接的重定向映射。

**备注：** 即便是这项技术可以同样应用于内部链接，但是应该尽量避免内部重定向映射。重定向机制会带来相当大的性能开销（额外的 HTTP 请求）。所以如果你可以通过修复链接来避免的话，那么就应该将其修复。

### 对于不安全请求的临时响应

不安全的请求会修改服务器端的状态，应该避免用户无意的重复发送它们。

通常，你并不想要你的用户重复发送 `PUT`、`POST` 或 `DELETE` 请求。假如你为该类请求返回响应的话，简单地点击刷新按钮就会导致请求的重复发送（可能在确认消息之后）。

在这种情况下，服务器可以为 URL 发回一个 `303`（See Other）响应，其中含有正确的响应信息。如果刷新按钮被点击的话，只会导致该页面被刷新，而不会重复提交不安全的请求。

### 对于耗时请求的临时响应

一些请求的处理会需要比较长的时间，比如有时候 `DELETE` 请求会被安排为稍后处理。在这种情况下，会返回一个 `303`（See Other）重定向响应，该响应链接到一个页面，表示请求的操作已经被列入计划，并且最终会通知用户操作的进展情况，或者允许用户将其取消。

在通用服务器中配置重定向
------------

### Apache

重定向可以在服务器的配置文件中设置，也可以在每一个文件目录的 `.htaccess` 文件中设置。

mod\_alias 模块提供了 `Redirect` 和 `Redirect_Match` 两种指令来设置 `302` 响应（默认值）：

```xml
<VirtualHost \*:443>
  ServerName example.com
  Redirect / https://www.example.com
</VirtualHost>
```

URL `https://example.com/` 会被重定向至 `https://www.example.com/`，URL 下的任何文件或目录也将重定向到该 URL（`https://example.com/some-page` 将重定向至 `https://www.example.com/some-page`）。

`Redirect_Match` 指令的功能与之类似，不同之处在于它可以通过正则表达式来指定一批受影响的 URL：
```

RedirectMatch ^/images/(.*)$ http://images.example.com/$1
```

位于 `images/` 文件夹下的所有文档都会被重定向至新的域名。

如果你不想要临时重定向，可以使用额外参数（要么使用的 HTTP 状态代码，要么设置 `permanetn` 关键字）来设置不同的重定向：
```

Redirect permanent / https://www.example.com
# …acts the same as:
Redirect 301 / https://www.example.com
```

mod\_rewrite 模块也可以用来设置重定向。它应用起来更灵活，但也更加复杂。

### Nginx

在 Nginx 中，你可以创建一个服务器模块来进行重定向设置：
```

server {
  listen 80;
  server_name example.com;
  return 301 $scheme://www.example.com$request_uri;
}
```

要将重定向应用于目录或者仅是部分页面，请使用 `rewrite` 指令：
```

rewrite ^/images/(.*)$ http://images.example.com/$1 redirect;
rewrite ^/images/(.*)$ http://images.example.com/$1 permanent;
```

### IIS

在 IIS 中，你可以使用 `<httpRedirect>` 元素来配置重定向。

重定向死锁（循环）
---------

当后续的重定向路径重复之前的路径的时候，重定向循环就产生了。换句话说，就是陷入了无限循环当中，不会有一个最终的页面返回。

大多数情况下，这属于服务器端错误。如果服务器检测不到，就会返回 `500` `Internal Server Error`。假如你在修改了服务器配置不久就出现了这个问题，八成是遇到了重定向循环。

有时候，服务器端无法对其进行检测：重定向循环发生于多台服务器之间，对于每一台服务器来说，都无法获得一个全景图。在这种情况下，浏览器会负责进行检测，然后返回错误信息。Firefox 会呈现如下信息：

>
> Firefox 检测到该服务器正在将指向此网址的请求无限循环重定向。
>
>
>

而 Chrome 则会呈现如下信息：

>
> 该网页将你重定向的次数过多。
>
>
>

无论哪个场景，用户对此都无能为力（除非客户端发生突变，比如说缓存或者 Cookie 不匹配）。

避免重定向循环非常重要，因为它会完全毁掉用户的体验。


使用用户代理字段进行浏览器检测 - HTTP

使用用户代理字段进行浏览器检测
===============

为不同浏览器提供不同的网页或服务通常是一个坏主意。互联网的本意是让任何人都可以访问，无论他们使用哪个浏览器或设备。有一些方法可以根据功能的可用性而不是针对特定的浏览器来开发你的网站以逐步增强自身。
但浏览器和标准并不完美，仍然需要检测浏览器的一些边缘情况。使用用户代理检测浏览器看起来很简单，但是做得很好，实际上是一个非常困难的问题。本文档将指导你尽可能正确地进行此操作。

**备注：** 值得重申的是：使用用户代理嗅探很少会成为一个好主意。你几乎总能发现一个更好的、更广泛兼容的方式来解决你的问题。

在使用浏览器检测之前需要考虑的一些事情
-------------------

当你考虑使用用户代理字段来检测用户正在使用哪种浏览器的时候，第一步是试着回避它。从想明白你**为什么**要这样做开始。

你正尝试解决某个浏览器的某个版本中的一个特定错误吗？

在专业论坛中查找、询问：你不太可能是第一个遇到这个问题的人。你也可以询问专家，或者仅仅是那些和你持有不同观点的人们，他们能提供给你不同的思路。如果这个问题看上去是不常见的，你需要检查一下是否这个错误已经通过错误跟踪系统 (Mozilla; WebKit; Opera) 报告给浏览器厂商。浏览器开发商非常关注错误报告，相关的分析也可能会有其他解决方案的提示。

你正尝试检查某个特性是否可用吗？

你的站点需要使用一个特定的网页特性，然而有一些浏览器还不支持该特性，你想发给那些使用不支持该特性的浏览器的用户一个更老的网页，该网页有更少的特性，但是你知道他们将正常显示。这是最糟糕的使用用户代理检测的理由，因为有很大的概率最终其余的所有浏览器都将使用这个特性。你应该尽你最大努力在这种可能情况下阻止使用用户代理嗅探器，作为替代，使用功能检测器。

你想依据正在使用的是哪个浏览器来提供不同的 HTML 页面吗？

这通常是一个坏的做法，不过有些情况下这又是必要的。在这些情形下，你首先应该分析你所处的情形并确定这确实是必要的。你是否可以通过添加一些无语义的 `<div>` 或者 `<span>` 元素来避免呢？想成功使用用户代理检测是困难的，值得牺牲一些 HTML 的整洁性来换取。另外，也请重新思考一下你的设计：能否通过使用渐进增强或者流体布局来去除使用用户代理检测的需要呢？

避免进行用户代理检测
----------

如果你想要尝试避免使用用户代理检测，在某些情形下有些可供选择的方法。

功能检测

使用功能检测，你不需要弄清楚是哪种浏览器正在渲染你的页面，你只需要检测你需要的某个特定功能是否可用即可。如果该功能不可用，使用一个应变策略。然而，在某些罕见的情形下，你确实需要检测浏览器种类，由于其他浏览器也会在将来以不同方式实现这个功能，绝不要使用功能检测来判断。由此造成的错误将非常难发现和修复。

渐进增强

这种设计技术涉及了按照“层次”开发网页，使用自底向上的方法，从一个简单的层次开始，在一系列连续的层次中通过使用更多的功能来逐步提升站点的能力。

优雅降级

这是一种自顶向下的方式，在搭建可能最好的的站点时使用所有你想要的功能，然后稍微改进使其能在更老版本的浏览器上工作。与渐进增强的方法相比，这种方法可能更困难、效率更低，不过在一些情景下也许是有效的。

用户代理中的哪个部分包含你正在寻找的信息
--------------------

由于用户代理字串的不同部分缺乏统一性，这是一个较难对付的问题。

### 浏览器名

当人们说他们想要进行“浏览器检测”的时候，他们实际上经常想要进行的是“渲染引擎检测”。你实际上想要检测使用的是火狐（Firefox）还是与之对应的海猴（SeaMonkey），或者使用的是 Chrome 还是与之对应的 Chromium？或者你实际上只不过想看浏览器是否正在使用 Gecko 还是 WebKit 渲染引擎？如果这正是你需要的，请参阅后面的部分。

除了 IE 浏览器这个显著的例外，大部分的浏览器以浏览器名/版本号的格式设置浏览器名和版本。但是由于浏览器名不是用户代理字符串中唯一一个以这种格式命名的信息，你不能发现浏览器名，你只能检测是否这是你正在寻找的名字。但是要注意到，一些浏览器在说谎：例如 Chrome 的用户代理字符串中既会包含 Chrome 又会包含 Safari。所以为了检测 Safari 浏览器，你不得不检测其中是否有 Safari 字符串同时没有 Chrome 字符串，Chromium 也经常汇报它自己是 Chrome 浏览器，海猴有时也汇报它自己是火狐浏览器。

也要注意不要对浏览器名使用简单的正则表达式，用户代理字符串中也包含不属于键值对形式的字符串。例如在 Safari 和 Chrome 中包含有“like Gecko”一样的字符串。

|  | 必须包含 | 禁止包含 |  |
| --- | --- | --- | --- |
| Firefox | Firefox/xyz | Seamonkey/xyz |  |
| Seamonkey | Seamonkey/xyz |  |  |
| Chrome | Chrome/xyz | Chromium/xyz |  |
| Chromium | Chromium/xyz |  |  |
| Safari | Safari/xyz | Chrome/xyz or Chromium/xyz | Safari 有两个版本号，一个技术性较强，格式是 Safari/xyz，一个对用户友好一点，格式是 Version/xyz |
| Opera | OPR/xyz [1]Opera/xyz [2] |  | [1] Opera 15+ (基于 Blink 的引擎)[2] Opera 12- (基于 Presto 的引擎) |
| Internet Explorer | ; MSIE xyz; |  | IE 浏览器的名字并没有使用*BrowserName/VersionNumber*的格式 |

当然，这里并没有保证其他浏览器不劫持上述字符串（像在过去，Chrome 劫持了 Safari 的字符串）。这就是为什么使用用户代理字段进行浏览器检测是不可信的，并且做这个检测也仅仅应该是为了检测版本号（劫持过去的版本号这种事情很少发生）。

### 浏览器版本

浏览器版本号通常但并不总是出现在用户代理字符串的*浏览器名/版本号*记号的值的位置。IE 浏览器（该浏览器放置版本号刚好在 MSIE 记号之后）以及在版本 10 之后的 Opera（新增了版本/版本号标记）就是例子。

此处再一次强调，由于并没有保证其他标记会包含有效的数字，请确保选取你正在寻找的浏览器的正确的标记。

### 渲染引擎

正如早先提及到的，在大多数情况下，寻找渲染引擎是一个更好的方式。这将有助于保留鲜为人知的浏览器。使用共同的渲染引擎的浏览器将以相同的方式显示页面：这经常是一个公平的假设：一处有效，处处有效。

现在市面上有 5 个主流的渲染引擎：Trident，Gecko，Presto，Blink 和 WebKit。由于嗅探渲染引擎名是很普遍的，大量的用户代理在其字段中增加其他的渲染引擎名来触发检测。因此当检测渲染引擎的时候，一件很重要的事情，就是要避免“假阳性”（注：false-positives，指一个特性可通过检测，但实际在该浏览器下并不可靠或不可用）情况的发生。

|  | 必须包含 |  |
| --- | --- | --- |
| Gecko | Gecko/xyz |  |
| WebKit | AppleWebKit/xyz | 请注意，WebKit 浏览器包含了'like Gecko'字符串，如果不加以注意，可能会触发检测 Gecko 的 false positive。 |
| Presto | Opera/xyz | **注：** 在 Opera 浏览器在 15 及以上的版本中，已经不再使用 Presto（参见'Blink'）。 |
| Trident | Trident/xyz | IE 浏览器将此字符串放在 User Agent 字符串的*注释*部分。 |
| Blink | Chrome/xyz |  |

渲染引擎版本
------

除了 Gecko 这个显著的例外，大部分的渲染引擎将版本号放置在渲染引擎/版本号标记中。该引擎将 Gecko 版本号放置在用户代理的注释部分，在 rv 字符串之后。在手机版 Gecko14 以及桌面版 Gecko17 之后，该引擎也在 Gecko/version 标记处放置版本号的值（先前的版本中此处放置的是构建日期，然后固定的日期调用 GeckoTrail）。

操作系统
----

大多数的用户代理字符串也给出了用户使用的操作系统（尽管像 Firefox OS 这样的网络为中心的操作系统没有给出），但是给出的形式却多种多样。在用户代理的注释部分以夹在两个分号之间的固定字符串形式给出。对于每一个浏览器，这些字符串是特定的。这些字符串在显示操作系统的同时，也经常显示它的版本和其依赖的硬件信息（32 位还是 64 位，或者是 Mac 平台的 intel/PPC）.

正如所有情形一样，这些字符串在将来也可能会改变，我们应该仅仅与已经发布的浏览器一起使用它们。当新的浏览器版本发布的时候，一份修改脚本以适应新版本的技术调查必须出现。

### 手机，平板或者台式电脑

执行用户代理嗅探的一个最普遍的原因是查明浏览器是在何种硬件设备上运行的。而这么做的目的是对不同类型的设备提供不同的 HTML 页面。

* 从不要假设一款浏览器或者一种渲染引擎仅仅运行在一种类型的设备上。特别是不要对不同种类的浏览器或者渲染引擎采用不同的默认值。
* 从不要使用 OS 标记来定义是否该浏览器运行在手机、平板或者桌面机上。同一种操作系统也许也运行在不止一种类型的设备上（例如，Android 既能在平板上运行也能在手机上运行）。

下面这张表格总结了主要浏览器开发商声明他们的浏览器运行在手机设备上的方式：

| 浏览器 | 规则 | 示例 |
| --- | --- | --- |
| Mozilla (Gecko, Firefox) | 注释中的 **Mobile 或 Tablet 标记** | Mozilla/5.0 (Android; Mobile; rv:13.0) Gecko/13.0 Firefox/13.0 |
| WebKit-based (Android, Safari) | 注释外的**Mobile Safari 标记** | Mozilla/5.0 (Linux; U; Android 4.0.3; de-ch; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30 |
| Blink-based (Chromium, Google Chrome, Opera 15+) | 注释外的**Mobile Safari 标记** | Mozilla/5.0 (Linux; Android 4.4.2); Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Mobile Safari/537.36 OPR/20.0.1396.72047 |
| Presto-based (Opera 12-) | 注释中的**Opera Mobi/xyz 标记**(Opera 12-) | Opera/9.80 (Android 2.3.3; Linux; Opera Mobi/ADR-1111101157; U; es-ES) Presto/2.9.201 Version/11.50 |
| Internet Explorer | 注释中的**IEMobile/xyz 标记** | Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0) |

总之，我们建议检测移动设备的时候，在用户代理字符串中寻找“Mobi”字符串。

**备注：** 如果设备屏幕足够大，它就不会被用“Mobi”标记，你应该提供给用户你的桌面版网页（由于越来越多的桌面设备开始配有触摸屏，作为一个最佳体验，不管怎样，应该在该网页中提供触碰输入）


Resources and URIs - HTTP

Resources and URIs
==================

HTTP 允许浏览器或用户代理与互联网上的不同*资源*进行通信。要做到这一点，需要知道资源的*身份*与*位置*。这两部分的信息用URI来定义。

定位网络资源

URI 与其如何访问网络资源。

数据 URI

一类特殊的 URI：数据 URI。其将数据嵌入标识符。

选择“www”URI 或“非 www”URI

有关是否使用以“www”为前缀的域名的建议。这篇文章解释了相关的结果以及其如何实现。

MIME 类型

MIME 媒体类型定义了资源是什么类型的文档。这篇文章展示了其格式与常用的 Web MIME 类型。

MIME 类型的完整列表

Web 开发人员常用的 MIME 类型列表。

分离身份与位置信息：Alt-Svc 头部

即便是使用了URI来描述身份与位置信息，其仍然是不同的两个概念。有时明确的进行区分非常必要。这篇文章介绍了Alt-Svc。


HTTP 请求范围 - HTTP

HTTP 请求范围
=========

HTTP 协议范围请求允许服务器只发送 HTTP 消息的一部分到客户端。范围请求在传送大的媒体文件，或者与文件下载的断点续传功能搭配使用时非常有用。

检测服务器端是否支持范围请求
--------------

假如在响应中存在 `Accept-Ranges` 首部（并且它的值不为“none”），那么表示该服务器支持范围请求。例如，你可以使用 cURL 发送一个 `HEAD` 请求来进行检测。
```

curl -I http://i.imgur.com/z4d4kWk.jpg

HTTP/1.1 200 OK
...
Accept-Ranges: bytes
Content-Length: 146515
```

在上面的响应中， `Accept-Ranges: bytes` 表示界定范围的单位是 bytes。这里 `Content-Length` 也是有效信息，因为它提供了要检索的图片的完整大小。

如果站点未发送 `Accept-Ranges` 首部，那么它们有可能不支持范围请求。一些站点会明确将其值设置为 "none"，以此来表明不支持。在这种情况下，某些应用的下载管理器会将暂停按钮禁用。
```

curl -I https://www.youtube.com/watch?v=EwTZ2xpQwpA

HTTP/1.1 200 OK
...
Accept-Ranges: none
```

从服务器端请求特定的范围
------------

假如服务器支持范围请求的话，你可以使用 `Range` 首部来生成该类请求。该首部指示服务器应该返回文件的哪一或哪几部分。

### 单一范围

我们可以请求资源的某一部分。这次我们依然用 cURL 来进行测试。"-H" 选项可以在请求中追加一个首部行，在这个例子中，是用 Range 首部来请求图片文件的前 1024 个字节。
```

curl http://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023"
```

这样生成的请求如下：
```

GET /z4d4kWk.jpg HTTP/1.1
Host: i.imgur.com
Range: bytes=0-1023
```

服务器端会返回状态码为 `206` `Partial Content` 的响应：
```

HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/146515
Content-Length: 1024
...
(binary content)
```

在这里，`Content-Length` 首部现在用来表示先前请求范围的大小（而不是整张图片的大小）。`Content-Range` 响应首部则表示这一部分内容在整个资源中所处的位置。

### 多重范围

Range 头部也支持一次请求文档的多个部分。请求范围用一个逗号分隔开。
```

curl http://www.example.com -i -H "Range: bytes=0-50, 100-150"
```

服务器返回 206 Partial Content 状态码和 Content-Type：multipart/byteranges; boundary=3d6b6a416f9b5 头部，Content-Type：multipart/byteranges 表示这个响应有多个 byterange。每一部分 byterange 都有他自己的 Content-type 头部和 Content-Range，并且使用 boundary 参数对 body 进行划分。
```

HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5
Content-Length: 282

--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 0-50/1270

<!doctype html>
<html>
<head>
    <title>Example Do
--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 100-150/1270

eta http-equiv="Content-type" content="text/html; c
--3d6b6a416f9b5--
```

### 条件式范围请求

当（中断之后）重新开始请求更多资源片段的时候，必须确保自从上一个片段被接收之后该资源没有进行过修改。

The `If-Range` 请求首部可以用来生成条件式范围请求：假如条件满足的话，条件请求就会生效，服务器会返回状态码为 `206` `Partial` 的响应，以及相应的消息主体。假如条件未能得到满足，那么就会返回状态码为 `200` `OK` 的响应，同时返回整个资源。该首部可以与 `Last-Modified` 验证器或者 `ETag` 一起使用，但是二者不能同时使用。
```

If-Range: Wed, 21 Oct 2015 07:28:00 GMT
```

范围请求的响应
-------

与范围请求相关的有三种状态：

* 在请求成功的情况下，服务器会返回 `206` `Partial Content` 状态码。
* 在请求的范围越界的情况下（范围值超过了资源的大小），服务器会返回 `416` `Requested Range Not Satisfiable` （请求的范围无法满足）状态码。
* 在不支持范围请求的情况下，服务器会返回 `200` `OK` 状态码。
与分块传输编码的对比
----------

`Transfer-Encoding` 首部允许分块编码，这在数据量很大，并且在请求未能完全处理完成之前无法知晓响应的体积大小的情况下非常有用。服务器会直接把数据发送给客户端而无需进行缓冲或确定响应的精确大小——后者会增加延迟。范围请求与分块传输是兼容的，可以单独或搭配使用。

相关内容
----

* 相关状态码： `200`, `206`, `416`.
* 相关首部： `Accept-Ranges`, `Range`, `Content-Range`, `If-Range`, `Transfer-Encoding`.
* IE 浏览器中的断点续传功能