hehe

hehe 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

hehe 关注了用户 · 9月13日

华猾稽 @slip

学生仔
只在想上的时候上网

关注 9

hehe 赞了文章 · 9月13日

ES6 Module + 自定义元素 初体验

可能是最全最新的 “初体验”。
欸,一位朋友和我说,作者啊,你写这么多,指不定也没人看,一点意思都没有,还不如省下这点时间坐下来 喝 杯 奶 茶 ,对吧。我气不过啊,发出来,就想看看有多少人,看了这篇文章,完事了还能给我点个 👍 ,气一气我这炫富的朋友。

新一代 ECMAScript 和 Web Components 标准已经发展得非常全面,现代浏览器支持也十分广泛。纯原生代码也可以写出 Vue 单文件组件的类似效果。

<game-character data-name="El Primo" data-image="//example.dev/el-primo.png"></game-character>
import GameCharacter from '//example.dev/xxx.js';
GameCharacter.register('game-character');

Module + Components Example - CodePen (一定要点开哦!)

元素,是组件的身躯

HTML 虽是构建网页的基础技术,但不可否认地,其原生标签数和可扩展性相当有限。开发者总总缺乏一种自动将 JS 与 HTML 相关联的方式。

这一切持续到自定义元素的出现。自定义元素MDN)是 HTML 现代化进程中的里程碑,它将指定 HTML 元素与 ES Class 相绑定,增强了 Web 开发结构与功能的联系,是现代仿生学思想(人们研究生物体的结构与功能工作的原理,创造先进技术) 在前端领域的重要突破,使得易于复用的 HTML 元素的创建和扩展格外简单。总总认同,通过原生自定义元素创建组件简单直接,符合直觉,代码量小

规范的命名

为了区别原生元素、帮助浏览器解析,HTML 规范明确而详细地制定了自定义元素的名称应遵循的规定

  • 自定义元素的名称必须包含连字符(-)。
  • 以字母开头。
  • 不包含大写字母。
  • 不能是以下中的一个:

    • <annotation-xml>
    • <color-profile>
    • <font-face>
    • <font-face-src>
    • <font-face-uri>
    • <font-face-format>
    • <font-face-name>
    • <missing-glyph>

规范以 <math-α><emotion-😍> 为例,只要满足上述要求,名称都可以使用

另外,一个符合命名要求的自定义元素,即使未被注册,也不会被视为 HTMLUnknownElement 的实例。

// 'game-character' 符合命名标准。
document.createElement('game-character') instanceof HTMLUnknownElement
// false

// 'character' 不符合命名标准。
document.createElement('character') instanceof HTMLUnknownElement
// true

简单清晰的关系定义

自定义元素的卖点,基本就在自定义类。以本例为例,所有的 <game-character> 都将会是 GameCharacter 的实例。

class GameCharacter extends HTMLElement {
  // constructor 不继承且为空、或继承但只有一个 super() 时,可以忽略。
  constructor() {
    super();
  }
}

自定义元素与类的关系,可以使用 customElements.defineMDN)搭建,这个 API 同时还会完成该元素在 DOM 中的注册

customElements.define('game-character', GameCharacter);

自定义元素完成了注册流程, new 操作符就掌握了创造该自定义元素实例的能力。

// 注册后,
const ele = document.createElement('game-character');
// 功能上等价于:
const ele = new GameCharacter();

可复用的结构

Shadow DOMMDN)是 HTML 和 CSS 特异性的原生解决方案,书写局部作用域的元素 id 和 CSS 样式由此摆脱了对第三方框架的依赖。它是 Web Components 的关键一环;与自定义元素和 ES6 Module 一起,组成了现代 Web 开发模块化的原生 “硬件” 基础。

创建 Shadow DOM 十分容易,在选定的元素上 attachShadowMDN)即可创造该元素的 “影子根”。该影子根同时也是此方法的返回值,可以通过元素的 shadowRootMDN)属性访问。每个元素只能有一个影子根,调用多次 attachShadow 会报一个 NotSupportedError 错误。影子根拥有正常元素的大多数 API 。

由文档 API 动态构建

class GameCharacter extends HTMLElement {
  imgEle;

  nameEle;

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const imgEle = document.createElement('img');
    imgEle.title = 'Avatar Picture Unset.';
    imgEle.alt = 'Unknown Person.';
    shadowRoot.appendChild(imgEle);
    this.imgEle = imgEle;

    const nameEle = document.createElement('span');
    nameEle.textContent = 'Unknown Person.';
    shadowRoot.appendChild(nameEle);
    this.nameEle = nameEle;
  }

  // 响应 HTML 属性变更
  attributeChangedCallback(name, oldValue, newValue) {
    switch (name) {
      case 'data-image':
        this.imgEle.src = newValue;
        break;
      case 'data-name':
        this.nameEle.textContent = newValue;
        this.imgEle.title = newValue;
        this.imgEle.alt = newValue;
        break;
      default:
        break;
    }
  }
}

简单的影子树结构可以直接使用示例的方法,逐个生成。

由模板字符串静态构建

复杂的影子树结构可以创建 <template>MDN)元素,通过接受换行的反引号模板字符串,储存作为模板。

模板
// 元素结构
const template = document.createElement('template');
template.innerHTML = `
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;
模板结构的导入

模板元素 <template>contentMDN)属性返回模板元素子元素组成的 DocumentFragmentMDN)。

class GameCharacter extends HTMLElement {
  // ...

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const children = template.content.cloneNode(true);
    shadowRoot.appendChild(children);

    this.imgEle = shadowRoot.querySelector('img');
    this.nameEle = shadowRoot.querySelector('span');
  }

  // ...
}

储存于 <template> 元素中的内容会被浏览器解析,但不会被渲染。

与世隔绝的样式设计

Shadow DOM 提供的 CSS 作用域保证了影子树中的样式规则不会泄漏,外部样式也不会渗入,这带来了更简单高效的 CSS 选择器,更通用易读的 class 类名称,命名冲突之忧被打入冷宫。给予初涉模块化的开发者入身世外桃源般的豁然开朗,犹如懵懂少年与 Hello World 的悄然初见。项目越做越大,代码又臭又长,作者今日竟能由此返璞归真,不由得破涕为笑。

:hostMDN)选择器负责选择影子根的宿主。限定宿主需要满足的条件时,需要将相关选择器放到 :host()MDN选择器内部:host-content()MDN)选择器用于限定宿主的父元素需要满足的条件。

示例

在这个例子中,鼠标悬浮在自定义元素上时会改变边框颜色和图像大小、父元素包含 .black 类时自定义元素会更换为黑色背景:

:host {
  background: #fff;
  color: #333;
  border-width: 2px;
  border-style: solid;
  border-color: #2196f3;
  transition: border-color 240ms ease-in-out;
}

:host-content(.black) {
  background: #333;
  color: #fff;
}

:host(:hover) {
  border-color: #ffc107;
}

/* 无效规则 */
:host:hover {
  /* 随便什么都无效 */
}

img {
  transition: transform 240ms ease-in-out;
}

:host(:hover) img {
  transform: scale(1.1);
}
如何应用

样式设计可以直接写在 JS 文件中,就像由模板字符串静态构建元素 HTML 结构时做的一样,把 CSS 与 HTML <template> 放在一起。也可以选择独立成为单个文件,在脚本中记录或获取该文件的 URL,在自定义元素的样式类子元素中导入。

静态记录样式
/* 包含在 JS 文件中 */
const styleText = `
  :host {
    ...
  }
  ...
`;

class GameCharacter extends HTMLElement {
  //...

  constructor() {
    // ...

    const styleEle = document.createElement('style');
    styleEle.textContent = styleText;
    shadowRoot.appendChild(styleEle);

    // ...
  }

  // ...
}

下面的包含在 <template> 元素中的做法是较为推荐的,可以通过后文 “import.meta” 和 “模板字符串” 两节获得更多信息。

/* 包含在 JS 文件中 2,使用 <template> 元素 */
const template = document.createElement('template');
template.innerHTML = `
  <style>
  :host {
    /* ... */
  }
  /* ... */
  </style>
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;

class GameCharacter extends HTMLElement {
  // ...

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const children = template.content.cloneNode(true);
    shadowRoot.appendChild(children);

    // ...
  }

  // ...
}
导入外部样式表
/* 外部样式载入 */
const styleURL = '//example.dev/index.css';

class GameCharacter extends HTMLElement {
  //...

  constructor() {
    // ...

    const styleEle = document.createElement('style');
    styleEle.textContent = `@import url(${styleURL})`;
    shadowRoot.appendChild(styleEle);

    // ...
  }

  // ...
}
/* 外部样式载入 2,使用 <link> 元素 */
  constructor() {
    // ...

    const styleEle = document.createElement('link');
    styleEle.setAttribute('rel', 'stylesheet');
    styleEle.setAttribute('href', styleURL);
    shadowRoot.appendChild(styleEle);

    // ...
  }

回调函数

自定义元素涉及的回调函数名、对应的解释补充点如下:

名称响应
constructor元素实例被创建或升级。
connectedCallback元素被注入 DOM 中。
disconnectedCallback元素被移出 DOM 。
attributeChangedCallback元素指定的 HTML 属性被更改。
adoptedCallback元素被移到新文档。

constructor

HTML Standard 对自定义元素所对应类的 constructor 函数的要求,如下:

  1. 每一个自定义元素对应类的 constructor 函数内部必须首先无参数调用一次 super
  2. 除了直接 returnreturn this ,不能使用 return
  3. 不能调用 document.writedocument.open 方法。
  4. 不能获取或变更任何子元素和任何 HTML 属性。
  5. 一般来说,尽可能把工作交给 connectedCallback(注入 DOM 回调)。
  6. 一般来说, constructor 函数的作用是设置默认值、事件侦听器和可能需要的 shadow root 。

connectedCallback & disconnectedCallback

虽然字面上,元素注入和移除回调的触发意味着元素在或不在 DOM 状态的切换,但值得注意的是,这两个回调都是不阻塞当前队列的同步函数。这意味着,如果你将一个自定义元素添加入 DOM 后又快速移除,那么在 connectedCallback 触发时该元素可能已经不在 DOM 里了

attributeChangedCallback

该回调会接受三个字符串形式的参数,第一个是被改变的 HTML 属性名,第二个是被改变前的旧值,第三个是改变后的新值。该回调只会侦听类的 observedAttributes静态属性返回的数组中包含的 HTML 属性名。例如,

class GameCharacter extends HTMLElement {
  // ...
  static observedAttributes = ['data-image', 'data-name'];

  attributeChangedCallback(name) {
    // 永远为 true 。
    this.constructor.observedAttributes.includes(name);
  }
  // ...
}

目前,网上大多数教程,包括 HTML 规范、MDN 文档中的示例,都将 observedAttributes 用一个静态 getter 返回,

  // ...
  static get observedAttributes() {
    return ['data-image', 'data-name'];
  }
  // ...

这是 ES6 早期 class 内部不允许声明属性、只能包含函数时的做法,现在没有理由再这么做。

adoptedCallback

当元素被移入新 document 对象时调用。例如,存在一个自定义元素 ele ,调用 document.adoptNode(ele) 将触发 ele 元素的 adoptedCallback 回调。

注册系列回调

注册流程完成后,每个已经存在<game-character>触发一系列实例中的回调函数,该过程也称为 “升级(upgrade)”,按顺序主要包含:

  1. 跑一次 constructor
  2. 以元素每个存在的 HTML 属性为相关参数,各跑一次 attributeChangedCallback 属性变更回调。
  3. 若已经存在于 DOM ,跑一次 connectedCallback 注入 DOM 回调。

模块,是组件的灵魂

在 ES6 出现之前,服务器和浏览器端的模块化主要由第三方框架 CommonJS 和 AMD 实现。ES6 提供了更为简单直接的解决方案,闭合了原生 HTML、CSS、JS 模块化开发的最后一环

将自定义元素与 ES6 Module 结合,首先可以解决组件脚本分离问题,毕竟 ES6 Module 就是为了将模块代码独立成文件而设计的。在脚本分离后,🤔 如何利用 Module 的特性,解决组件相关的文件和变量依赖,是本节讨论的主题。

import.meta

ES6 中,被 import 导入的项目可以访问一个 import.metaMDN)对象,在当前 HTML 规范中其含且仅含一个 url 属性,以字符串的形式传递模块脚本文件自身的路径。可以简单封装这个路径,创造一个取得同模块下其它文件路径或内容的模块。

取得文件路径
/* get-sibling-url/main.js */
const getSiblingURL = (fileName, referencePath) => {
  const folderPath = referencePath.substr(0, referencePath.lastIndexOf('/'));
  return `${folderPath}/${fileName}`;
};

export default getSiblingURL;
取得文件内容(Promise)
/* get-sibling-file/main.js */
const getSiblingFile = (fileName, referencePath) => {
  const folderPath = referencePath.substr(0, referencePath.lastIndexOf('/'));
  const filePath = `${folderPath}/${fileName}`;
  return fetch(filePath);
};

export default getSiblingFile;

CSS 样式表要不要从模块 JS 中独立出来呢?

作者的建议是,不需要。现代代码编辑器已经可以做到多窗口编辑同一个文件的不同部分,而自定义元素模块的 HTML、CSS、JS 之间的联系十分紧密。这里摘录 Vue 文档 “单文件组件” 一章中的部分内容:

怎么看待关注点分离?
一个重要的事情值得注意,关注点分离不等于文件类型分离。在现代 UI 开发中,我们已经发现相比于把代码库分离成三个大的层次并将其相互交织起来,把它们划分为松散耦合的组件再将其组合起来更合理一些。在一个组件里,其模板、逻辑和样式是内部耦合的,并且把他们搭配在一起实际上使得组件更加内聚且更可维护。

以字符串形式在 JS 中保存 CSS 和 HTML 具有一定优化空间,建议阅读后文 “模板字符串” 一节。

示例

自定义元素 <hello-world> ,用于展示 settings.json 文件内 greeting 属性的字符,则模块配置参数 settings.json 、模块脚本 main.js 和所在的目录结构可以为:

模块配置参数
{
  "greeting": "Are you OK?"
}
模块脚本
/* hello-world/main.js */
import getSiblingFile from '../get-sibling-file';

class HelloWorld extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    const selfPath = import.meta.url;
    getSiblingFile('settings.json', selfPath)
    .then((r) => r.json())
    .then((settings) => {
      shadowRoot.textContent = settings.greeting;
    })
    .catch(console.log);
  }
}

export default HelloWorld;
目录结构
├── 📁src
│   ├── 📁modules
│   │   ├── 📁hello-world
│   │   │   ├── 📜settings.json
│   │   │   ├── 📑main.js
│   │   │   ├── 📰README.md
│   │   │   └── 📜package.json
│   │   └── 📁get-sibling-file
│   │       ├── 📑main.js
│   │       ├── 📰README.md
│   │       └── 📜package.json
│   ├── 📁images
│   │   ├── 🌴banner.png
│   │   └── 🌴icon.png
│   ├── 📁scripts
│   │   └── 📑index.js
│   └── 📄robots.txt
├── 📰CHANGELOG.md
├── ✍LICENSE
├── 📰README.md
├── 📜package.json

主脚本 index.js 将能够在 import 导入 HelloWorld 类之后,通过 customElements.define('hello-world', HelloWorld) 建立 <hello-world> 与该类的联系、完成元素在 DOM 中的注册。

注册回调 vs 注册函数

如果需要在注册时执行函数, <hello-world> 可以通过 customElements.whenDefinedMDN)侦听自己在 DOM 中的注册事件。该函数接受一个参数作为所侦听的目标元素名称,返回一个 Promise ,根据 whenDefined 被调用时目标元素名称的注册情况,立即或稍后在注册时以 undefined 为值进行 resolve 。但是,

  1. 在自定义元素模块内部静态储存自身元素名称没有任何意义,还降低了模块的灵活性、可能产生主脚本无法解决的命名冲突,不符合模块化的宗旨;
  2. 如果该自定义元素内部存在其它的自定义元素,依赖 whenDefined 会使自定义元素的注册从外元素到内元素进行,违背直觉,逻辑上更加导致性能损耗;
  3. customElements 系列函数无法传参,限制了自定义元素模块与主脚本的信息传递。

哎呀这可难死作者了。怎么办呢?想来想去终于发现通过创建模块内定义的具有一定规范的 “注册函数” 替换注册回调,以上所有问题就都有了解法:

  1. 主脚本可以向注册函数传参,可以控制该自定义元素及其子自定义元素的名称,防止命名冲突
  2. 包含其它自定义元素的自定义元素组件,可以在自身的注册函数被调用时首先调用子自定义元素的注册函数,创造从内元素到外元素的注册链。

示例

在下面的示例中,父自定义元素模块会接受指定的元素名称,在注册自身前先调用子自定义元素的注册函数

父自定义元素模块
/* modules/parent-ele/main.js */
import ChildEle from '../child-ele';

// 默认名称。
const elementNames = {
  ParentEle: 'parent-ele',
  ChildEle: 'child-ele',
};

class ParentEle {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    const child = new ChildEle();
    shadowRoot.appendChild(child);
  }

  static register(nameSettings = {}) {
    Object.assign(elementNames, nameSettings);
    // 先调用子自定义元素的注册函数,
    ChildEle.register(elementNames.ChildEle);
    // 再在 DOM 中注册自身。
    customElements.define(elementNames.ParentEle, ParentEle);
  }
}

export default ParentEle;
子自定义元素模块

子自定义元素模块接受指定的元素名称,以指定名称注册自身。

/* modules/child-ele/main.js */

// 默认名称。
const elementName = 'spider-man';

class ChildEle {
  static register(name) {
    if (name) elementName = name;
    customElements.define(elementName, this);
  }
}

export default ChildEle;
主脚本

主脚本实际控制着导入的自定义元素模块的各元素的名称。

/* scripts/main.js */
import ParentEle from '../modules/parent-ele';
ParentEle.register({
  ParentEle: 'ben-ele',
  ChildEle: 'peter-ele',
});

ParentEle 会以 <ben-ele> 身份, ChildEle 会以 <peter-ele> 身份完成 DOM 注册流程。

如果,

  • 主脚本无参数调用注册函数,ParentEle 将会以 <parent-ele> 身份、 ChildEle<child-ele> 身份完成 DOM 注册流程。
  • 主脚本无参数调用注册函数且导入的是 ChildEle ,则 ChildEle 将会以 <spider-man> 身份完成 DOM 注册流程。

可以同理嵌套多层自定义元素模块。

法术,这是法术

不看到这你就输了。

FOUC 全解

不论是将 CSS 样式包含在 JS 文件中,还是设定 <style>@import url()</style><link> 从外部载入样式,自定义元素都有可能出现 FOUC(Flash Of Unstyled Content,浏览器样式闪烁)现象,极大地影响用户体验。开发者似乎只能选择放弃 Shadow DOM、原生 CSS 作用域带来的巨大便利,或是期望用户接受 FOUC 造成的视觉侵犯。开发实践中的痛苦和悲伤,带走了前端码农对新特性的好奇与憧憬,反噬着 Web Components 的发展,自定义元素的前程在 FOUC 频频闪电的笼罩下,若昏黑黯淡,分崩离析。

针对自定义元素,FOUC 分为两种情况。

既有元素定义闪烁

回调函数” 一节中提到,DOM 中已有的自定义元素所关联的类,其相关函数会在调用 customElements.define之后,触发。Web Components 标准在设计之初就没有准备像 ES Module 一样支持静态分析。自定义元素关联的类是动态选择的,因此浏览器不可能做到在解析 DOM 时就执行自定义元素对应的类的相关函数。

那么在 customElements.define 指定的类的 constructor 执行时生成 Shadow DOM,势必导致一次元素重绘。

针对这种元素闪烁,常见的解法是使用 :not(:defined) 选择器隐藏未定义的元素,比如将以下代码加到主文档样式中。

game-character:not(:defined) {
  visibility: hidden;
}

但对于主文档流中(positionstaticrelative)的自定义元素(MDN),往往还需要在 DOM 树加载之前设定好定义后的宽高,以免定义后影响主文档流中的其它元素,造成一定闪烁。然而,在 DOM 树加载时计算宽高不符合直觉,对于宽高不定的自定义元素几乎不现实。在 2019 年 9 月曾经有过一个提案,致力于解决这个 “不现实” 的问题,但后来因故废弃[注],目前还未有新提案被提出。

可行的办法之一,是使用过渡动画减轻视觉上的不适感。

/* 暂不需要,本例中默认宽高为 0 */
game-character:not(:defined) {
  /* visibility: hidden; */
}
class GameCharacter extends HTMLElement {
  //...

  connectedCallback() {
    // 默认宽高为 0 。
    Object.assign(this.style, {
      width: '0',
      height: '0',
      opacity: '0',
      overflow: 'hidden',
      transition: 'all 240ms ease-in-out',
    });
    // 过渡到内容宽高、重置透明度。
    Object.assign(this.style, {
      // 计算内容宽高时会导致元素重绘,因此此处不会造成前一处设置宽高为零的语句失效。
      width: `${this.scrollWidth}px`,
      height: `${this.scrollHeight}px`,
      opacity: '',
    });
    // 过渡后重置变更。
    this.addEventListener('transitionend', () => {
      Object.assign(this.style, {
        width: '',
        height: '',
        overflow: '',
        transition: '',
      });
    }, { once: true });
  }

  // ...
}
[注] 原提案参阅 Mitigating flash of unstyled content with custom elements
有关提案废弃原因请自行在 2020 Spring Virtual F2F · Issue #855 · w3c/webcomponents 中检索关键词 “FOUC”。

外部样式表载入闪烁

与存在于主文档流时的行为不同,样式类元素 <style><link> 在 Shadow DOM 中不会阻塞渲染,自定义元素即使在外联样式已被缓存的情况下,也可能会出现一次闪烁。动态添加自定义元素时,添加本身造成一次闪烁,外联样式载入又造成一次闪烁。

因此,即使从这个角度,在 Shadow DOM 中也不建议导入外部样式表。

如果一定要从外部导入,同时不产生额外的 FOUC 现象,那么可以先获取外部 CSS 文件,动态记录其文本内容为 JS 变量。在样式文本载入之前,避免元素显示、影响其它元素定位;在载入之后,将该变量作为内部样式应用到已有、后续创建的元素。

解决示例
import getSiblingFile from '../get-sibling-file';
const selfPath = import.meta.url;
const styleInfo = {
  // 外部样式文件名。
  fileName: 'main.css',

  // 初始样式, absolute 不影响主文档流元素定位, hidden 隐藏元素。
  // 不使用 display: none 以避免内部图片等资源不被加载。
  defaultText: `
    :host {
      position: absolute;
      visibility: hidden;
    }
  `,

  // 储存外部样式的变量。
  text: '',

  // 等待外部样式加载的实例。
  applyQueue: [],

  // 加载外部样式。
  load() {
    getSiblingFile(this.fileName, selfPath)
    .then((r) => r.text())
    .then((text) => {
      this.text = text;
      // 修改需要载入样式的实例。
      this.applyQueue.forEach((ele) => {
        const { styleEle } = ele;
        styleEle.textContent = text;
      });
    }).catch((error) => {
      throw error;
    });
  },
};

// 加载。
styleInfo.load();

class OutsideLover extends HTMLElement {
  styleEle = null;

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    const styleEle = document.createElement('style');
    this.styleEle = styleEle;
    // 根据加载完成与否,
    if (!styleInfo.text) {
      // 使用默认样式,置入等待队列。
      styleEle.textContent = styleInfo.defaultText;
      styleInfo.applyQueue.push(this);
    } else {
      // 使用加载完成的样式。
      styleEle.textContent = styleInfo.text;
    }
    shadowRoot.appendChild(styleEle);

    // Something else.
  }
}

作者已经将其做成了一个简单的插件。在导入之后,将继承的 HTMLElement 改为 StylingAdvancedHTMLElement ,省去 attachShadow ,把要导入的外部样式文件名记为静态属性 styleFileUrl 即可。

示例中的代码就可以简化成这样:

import StylingAdvancedHTMLElement from '//raw.githubusercontent.com/PaperFlu/StylingAdvancedHTMLElement/master/export.js';
// 获得 Promise 改为获得 URL 。
import getSiblingURL from '../get-sibling-url';
const selfPath = import.meta.url;

class OutsideLover extends HTMLElement {
  styleFileUrl = getSiblingURL('main.css', selfPath);

  constructor() {
    super();
    // 不再调用 attachShadow ,直接引用 shadowRoot 。
    const { shadowRoot } = this;

    // Something else.
  }
}

模板字符串

Vue 设计者在文档中明确地指出使用字符串在 JS 中保存组件的 HTML 结构的缺点,也是字符串保存其它所有语言文本的共同缺点:一是失去了编辑器语法高亮,二是换行需要使用 \不同于 Vue 中的 “字符串模板”,ES6 的模板字符串MDN)接受换行,在大多数编辑器,以及 Github、NPM 等处的 Markdown 代码块内,可以通过添加标签得到语法高亮

// ES6 模板字符串可以通过这种方式在大多数编辑器中获得正确的语法高亮
// 并被 Prettier 一类的格式化工具正确处理。
const html = String.raw;
const someHTML = html`
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;

在模板字符串前添加标签是存在于 ES6 标准中的合法操作,标签将被视为函数,以一定规则解析该模板字符串(MDN)。类似 Prettier 的格式化工具能够在标签的辅助下正确格式化模板字符串。

NPM 上有一个包,common-tags,设计了 ES6+ 常用的标签函数。安装后,可以这样:

import { html } from 'common-tags';
const template = document.createElement('template');
template.innerHTML = html`
  <style>
    :host {
      background: #fff;
      color: #333;
      border-width: 2px;
      border-style: solid;
      border-color: #2196f3;
      transition: border-color 240ms ease-in-out;
    }
  </style>
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;

class GameCharacter extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const children = template.content.cloneNode(true);
    shadowRoot.appendChild(children);

  }
}

common-tags 的设计不仅仅是为开启编辑器语法高亮,它还可以实现多余空格消除、换行调整、HTML 安全转义等。(官方文档

语法高亮示例

tagged-template-example

扩展原生元素

如果你不想设计一个全新的自定义元素,只是对现有元素不满意,怎么办?用于联系自定义元素名与类之间的关系的 customElements.define 还接受第三个参数,用于在需要时标记要扩展的原生元素名。对于这样的自定义元素,我们叫它:自定义原生元素

同时,自定义原生元素的对应类继承的可能就不是 HTMLElement 了,它需要继承被扩展的原生元素的 DOM 接口。比如我们需要扩展 <button> ,我们就需要继承 HTMLButtonElement ,如果我们需要扩展 <img> ,我们的类就需要继承 HTMLImageElement

到了这里,我的右手就不高兴了,它认为既然都继承了接口,注册的时候,直接忽略被扩展的原生元素的名字,不是很美观、很简洁吗?因此不愿意帮我继续码字。哎呀我好说歹说,它同意帮我去搜索这个问题,最后发现,不同的原生元素对应的 DOM 接口可能会相同。就像 <blockquote><q> ,接口都是 HTMLQuoteElement ,浏览器怎么知道你想扩展的是哪一个。HTML 规范中有具体的元素接口对应表

这下我的右手满意了,它同意帮我继续完成下面的文章,做一个能够轻松输出自己 DataURL<img>。这样我们在用纯文本传递图片的时候,就方便多了。

示例

在本地文件系统测试本例可能会出现 CORS 相关错误。(file://
// 定义一个扩展 <img> 的自定义元素
class TextImg extends HTMLImageElement {
  get dataURL() {
    const canvas = document.createElement('canvas');
    canvas.width = this.width;
    canvas.height = this.height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(this, 0, 0);

    return canvas.toDataURL('image/x-icon');
  }
}
customElements.define('text-img', TextImg, { extends: 'img' });

静态和动态创建

自定义原生元素的标签名不变,但存在一个 is 属性,指示其对应的自定义元素名,从而由此绑定对应的类。

在 HTML 中创建
<img is="text-img" data-original="example.png" alt="example">
在 JS 中创建
// 可以这样
const ele = document.createElement('img', { is: 'text-img' });
// 也可以这样
const ele = new TextImg();

ele.src = 'example.png';
ele.alt = 'example';

矛盾?错误?

可是看到这里,我的读者又不满意了,他们通过广泛的学习,发现我的扩展方法和谷歌 Web Fundamentals 上的 Custom Elements v1: Reusable Web Components 教程 不 一 样!教程里的示例二 明明写着:

Example - extending <img>:

customElements.define('bigger-img', class extends Image {
  // Give img default size if users don't specify.
  constructor(width=50, height=50) {
    super(width * 10, height * 10);
  }
}, {extends: 'img'});

Users declare this component as:

<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">

or create an instance in JavaScript:

const BiggerImage = customElements.get('bigger-img');
const image = new BiggerImage(15, 20); // pass constructor values like so.
console.assert(image.width === 150);
console.assert(image.height === 200);

继承的是 Image 呢!

所以也是没有办法,又仔细阅读了这篇文章。发现这篇文章前后似乎是存在矛盾的,示例之前明明也点明了 <img> 的扩展要继承 HTMLImageElement

To extend an element, you'll need to create a class definition that inherits from the correct DOM interface. For example, a custom element that extends <button> needs to inherit from HTMLButtonElement instead of HTMLElement. Similarly, an element that extends <img> needs to extend HTMLImageElement.

我的右手因为之前的误解,决定要补偿我,使出了九牛二虎之力,搜到这个示例至少从 2016 年 12 月 9 日开始,就再没有更新过了,而那个时候自定义原生元素还没有被浏览器支持[注]。即使到了现在,自定义原生元素的需求还很小,因此右手怀疑文章年久失更,在 GitHub 上开了个 issue 提出这个问题,只是目前还没有人理。

[注] 参阅 javascript - How to extend default HTML elements as a "customized built-in element" using Web Components? - Stack Overflow 。讨论点不同,但引用了同一文段。

现在你们都满意了,那我们就继续吧。

注意点

  • 使用 JS 编程式创建的元素的 is 属性在序列化时显示,但不会在 DOM 中显示。
  • 所有原生 <img> 元素的特性都可用于自定义 <img> 元素。比如 src 显示图片, onload 事件等。
  • 自定义原生元素只能扩展规范中包含的 HTML 元素,元素接口为 HTMLUnknownElement 的旧有元素,如 <bgsound><blink><isindex><keygen><multicol><nextid><spacer> ,不能被扩展。

参阅

自定义原生元素在 HTML 规范中的示例:Creating a customized built-in element - HTML Standard

结语

真心感谢有人有耐心能看到最后,我太感动了 😍 !!点个👍、收个藏再走吧!

资料来源

查看原文

赞 13 收藏 9 评论 0

hehe 收藏了文章 · 9月13日

ES6 Module + 自定义元素 初体验

可能是最全最新的 “初体验”。
欸,一位朋友和我说,作者啊,你写这么多,指不定也没人看,一点意思都没有,还不如省下这点时间坐下来 喝 杯 奶 茶 ,对吧。我气不过啊,发出来,就想看看有多少人,看了这篇文章,完事了还能给我点个 👍 ,气一气我这炫富的朋友。

新一代 ECMAScript 和 Web Components 标准已经发展得非常全面,现代浏览器支持也十分广泛。纯原生代码也可以写出 Vue 单文件组件的类似效果。

<game-character data-name="El Primo" data-image="//example.dev/el-primo.png"></game-character>
import GameCharacter from '//example.dev/xxx.js';
GameCharacter.register('game-character');

Module + Components Example - CodePen (一定要点开哦!)

元素,是组件的身躯

HTML 虽是构建网页的基础技术,但不可否认地,其原生标签数和可扩展性相当有限。开发者总总缺乏一种自动将 JS 与 HTML 相关联的方式。

这一切持续到自定义元素的出现。自定义元素MDN)是 HTML 现代化进程中的里程碑,它将指定 HTML 元素与 ES Class 相绑定,增强了 Web 开发结构与功能的联系,是现代仿生学思想(人们研究生物体的结构与功能工作的原理,创造先进技术) 在前端领域的重要突破,使得易于复用的 HTML 元素的创建和扩展格外简单。总总认同,通过原生自定义元素创建组件简单直接,符合直觉,代码量小

规范的命名

为了区别原生元素、帮助浏览器解析,HTML 规范明确而详细地制定了自定义元素的名称应遵循的规定

  • 自定义元素的名称必须包含连字符(-)。
  • 以字母开头。
  • 不包含大写字母。
  • 不能是以下中的一个:

    • <annotation-xml>
    • <color-profile>
    • <font-face>
    • <font-face-src>
    • <font-face-uri>
    • <font-face-format>
    • <font-face-name>
    • <missing-glyph>

规范以 <math-α><emotion-😍> 为例,只要满足上述要求,名称都可以使用

另外,一个符合命名要求的自定义元素,即使未被注册,也不会被视为 HTMLUnknownElement 的实例。

// 'game-character' 符合命名标准。
document.createElement('game-character') instanceof HTMLUnknownElement
// false

// 'character' 不符合命名标准。
document.createElement('character') instanceof HTMLUnknownElement
// true

简单清晰的关系定义

自定义元素的卖点,基本就在自定义类。以本例为例,所有的 <game-character> 都将会是 GameCharacter 的实例。

class GameCharacter extends HTMLElement {
  // constructor 不继承且为空、或继承但只有一个 super() 时,可以忽略。
  constructor() {
    super();
  }
}

自定义元素与类的关系,可以使用 customElements.defineMDN)搭建,这个 API 同时还会完成该元素在 DOM 中的注册

customElements.define('game-character', GameCharacter);

自定义元素完成了注册流程, new 操作符就掌握了创造该自定义元素实例的能力。

// 注册后,
const ele = document.createElement('game-character');
// 功能上等价于:
const ele = new GameCharacter();

可复用的结构

Shadow DOMMDN)是 HTML 和 CSS 特异性的原生解决方案,书写局部作用域的元素 id 和 CSS 样式由此摆脱了对第三方框架的依赖。它是 Web Components 的关键一环;与自定义元素和 ES6 Module 一起,组成了现代 Web 开发模块化的原生 “硬件” 基础。

创建 Shadow DOM 十分容易,在选定的元素上 attachShadowMDN)即可创造该元素的 “影子根”。该影子根同时也是此方法的返回值,可以通过元素的 shadowRootMDN)属性访问。每个元素只能有一个影子根,调用多次 attachShadow 会报一个 NotSupportedError 错误。影子根拥有正常元素的大多数 API 。

由文档 API 动态构建

class GameCharacter extends HTMLElement {
  imgEle;

  nameEle;

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const imgEle = document.createElement('img');
    imgEle.title = 'Avatar Picture Unset.';
    imgEle.alt = 'Unknown Person.';
    shadowRoot.appendChild(imgEle);
    this.imgEle = imgEle;

    const nameEle = document.createElement('span');
    nameEle.textContent = 'Unknown Person.';
    shadowRoot.appendChild(nameEle);
    this.nameEle = nameEle;
  }

  // 响应 HTML 属性变更
  attributeChangedCallback(name, oldValue, newValue) {
    switch (name) {
      case 'data-image':
        this.imgEle.src = newValue;
        break;
      case 'data-name':
        this.nameEle.textContent = newValue;
        this.imgEle.title = newValue;
        this.imgEle.alt = newValue;
        break;
      default:
        break;
    }
  }
}

简单的影子树结构可以直接使用示例的方法,逐个生成。

由模板字符串静态构建

复杂的影子树结构可以创建 <template>MDN)元素,通过接受换行的反引号模板字符串,储存作为模板。

模板
// 元素结构
const template = document.createElement('template');
template.innerHTML = `
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;
模板结构的导入

模板元素 <template>contentMDN)属性返回模板元素子元素组成的 DocumentFragmentMDN)。

class GameCharacter extends HTMLElement {
  // ...

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const children = template.content.cloneNode(true);
    shadowRoot.appendChild(children);

    this.imgEle = shadowRoot.querySelector('img');
    this.nameEle = shadowRoot.querySelector('span');
  }

  // ...
}

储存于 <template> 元素中的内容会被浏览器解析,但不会被渲染。

与世隔绝的样式设计

Shadow DOM 提供的 CSS 作用域保证了影子树中的样式规则不会泄漏,外部样式也不会渗入,这带来了更简单高效的 CSS 选择器,更通用易读的 class 类名称,命名冲突之忧被打入冷宫。给予初涉模块化的开发者入身世外桃源般的豁然开朗,犹如懵懂少年与 Hello World 的悄然初见。项目越做越大,代码又臭又长,作者今日竟能由此返璞归真,不由得破涕为笑。

:hostMDN)选择器负责选择影子根的宿主。限定宿主需要满足的条件时,需要将相关选择器放到 :host()MDN选择器内部:host-content()MDN)选择器用于限定宿主的父元素需要满足的条件。

示例

在这个例子中,鼠标悬浮在自定义元素上时会改变边框颜色和图像大小、父元素包含 .black 类时自定义元素会更换为黑色背景:

:host {
  background: #fff;
  color: #333;
  border-width: 2px;
  border-style: solid;
  border-color: #2196f3;
  transition: border-color 240ms ease-in-out;
}

:host-content(.black) {
  background: #333;
  color: #fff;
}

:host(:hover) {
  border-color: #ffc107;
}

/* 无效规则 */
:host:hover {
  /* 随便什么都无效 */
}

img {
  transition: transform 240ms ease-in-out;
}

:host(:hover) img {
  transform: scale(1.1);
}
如何应用

样式设计可以直接写在 JS 文件中,就像由模板字符串静态构建元素 HTML 结构时做的一样,把 CSS 与 HTML <template> 放在一起。也可以选择独立成为单个文件,在脚本中记录或获取该文件的 URL,在自定义元素的样式类子元素中导入。

静态记录样式
/* 包含在 JS 文件中 */
const styleText = `
  :host {
    ...
  }
  ...
`;

class GameCharacter extends HTMLElement {
  //...

  constructor() {
    // ...

    const styleEle = document.createElement('style');
    styleEle.textContent = styleText;
    shadowRoot.appendChild(styleEle);

    // ...
  }

  // ...
}

下面的包含在 <template> 元素中的做法是较为推荐的,可以通过后文 “import.meta” 和 “模板字符串” 两节获得更多信息。

/* 包含在 JS 文件中 2,使用 <template> 元素 */
const template = document.createElement('template');
template.innerHTML = `
  <style>
  :host {
    /* ... */
  }
  /* ... */
  </style>
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;

class GameCharacter extends HTMLElement {
  // ...

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const children = template.content.cloneNode(true);
    shadowRoot.appendChild(children);

    // ...
  }

  // ...
}
导入外部样式表
/* 外部样式载入 */
const styleURL = '//example.dev/index.css';

class GameCharacter extends HTMLElement {
  //...

  constructor() {
    // ...

    const styleEle = document.createElement('style');
    styleEle.textContent = `@import url(${styleURL})`;
    shadowRoot.appendChild(styleEle);

    // ...
  }

  // ...
}
/* 外部样式载入 2,使用 <link> 元素 */
  constructor() {
    // ...

    const styleEle = document.createElement('link');
    styleEle.setAttribute('rel', 'stylesheet');
    styleEle.setAttribute('href', styleURL);
    shadowRoot.appendChild(styleEle);

    // ...
  }

回调函数

自定义元素涉及的回调函数名、对应的解释补充点如下:

名称响应
constructor元素实例被创建或升级。
connectedCallback元素被注入 DOM 中。
disconnectedCallback元素被移出 DOM 。
attributeChangedCallback元素指定的 HTML 属性被更改。
adoptedCallback元素被移到新文档。

constructor

HTML Standard 对自定义元素所对应类的 constructor 函数的要求,如下:

  1. 每一个自定义元素对应类的 constructor 函数内部必须首先无参数调用一次 super
  2. 除了直接 returnreturn this ,不能使用 return
  3. 不能调用 document.writedocument.open 方法。
  4. 不能获取或变更任何子元素和任何 HTML 属性。
  5. 一般来说,尽可能把工作交给 connectedCallback(注入 DOM 回调)。
  6. 一般来说, constructor 函数的作用是设置默认值、事件侦听器和可能需要的 shadow root 。

connectedCallback & disconnectedCallback

虽然字面上,元素注入和移除回调的触发意味着元素在或不在 DOM 状态的切换,但值得注意的是,这两个回调都是不阻塞当前队列的同步函数。这意味着,如果你将一个自定义元素添加入 DOM 后又快速移除,那么在 connectedCallback 触发时该元素可能已经不在 DOM 里了

attributeChangedCallback

该回调会接受三个字符串形式的参数,第一个是被改变的 HTML 属性名,第二个是被改变前的旧值,第三个是改变后的新值。该回调只会侦听类的 observedAttributes静态属性返回的数组中包含的 HTML 属性名。例如,

class GameCharacter extends HTMLElement {
  // ...
  static observedAttributes = ['data-image', 'data-name'];

  attributeChangedCallback(name) {
    // 永远为 true 。
    this.constructor.observedAttributes.includes(name);
  }
  // ...
}

目前,网上大多数教程,包括 HTML 规范、MDN 文档中的示例,都将 observedAttributes 用一个静态 getter 返回,

  // ...
  static get observedAttributes() {
    return ['data-image', 'data-name'];
  }
  // ...

这是 ES6 早期 class 内部不允许声明属性、只能包含函数时的做法,现在没有理由再这么做。

adoptedCallback

当元素被移入新 document 对象时调用。例如,存在一个自定义元素 ele ,调用 document.adoptNode(ele) 将触发 ele 元素的 adoptedCallback 回调。

注册系列回调

注册流程完成后,每个已经存在<game-character>触发一系列实例中的回调函数,该过程也称为 “升级(upgrade)”,按顺序主要包含:

  1. 跑一次 constructor
  2. 以元素每个存在的 HTML 属性为相关参数,各跑一次 attributeChangedCallback 属性变更回调。
  3. 若已经存在于 DOM ,跑一次 connectedCallback 注入 DOM 回调。

模块,是组件的灵魂

在 ES6 出现之前,服务器和浏览器端的模块化主要由第三方框架 CommonJS 和 AMD 实现。ES6 提供了更为简单直接的解决方案,闭合了原生 HTML、CSS、JS 模块化开发的最后一环

将自定义元素与 ES6 Module 结合,首先可以解决组件脚本分离问题,毕竟 ES6 Module 就是为了将模块代码独立成文件而设计的。在脚本分离后,🤔 如何利用 Module 的特性,解决组件相关的文件和变量依赖,是本节讨论的主题。

import.meta

ES6 中,被 import 导入的项目可以访问一个 import.metaMDN)对象,在当前 HTML 规范中其含且仅含一个 url 属性,以字符串的形式传递模块脚本文件自身的路径。可以简单封装这个路径,创造一个取得同模块下其它文件路径或内容的模块。

取得文件路径
/* get-sibling-url/main.js */
const getSiblingURL = (fileName, referencePath) => {
  const folderPath = referencePath.substr(0, referencePath.lastIndexOf('/'));
  return `${folderPath}/${fileName}`;
};

export default getSiblingURL;
取得文件内容(Promise)
/* get-sibling-file/main.js */
const getSiblingFile = (fileName, referencePath) => {
  const folderPath = referencePath.substr(0, referencePath.lastIndexOf('/'));
  const filePath = `${folderPath}/${fileName}`;
  return fetch(filePath);
};

export default getSiblingFile;

CSS 样式表要不要从模块 JS 中独立出来呢?

作者的建议是,不需要。现代代码编辑器已经可以做到多窗口编辑同一个文件的不同部分,而自定义元素模块的 HTML、CSS、JS 之间的联系十分紧密。这里摘录 Vue 文档 “单文件组件” 一章中的部分内容:

怎么看待关注点分离?
一个重要的事情值得注意,关注点分离不等于文件类型分离。在现代 UI 开发中,我们已经发现相比于把代码库分离成三个大的层次并将其相互交织起来,把它们划分为松散耦合的组件再将其组合起来更合理一些。在一个组件里,其模板、逻辑和样式是内部耦合的,并且把他们搭配在一起实际上使得组件更加内聚且更可维护。

以字符串形式在 JS 中保存 CSS 和 HTML 具有一定优化空间,建议阅读后文 “模板字符串” 一节。

示例

自定义元素 <hello-world> ,用于展示 settings.json 文件内 greeting 属性的字符,则模块配置参数 settings.json 、模块脚本 main.js 和所在的目录结构可以为:

模块配置参数
{
  "greeting": "Are you OK?"
}
模块脚本
/* hello-world/main.js */
import getSiblingFile from '../get-sibling-file';

class HelloWorld extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    const selfPath = import.meta.url;
    getSiblingFile('settings.json', selfPath)
    .then((r) => r.json())
    .then((settings) => {
      shadowRoot.textContent = settings.greeting;
    })
    .catch(console.log);
  }
}

export default HelloWorld;
目录结构
├── 📁src
│   ├── 📁modules
│   │   ├── 📁hello-world
│   │   │   ├── 📜settings.json
│   │   │   ├── 📑main.js
│   │   │   ├── 📰README.md
│   │   │   └── 📜package.json
│   │   └── 📁get-sibling-file
│   │       ├── 📑main.js
│   │       ├── 📰README.md
│   │       └── 📜package.json
│   ├── 📁images
│   │   ├── 🌴banner.png
│   │   └── 🌴icon.png
│   ├── 📁scripts
│   │   └── 📑index.js
│   └── 📄robots.txt
├── 📰CHANGELOG.md
├── ✍LICENSE
├── 📰README.md
├── 📜package.json

主脚本 index.js 将能够在 import 导入 HelloWorld 类之后,通过 customElements.define('hello-world', HelloWorld) 建立 <hello-world> 与该类的联系、完成元素在 DOM 中的注册。

注册回调 vs 注册函数

如果需要在注册时执行函数, <hello-world> 可以通过 customElements.whenDefinedMDN)侦听自己在 DOM 中的注册事件。该函数接受一个参数作为所侦听的目标元素名称,返回一个 Promise ,根据 whenDefined 被调用时目标元素名称的注册情况,立即或稍后在注册时以 undefined 为值进行 resolve 。但是,

  1. 在自定义元素模块内部静态储存自身元素名称没有任何意义,还降低了模块的灵活性、可能产生主脚本无法解决的命名冲突,不符合模块化的宗旨;
  2. 如果该自定义元素内部存在其它的自定义元素,依赖 whenDefined 会使自定义元素的注册从外元素到内元素进行,违背直觉,逻辑上更加导致性能损耗;
  3. customElements 系列函数无法传参,限制了自定义元素模块与主脚本的信息传递。

哎呀这可难死作者了。怎么办呢?想来想去终于发现通过创建模块内定义的具有一定规范的 “注册函数” 替换注册回调,以上所有问题就都有了解法:

  1. 主脚本可以向注册函数传参,可以控制该自定义元素及其子自定义元素的名称,防止命名冲突
  2. 包含其它自定义元素的自定义元素组件,可以在自身的注册函数被调用时首先调用子自定义元素的注册函数,创造从内元素到外元素的注册链。

示例

在下面的示例中,父自定义元素模块会接受指定的元素名称,在注册自身前先调用子自定义元素的注册函数

父自定义元素模块
/* modules/parent-ele/main.js */
import ChildEle from '../child-ele';

// 默认名称。
const elementNames = {
  ParentEle: 'parent-ele',
  ChildEle: 'child-ele',
};

class ParentEle {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });
    const child = new ChildEle();
    shadowRoot.appendChild(child);
  }

  static register(nameSettings = {}) {
    Object.assign(elementNames, nameSettings);
    // 先调用子自定义元素的注册函数,
    ChildEle.register(elementNames.ChildEle);
    // 再在 DOM 中注册自身。
    customElements.define(elementNames.ParentEle, ParentEle);
  }
}

export default ParentEle;
子自定义元素模块

子自定义元素模块接受指定的元素名称,以指定名称注册自身。

/* modules/child-ele/main.js */

// 默认名称。
const elementName = 'spider-man';

class ChildEle {
  static register(name) {
    if (name) elementName = name;
    customElements.define(elementName, this);
  }
}

export default ChildEle;
主脚本

主脚本实际控制着导入的自定义元素模块的各元素的名称。

/* scripts/main.js */
import ParentEle from '../modules/parent-ele';
ParentEle.register({
  ParentEle: 'ben-ele',
  ChildEle: 'peter-ele',
});

ParentEle 会以 <ben-ele> 身份, ChildEle 会以 <peter-ele> 身份完成 DOM 注册流程。

如果,

  • 主脚本无参数调用注册函数,ParentEle 将会以 <parent-ele> 身份、 ChildEle<child-ele> 身份完成 DOM 注册流程。
  • 主脚本无参数调用注册函数且导入的是 ChildEle ,则 ChildEle 将会以 <spider-man> 身份完成 DOM 注册流程。

可以同理嵌套多层自定义元素模块。

法术,这是法术

不看到这你就输了。

FOUC 全解

不论是将 CSS 样式包含在 JS 文件中,还是设定 <style>@import url()</style><link> 从外部载入样式,自定义元素都有可能出现 FOUC(Flash Of Unstyled Content,浏览器样式闪烁)现象,极大地影响用户体验。开发者似乎只能选择放弃 Shadow DOM、原生 CSS 作用域带来的巨大便利,或是期望用户接受 FOUC 造成的视觉侵犯。开发实践中的痛苦和悲伤,带走了前端码农对新特性的好奇与憧憬,反噬着 Web Components 的发展,自定义元素的前程在 FOUC 频频闪电的笼罩下,若昏黑黯淡,分崩离析。

针对自定义元素,FOUC 分为两种情况。

既有元素定义闪烁

回调函数” 一节中提到,DOM 中已有的自定义元素所关联的类,其相关函数会在调用 customElements.define之后,触发。Web Components 标准在设计之初就没有准备像 ES Module 一样支持静态分析。自定义元素关联的类是动态选择的,因此浏览器不可能做到在解析 DOM 时就执行自定义元素对应的类的相关函数。

那么在 customElements.define 指定的类的 constructor 执行时生成 Shadow DOM,势必导致一次元素重绘。

针对这种元素闪烁,常见的解法是使用 :not(:defined) 选择器隐藏未定义的元素,比如将以下代码加到主文档样式中。

game-character:not(:defined) {
  visibility: hidden;
}

但对于主文档流中(positionstaticrelative)的自定义元素(MDN),往往还需要在 DOM 树加载之前设定好定义后的宽高,以免定义后影响主文档流中的其它元素,造成一定闪烁。然而,在 DOM 树加载时计算宽高不符合直觉,对于宽高不定的自定义元素几乎不现实。在 2019 年 9 月曾经有过一个提案,致力于解决这个 “不现实” 的问题,但后来因故废弃[注],目前还未有新提案被提出。

可行的办法之一,是使用过渡动画减轻视觉上的不适感。

/* 暂不需要,本例中默认宽高为 0 */
game-character:not(:defined) {
  /* visibility: hidden; */
}
class GameCharacter extends HTMLElement {
  //...

  connectedCallback() {
    // 默认宽高为 0 。
    Object.assign(this.style, {
      width: '0',
      height: '0',
      opacity: '0',
      overflow: 'hidden',
      transition: 'all 240ms ease-in-out',
    });
    // 过渡到内容宽高、重置透明度。
    Object.assign(this.style, {
      // 计算内容宽高时会导致元素重绘,因此此处不会造成前一处设置宽高为零的语句失效。
      width: `${this.scrollWidth}px`,
      height: `${this.scrollHeight}px`,
      opacity: '',
    });
    // 过渡后重置变更。
    this.addEventListener('transitionend', () => {
      Object.assign(this.style, {
        width: '',
        height: '',
        overflow: '',
        transition: '',
      });
    }, { once: true });
  }

  // ...
}
[注] 原提案参阅 Mitigating flash of unstyled content with custom elements
有关提案废弃原因请自行在 2020 Spring Virtual F2F · Issue #855 · w3c/webcomponents 中检索关键词 “FOUC”。

外部样式表载入闪烁

与存在于主文档流时的行为不同,样式类元素 <style><link> 在 Shadow DOM 中不会阻塞渲染,自定义元素即使在外联样式已被缓存的情况下,也可能会出现一次闪烁。动态添加自定义元素时,添加本身造成一次闪烁,外联样式载入又造成一次闪烁。

因此,即使从这个角度,在 Shadow DOM 中也不建议导入外部样式表。

如果一定要从外部导入,同时不产生额外的 FOUC 现象,那么可以先获取外部 CSS 文件,动态记录其文本内容为 JS 变量。在样式文本载入之前,避免元素显示、影响其它元素定位;在载入之后,将该变量作为内部样式应用到已有、后续创建的元素。

解决示例
import getSiblingFile from '../get-sibling-file';
const selfPath = import.meta.url;
const styleInfo = {
  // 外部样式文件名。
  fileName: 'main.css',

  // 初始样式, absolute 不影响主文档流元素定位, hidden 隐藏元素。
  // 不使用 display: none 以避免内部图片等资源不被加载。
  defaultText: `
    :host {
      position: absolute;
      visibility: hidden;
    }
  `,

  // 储存外部样式的变量。
  text: '',

  // 等待外部样式加载的实例。
  applyQueue: [],

  // 加载外部样式。
  load() {
    getSiblingFile(this.fileName, selfPath)
    .then((r) => r.text())
    .then((text) => {
      this.text = text;
      // 修改需要载入样式的实例。
      this.applyQueue.forEach((ele) => {
        const { styleEle } = ele;
        styleEle.textContent = text;
      });
    }).catch((error) => {
      throw error;
    });
  },
};

// 加载。
styleInfo.load();

class OutsideLover extends HTMLElement {
  styleEle = null;

  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    const styleEle = document.createElement('style');
    this.styleEle = styleEle;
    // 根据加载完成与否,
    if (!styleInfo.text) {
      // 使用默认样式,置入等待队列。
      styleEle.textContent = styleInfo.defaultText;
      styleInfo.applyQueue.push(this);
    } else {
      // 使用加载完成的样式。
      styleEle.textContent = styleInfo.text;
    }
    shadowRoot.appendChild(styleEle);

    // Something else.
  }
}

作者已经将其做成了一个简单的插件。在导入之后,将继承的 HTMLElement 改为 StylingAdvancedHTMLElement ,省去 attachShadow ,把要导入的外部样式文件名记为静态属性 styleFileUrl 即可。

示例中的代码就可以简化成这样:

import StylingAdvancedHTMLElement from '//raw.githubusercontent.com/PaperFlu/StylingAdvancedHTMLElement/master/export.js';
// 获得 Promise 改为获得 URL 。
import getSiblingURL from '../get-sibling-url';
const selfPath = import.meta.url;

class OutsideLover extends HTMLElement {
  styleFileUrl = getSiblingURL('main.css', selfPath);

  constructor() {
    super();
    // 不再调用 attachShadow ,直接引用 shadowRoot 。
    const { shadowRoot } = this;

    // Something else.
  }
}

模板字符串

Vue 设计者在文档中明确地指出使用字符串在 JS 中保存组件的 HTML 结构的缺点,也是字符串保存其它所有语言文本的共同缺点:一是失去了编辑器语法高亮,二是换行需要使用 \不同于 Vue 中的 “字符串模板”,ES6 的模板字符串MDN)接受换行,在大多数编辑器,以及 Github、NPM 等处的 Markdown 代码块内,可以通过添加标签得到语法高亮

// ES6 模板字符串可以通过这种方式在大多数编辑器中获得正确的语法高亮
// 并被 Prettier 一类的格式化工具正确处理。
const html = String.raw;
const someHTML = html`
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;

在模板字符串前添加标签是存在于 ES6 标准中的合法操作,标签将被视为函数,以一定规则解析该模板字符串(MDN)。类似 Prettier 的格式化工具能够在标签的辅助下正确格式化模板字符串。

NPM 上有一个包,common-tags,设计了 ES6+ 常用的标签函数。安装后,可以这样:

import { html } from 'common-tags';
const template = document.createElement('template');
template.innerHTML = html`
  <style>
    :host {
      background: #fff;
      color: #333;
      border-width: 2px;
      border-style: solid;
      border-color: #2196f3;
      transition: border-color 240ms ease-in-out;
    }
  </style>
  <img title="Avatar Picture Unset." alt="Unknown Person.">
  <span>Unknown Person.</span>
`;

class GameCharacter extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: 'open' });

    // 构建元素结构
    const children = template.content.cloneNode(true);
    shadowRoot.appendChild(children);

  }
}

common-tags 的设计不仅仅是为开启编辑器语法高亮,它还可以实现多余空格消除、换行调整、HTML 安全转义等。(官方文档

语法高亮示例

tagged-template-example

扩展原生元素

如果你不想设计一个全新的自定义元素,只是对现有元素不满意,怎么办?用于联系自定义元素名与类之间的关系的 customElements.define 还接受第三个参数,用于在需要时标记要扩展的原生元素名。对于这样的自定义元素,我们叫它:自定义原生元素

同时,自定义原生元素的对应类继承的可能就不是 HTMLElement 了,它需要继承被扩展的原生元素的 DOM 接口。比如我们需要扩展 <button> ,我们就需要继承 HTMLButtonElement ,如果我们需要扩展 <img> ,我们的类就需要继承 HTMLImageElement

到了这里,我的右手就不高兴了,它认为既然都继承了接口,注册的时候,直接忽略被扩展的原生元素的名字,不是很美观、很简洁吗?因此不愿意帮我继续码字。哎呀我好说歹说,它同意帮我去搜索这个问题,最后发现,不同的原生元素对应的 DOM 接口可能会相同。就像 <blockquote><q> ,接口都是 HTMLQuoteElement ,浏览器怎么知道你想扩展的是哪一个。HTML 规范中有具体的元素接口对应表

这下我的右手满意了,它同意帮我继续完成下面的文章,做一个能够轻松输出自己 DataURL<img>。这样我们在用纯文本传递图片的时候,就方便多了。

示例

在本地文件系统测试本例可能会出现 CORS 相关错误。(file://
// 定义一个扩展 <img> 的自定义元素
class TextImg extends HTMLImageElement {
  get dataURL() {
    const canvas = document.createElement('canvas');
    canvas.width = this.width;
    canvas.height = this.height;

    const ctx = canvas.getContext('2d');
    ctx.drawImage(this, 0, 0);

    return canvas.toDataURL('image/x-icon');
  }
}
customElements.define('text-img', TextImg, { extends: 'img' });

静态和动态创建

自定义原生元素的标签名不变,但存在一个 is 属性,指示其对应的自定义元素名,从而由此绑定对应的类。

在 HTML 中创建
<img is="text-img" data-original="example.png" alt="example">
在 JS 中创建
// 可以这样
const ele = document.createElement('img', { is: 'text-img' });
// 也可以这样
const ele = new TextImg();

ele.src = 'example.png';
ele.alt = 'example';

矛盾?错误?

可是看到这里,我的读者又不满意了,他们通过广泛的学习,发现我的扩展方法和谷歌 Web Fundamentals 上的 Custom Elements v1: Reusable Web Components 教程 不 一 样!教程里的示例二 明明写着:

Example - extending <img>:

customElements.define('bigger-img', class extends Image {
  // Give img default size if users don't specify.
  constructor(width=50, height=50) {
    super(width * 10, height * 10);
  }
}, {extends: 'img'});

Users declare this component as:

<!-- This <img> is a bigger img. -->
<img is="bigger-img" width="15" height="20">

or create an instance in JavaScript:

const BiggerImage = customElements.get('bigger-img');
const image = new BiggerImage(15, 20); // pass constructor values like so.
console.assert(image.width === 150);
console.assert(image.height === 200);

继承的是 Image 呢!

所以也是没有办法,又仔细阅读了这篇文章。发现这篇文章前后似乎是存在矛盾的,示例之前明明也点明了 <img> 的扩展要继承 HTMLImageElement

To extend an element, you'll need to create a class definition that inherits from the correct DOM interface. For example, a custom element that extends <button> needs to inherit from HTMLButtonElement instead of HTMLElement. Similarly, an element that extends <img> needs to extend HTMLImageElement.

我的右手因为之前的误解,决定要补偿我,使出了九牛二虎之力,搜到这个示例至少从 2016 年 12 月 9 日开始,就再没有更新过了,而那个时候自定义原生元素还没有被浏览器支持[注]。即使到了现在,自定义原生元素的需求还很小,因此右手怀疑文章年久失更,在 GitHub 上开了个 issue 提出这个问题,只是目前还没有人理。

[注] 参阅 javascript - How to extend default HTML elements as a "customized built-in element" using Web Components? - Stack Overflow 。讨论点不同,但引用了同一文段。

现在你们都满意了,那我们就继续吧。

注意点

  • 使用 JS 编程式创建的元素的 is 属性在序列化时显示,但不会在 DOM 中显示。
  • 所有原生 <img> 元素的特性都可用于自定义 <img> 元素。比如 src 显示图片, onload 事件等。
  • 自定义原生元素只能扩展规范中包含的 HTML 元素,元素接口为 HTMLUnknownElement 的旧有元素,如 <bgsound><blink><isindex><keygen><multicol><nextid><spacer> ,不能被扩展。

参阅

自定义原生元素在 HTML 规范中的示例:Creating a customized built-in element - HTML Standard

结语

真心感谢有人有耐心能看到最后,我太感动了 😍 !!点个👍、收个藏再走吧!

资料来源

查看原文

hehe 关注了专栏 · 9月13日

不会投入生产的新特性尝鲜(主要是)

产生和解决尝鲜过程中的各种奇葩 Bug。有一些 也会 投生产 吧?

关注 3

hehe 赞了文章 · 7月25日

连肝7个晚上,总结了计算机网络的知识点!(共66条)

前言

计算机网络知识,是面试常考的内容,在实际工作中也常常会涉及到。

最近总结了66条计算机网络相关的知识点,大家一起看一下吧:

1.比较http 0.9和http 1.0😀

  1. http0.9只是一个简单的协议,只有一个GET方法,没有首部,目标用来获取HTML。
  2. HTTP1.0协议大量内容:首部,响应码,重定向,错误,条件请求,内容编码等。

http0.9流程:

客户端,构建请求,通过DNS查询IP地址,三次握手建立TCP连接,客户端发起请求,服务器响应,四次挥手,断开TCP连接。(与服务器只有一个来回)

http1.0流程:

客户端,构建请求,通过DNS查询IP地址,三次握手建立TCP连接,客户端发起请求,服务器响应,四次挥手,断开TCP连接。(与服务器有两个来回)

因为不足缺陷,就有了http1.1。

2.关于http1.1以及http2😁

http1.1中浏览器再也不用为每个请求重新发起TCP连接了,增加内容有:缓存相关首部的扩展,OPTIONS方法,Upgrade首部,Range请求,压缩和传输编码,管道化等。但还是满足不了现在的web发展需求,so,就有了http.2版本。

http2解决了(管道化特性可以让客户端一次发送所有的请求,但是有些问题阻碍了管道化的发展,即是某个请求花了很长时间,那么队头阻塞会影响其他请求。)http中的队头阻塞问题。

使用http2会比http1.1在使用TCP时,用户体验的感知多数延迟的效果有了量化的改善,以及提升了TCP连接的利用率(并行的实现机制不依赖与服务器建立多个连接)

所以需要学习http2,了解更过的内容来掌握计算机网咯。

对于http2,你可以来运行一个http2的服务器,获取并安装一个http2的web服务器,下载并安装一张TLS证书,让浏览器和服务器通过http2来连接。(从数字证书认证机构申请一张证书)。

了解http2的协议,先让我们了解一下web页面的请求,就是用户在浏览器中呈现的效果,发生了些什么呢?

资源获取的步骤:

把待请求URL放入队列,判断URL是否已在请求队列,否的话就结束,是的话就判断请求域名是否DNS缓存中,没有的话就解析域名,有的话就到指定域名的TCP连接是否开启,没有的话就开启TCP连接,进行HTTPS请求,初始化并完成TLS协议握手,向页面对应的URL发送请求。

接收响应以及页面渲染步骤:

接收请求,判断是否HTML页面,是就解析HTML,对页面引用资源排优先级,添加引用资源到请求队列。(如果页面上的关键资源已经接收到,就开始渲染页面),判断是否有还要继续接收资源,继续解析渲染,直到结束。

3.HTTP的几种请求方法用途😂

第一种GET方法:发送一个请求来获取服务器上的某一些资源。

第二种POST方法:向URL指定的资源提交数据或附加新的数据。

第三种PUT方法:跟POST方法一样,可以向服务器提交数据,但是它们之间也所有不同,PUT指定了资源在服务器的位置,而POST没有哦。

第四种HEAD方法:指请求页面的首部。

第五种DELETE方法:删除服务器上的某资源。

第六种OPTIONS方法:它用于获取当前URL所支持的方法,如果请求成功,在Allow的头包含类似GET,POST等的信息。

第七种TARCE方法:用于激发一个远程的,应用层的请求消息回路。

第八种CONNECT方法:把请求连接转换到TCP/TP通道。

4.从浏览器地址栏输入url到显示页面的步骤🤣

简单说说,浏览器根据请求的url交给dns域名解析,查找真正的ip地址,向服务器发起请求;服务器交给后台处理后,返回数据,浏览器会接收到文件数据,比如,html,js,css,图像等;然后浏览器会对加载到的资源进行语法解析,建立相应的内部数据结构;载入解析到得资源文件,渲染页面,完成显示页面效果。

🙅不够清楚明白码?

那就再次详细一下,咳咳,从浏览器接收url,开始进行网络请求线程,发出一个完整的HTTP请求,从服务器端接收请求到对应的后台接收到请求,然后是后台和前台的http交互;其中的缓存问题(http的缓存),浏览器接收到http数据包后的解析流程,css的可视化格式模型,js引擎解析过程等;其他呈现页面效果。

🙅:这里就需要你对浏览器内核的理解:其中主要的渲染引擎和JS引擎,这里了解一下你对浏览器内核的理解。

  1. 渲染引擎,是负责取得网页的内容,整理信息,以及计算网页的显示方式,然后输出到显示器上。
  2. JS引擎是用于解析和执行javascript来实现网页的动态效果。
浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。其实最开始渲染引擎和JS引擎是没有区分明确的,不过后来JS引擎越来越独立,so,内核就倾向于渲染引擎。

对于资源请求/获取,资源响应/页面渲染,会给网络带宽和设备资源带来压力,这个时候就会考虑到web的性能优化。

5.web的性能优化😃

其中里面的性能关键:

什么是数据包
数据包(IP数据包),指封装在固定结构的一系列字节,它定义了数据包的长度,传输的细节,以及其他与TCP相关的信息。

延迟:指IP数据包从一个网络端点到另一个网络端点所花费的时间。(所花费时间在于往返时延,是延迟的时间的两倍)

带宽:只要带宽没有饱和,两个网络端点的连接会一次处理尽可能多的数据量(所以带宽可能会成为性能的瓶颈)

建立连接时间:在客户端和服务器之间建立连接往返数据(三次握手)

TCP三次握手过程:客户端向服务器发起一个SYN包,服务器端返回对应的SYN的ACK响应以及新的SYN包,然后客户端返回对应的ACK。(在客户端和服务器之间建立正常的TCP网络连接时,客户端首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收了这个消息,最后客户端再以ACK消息响应。)

SYN是同步序列编号,是TCP/IP建立连接时使用的握手信息。
ACK是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。在TCP/IP协议中,如果接收方成功的接收到数据,那么会回复一个ACK数据。通过ACK信号有自己固定的格式,长度大小,由接收方回复给发送方。

详解三次握手

第一次握手,建立连接时,客户端发送SYN包到服务器,并进入SYN_SENT状态,等待服务器确认,其中SYN就是同步序列编号。

第二次握手,服务器收到SYN包,必须确认客户的SYN,同时自己也发送一个SYN包,即是SYN+ACK包,此时服务器进入SYN_RECV状态。

第三次握手,客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

TLS协商时间(TLS会造成额外的往返传输)

  1. 客户端发起https连接,需要进行传输层安全协议协商
  2. TLS用来取代安全套接层SSL

除了网络,还有页面内容本身或服务器性能,如首字节时间TTFB,内容下载时间,开始渲染时间,文档加载完成的时间等。

那么什么是TTFB,它是指客户端从开始定位到web页面,至接收到主体页面响应的第一字节所耗费的时间。它是测量:从浏览器发起请求至收到其第一字节之间的耗时。

内容下载时间是等同于被请求资源的最后字节到达时间。

开始渲染时间,从客户看到空白页面的时长。

5.1web性能优化技术(减少客户端网络延迟和优化页面渲染性能来提升web性能)

优化技术:

  • DNS查询优化
  • 客户端缓存
  • 优化TCP连接
  • 避免重定向
  • 网络边缘的缓存
  • 条件缓存
  • 压缩和代码极简化
  • 图片优化

6. http1.1😄

  • 改进持久连接和CDN域名的分片机制
  • 不成熟的http管道化
  • 提供虚拟主机支持
  • 对动态生成的内容完美支持
  • 引入cookie以及安全机制

对于http1的问题,迎来了http2。其中http1的问题:

队头阻塞,大多数情况下,浏览器会希望同时获取许多资源,但http1未提供机制来同时请求这些资源,如果仅是使用一个连接,需要发起请求,等待响应,然后才能发起下一个请求。

在http1中要给特性为管道化,可以允许一次发送一组请求,但是需要按照发送顺序依次接收响应。所以在请求应答过程中,如发生什么情况,剩下的工作都会被阻塞,这就是“队头阻塞”(阻塞在那次请求应答发生错误),阻碍网络传输和web页面的渲染,指导失去响应。

低效的TCP利用,TCP协议作为最可靠的协议之一,其核心是拥塞窗口。

拥塞窗口,是卫星通信在因特网中防止通信拥塞的一种措施,它是在发端采用了一种“拥塞避免”算法和“慢速启动”算法相结合的机制。“拥塞窗口”就是“拥塞避免”的窗口,它是一个装在发送端的可滑动窗口,窗口的大小是不超过接收端确认通知的窗口。

拥塞窗口指在接收方确认数据包之前,发送方可以发送的TCP包的数据。(如拥塞窗口指定为1的情况,那么发送方就发出1哥数据包之后,只有接收方确认了那个发出的数据包,才能发送下一个)

拥塞控制能防止过多的数据注入到网络中,用于避免网络过载,TCP中可以通过慢启动探索当前连接对应拥塞窗口的合适大小。即发送者发送数据的时候并非一开始注入大量数据到网络中,而是发送一个数据包进行测试,当得到确认回复后,额外发送一个未确认包。

这意味着得到一个确认回复,可以发送两个数据包,得到两个确认回复,可以发送四个数据包,以几何形式增长很快到达协议规定的拥塞窗口大小(发包数上限),这时候连接进入拥塞避免阶段,这种机制需要往返几次才能得知最佳拥塞窗口大小,但往返几次所需的时间成本不可忽略。

  • 拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口。如果再考虑到接收方的接收能力,那么发送窗口还可能小于拥塞窗口。
  • 发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减少一些,以减少注入到网络中的分组数。
tcp中的慢启动概念,是用来探索当前连接对应拥塞窗口的合适大小。用来弄清楚新连接当前的网络情况。“慢速启动”是在连接建立后,每收到一个来自收端的确认,就控制窗口增加一个段值大小,当窗口值达到“慢速启动”的限值后,慢速启动便停止工作,避免了网络发生拥塞。

TCP传输控制协议的设计思路是,对假设情况很保守情况下,能够公平对待同一网络的不同流量的应用,它的避免拥塞机制被设计城即使在最差的网络情况下也可以起作用。

臃肿的消息首部,HTTP/1.1能压缩请求内容,但是消息首部却不能压缩。它可能占据请求的绝大部分(也可能是全部)也是比较常见了。(在这里如果能压缩请求首部,把😙请求变得更小,就能够缓解带宽压力了,降低系统的总负载)

受限的优先级设置,即如果浏览器针对指定域名开启多个socket请求,若web页面某些资源会比另外一些资源重要,会加重资源的排队效应,会延迟请求其他的资源,优先级高的资源先获取,优先级低的资源会在资源高的资源处理完成,(在处理过程中,浏览器不会发起新的资源请求)等待高的完成后再发起请求,(这就会让总的页面下载时间延长)。

在请求优先级高的资源的时间区间内浏览器并不会发起优先级较低的新请求

小结:HTTP1.1慢启动影响资源首次加载速度,TCP建立连接后,会开始请求传输,开始比较慢,然后不断加快,为了防止出现网络拥堵,会让页面的首次渲染时间变长。开始多个tcp,如出现网络下降,无法识别资源的优先级,会出现竞态问题。

7.如何进行网站性能优化😅

  1. 内容方面,减少Http请求(合并文件,css精灵,inline Image),减少DNS查询(DNS缓存,将资源分布到合适的数量的主机名),减少DOM元素的数量。
  2. Cookie方面,可以减少Cookie的大小。
  3. css方面,将样式表放到页面顶部;不使用css表达式;使用<link>不使用@import;可将css从外部引入;压缩css。
  4. JavaScript方面,将脚本放到页面底部;将JavaScript从外部引入;压缩JavaScript,删除不需要的脚本,减少DOM的访问。
  5. 图片方面,可优化css精灵,不要再HTML中拉伸图片,优化图片(压缩)。

8.http状态码以及含义😆

  1. 对于1xx的状态码,为信息状态码,100 为继续,表示确认,成功返回具体参数信息。
  2. 对于2xx的状态码,200 表示正常返回信息,201表示请求成功并且服务器创建了新的资源,202表示服务器已接受请求,但尚未处理。
  3. 对于3xx,重定向,301表示,请求的网页已永久移动到新位置,302表示,临时性重定向,303表示临时性重定向,且总是使用 GET 请求新的 URI。304表示,自从上次请求后,请求的网页未修改过。
  4. 对于4xx,客户端错误,404,服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求,401,请求未授权,403,禁止访问,404,找不到如何与 URI 相匹配的资源。
  5. 对于5xx,服务器错误,500,最常见的服务器端错误,503,服务器端暂时无法处理请求,可能是过载或维护。

9.http-数据压缩😉

数据压缩,在浏览器中发送请求时会带着Content-Encoding: gzip,里面时浏览器支持的压缩格式列表,有多种如,gzip,deflate,br等。这样服务器就可以从中选择一个压缩算法,放进Content-Encoding响应头里,再把原数据压缩后发给浏览器。

10.http-分块传输😊

分块传输,就是将传输的文件分解成多个小块,然后分发给浏览器,浏览器收到后再重新组装复原。

每个分开包含两个部分,分块长度和分块数据(长度头和数据块),长度头以CRLF结尾的一行明文,数据块紧跟在长度头后面,也是用CRLF结尾,最后用一个长度为0的块表示结束。

在响应报文里用头字段Transfer-Encoding:chunked表示报文里的body部分不是一次性发送过来的,而是分成了许多块逐个发送的。

在Transfer-Encoding:chunked和Content-Length中,这两个字段是互斥的。

一个响应报文的传输长度要么已知,要么长度未知(chunked)。

Content-Length: 299

11.http-范围请求😋

断点续传

要实现该功能需要制定下载的实体范围,这种制定范围发送请求叫做范围请求。

Accept-Ranges:服务器使用http响应头Accept-Ranges标识自身支持范围请求,字段的具体值用于定义范围请求的单位。

语法

Accept-Ranges: bytes,范围请求的单位是 bytes (字节)
Accept-Ranges: none,不支持任何范围请求单位

范围请求时用于不需要全部数据,只需要其中的部分请求时,可以使用范围请求,允许客户端在请求头里使用专用字段来表示只获取文件的一部分。

Range的格式,请求头Range是HTTP范围请求的专用字段,格式是“bytes=x-y”,以字节为单位的数据范围。

  1. “0-”表示从文档起点开始到文档结束的整个文件。
  2. “100-”表示从第100哥字节开始到文档末尾。
  3. “-10”表示从文档末尾倒数的第10个字节开始。

示例:

执行范围时会使用头部字段 Range 来指定资源 byte 的范围。
Range格式:
5001-10000字节
Range : byte = 5001-10000
5000之后的
Range : byte = 5001-
0-3000字节,5001-10000字节
Range : byte=-3000,5001-10000

上图表示服务器收到Range字段后,检测范围合法性,范围越界,就会返回状态码416,如你的文件只有1000个字节,但请求范围在20000-3000,就会导致这个状态码的出现。

如果成功读取文件,范围正确,返回状态码“206”。服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小。

最后是发送数据,直接把片段用TCP发给客户端,一个范围请求就算是处理完了。

格式是“bytes x-y/length”,与Range头区别在没有“=”

Content-Range: bytes 0-4395719/4395720

12.http-多段数据😎

多端数据,就是在Range头里使用多个“x-y",一次性获取多个片段数据。使用一种特殊的MIME类型:“multipart/byteranges”,用来表示响应报文包含了多个范围时使用。多重范围请求 响应会在头部 Content-Type 表明 multipart-byteranges。

多段数据图:分隔标记boundary来区分不同的分段

13.说一说cookies,sessionStorage 和 localStorage 的区别?😍

  • cookie是网站用来标识用户身份而存储在用户本地终端上的数据
  • cookie数据始终在同源的http请求中携带,即使是不需要的情况,so,会在浏览器和服务器间来回传递
  • sessionStorage和localStorage不会自动把数据发送给服务器,仅仅在本地保存
存储的大小

cookie的数据大小不能超过4k;sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更大。

有限期时间
  1. localStorage存储持久数据,浏览器关闭后数据不会丢失,除了主动删除数据
  2. sessionStorage数据在当前浏览器窗口关闭后自动删除
  3. 设置得cookie过期时间之前都有效,就算窗口或者是浏览器关闭

14.为什么说利用多个域名来存储网站资源会更有效?😘

因为CDN缓存更方便;突破浏览器并发限制;节约cookie带宽;节约主域名得连接数,优化页面响应速度;防止不必要得安全性问题。

15.http2.0的内容🥰

http2是超文本传输协议的第二版,相比http1协议的文本传输格式,http2是以二进制的格式进行数据传输的,具有更小的传输体积以及负载。

http2.0分层,分帧层(http2多路复用能力的核心部分),数据或http层(包含传统上被认为是 HTTP 及其关联数据的部分)。

HTTP2.0:

  • 多路复用机制,引入了二进制的分帧层机制来实现多路复用。(分帧层是基于帧的二进制协议。这方便了机器解析。请求和响应交织在一起。)
  • 可以设置请求的优先级(客户端的分帧层对分割块标上请求的优先级)。
  • 头部压缩 请求头压缩,增加传输效率。
HTTP/2较HTTP/1.1优化亮点
  • 多路复用的流
  • 头部压缩
  • 资源优先级和依赖设置
  • 服务器推送
  • 流量控制
  • 重置消息

多路复用的实现:

在单个域名下仍可以建立一个TCP管道,使用一个TCP长连接,下载整个资源页面,只需要一次慢启动,并且避免了竞态,浏览器发起请求,分帧层会对每个请求进行分割,将同一个请求的分割块打上相同的id编号,然后通过协议栈将所有的分割体发送给服务器,然后通过服务器的分帧层根据id编号进行请求组装,服务器的分帧层将回应数据分割按同一个回应体进行ID分割回应给客户端,客户端拼装回应。

对于http2中的帧(frame),http1不是基于帧(frame)的,是文本分隔的。

GET/HTTP/1.1 <crlf>

这样,对于http1的请求或者是响应可能有的问题:

  1. 一次只能处理一个请求或者是响应,完成之前是不能停止解析的。
  2. 无法预判解析需要多少内层。

HTTP/1 的请求和响应报文,是由起始行、首部和正文组成,换行符分隔;HTTP/2是将请求和响应数据分割成更小的帧,采用二进制编码,易于解析的。

参考图片:

帧结构总结
所有的帧都包含一个9 byte的帧头 + 可边长的正文不同。根据帧的类型不同,正文部分的结构也不一样。

帧头:

16.http2-幕后😗

http2作为一个二进制协议,拥有包含轻量型,安全和快速在内的所有优势,保留了原始的http协议语义,对于http2更改了在系统之间传输数据的方式。

二进制分帧层(binary framing layer),所有通信都在单个TCP连接上执行,该连接在整个对话期间一直处于打开状态,主要是二进制协议将通信分解为帧的方式,这些帧交织在客户端与服务器之间的双向逻辑流中。

HTTP/2 连接的拓扑结构(展示了一个用于建立多个流的连接)

在流 1 中,发送了一条请求消息,并返回了相应的响应消息。

HTTP/2 帧结构

前9个字节对于每个帧是一致的。解析时只需要读取这些字节,就可以准确地知道在整个帧中期望的字节数。

帧首部字段表格:

名称长度描述
length3字节表示帧负载的长度
type1字节当前帧类型
Flags1字节具体帧类型的标识
R1位保留位,不要设置,否则会带来严重后果
Stream Identifier31位每个流的唯一ID
Frame Payload长度可变真实的帧内容,长度是在length字段中设置的

备注:流Id是用来标识帧所属的流。流看作在连接上的一系列帧,它们构成了单独的 HTTP 请求和响应。

对于http1 的请求和响应都分成消息首部和消息体两部分;http2 从上面一张图可以知道,http2的请求和响应分成HEADERS 帧和 DATA 帧。

比较一下:👇

http2的一个重要特性是基于流的流量控制。提供了客户端调整传输速度的能力。其中WINDOW_UPDATE 帧用来指示流量控制信息。

有了多路复用,客户端可以一次发出多有资源的请求,不用像http1那样,发出对新对象请求之前,需要等待前一个响应完成。所以浏览器失去了在Http1中的默认资源请求优先级策略。

17.浏览器生成http请求消息😙

http的头字段

头字段类型含义
Date表示请求和响应生成的日期
Pragma表示数据是否允许缓存的通信选项
Cache-Control控制缓存的相关信息
Connection设置发送响应之后TCP连接是否继续保持的通信选项
Transfer-Encoding表示消息主体的编码格式
Via记录途中经过的代理和网关
Authorization身份认证数据
From请求发送者的邮件地址
Referer当通过点击超级链接进入下一个页面时,在这里会记录下上一个页面的URI
User-Agent客户端软件的名称和版本号等相关信息
Accept客户端可支持的数据类型,以MIME类型来表示
Accept-Charset客户端可支持的字符集
Accept-Language客户端可支持的语言
Host接收请求的服务器ip地址和端口号
Range当需要只获取部分数据而不是全部数据时,可通过这个字段指定要获取的数据范围
Location表示信息的准确位置
Server服务器程序的名称和版本号等相关信息
Allow表示指定的URI支持
Content-Encoding当消息体经过压缩等编码处理时,表示其编码格式
Content-Length表示消息体的长度
Content-Type表示消息体的数据类型,以MIME规格定义的数据类型来表示
Expires表示消息体的有效期
Last-Modified数据的最后更新日期
Content-Language表示消息体的语言
Content-Location表示消息体在服务器上的位置
Content-Range当仅请求部分数据时,表示消息体包含的数据范围

HTTP消息示例:

  1. HTTP,超文本传送协议。
  2. 协议,通信操作的规则定义称为协议。
  3. URI,统一资源标识符。
  4. 1 条请求消息中只能写 1 个 URI。如果需要获取多个文件,必须

对每个文件单独发送 1 条请求。

IP 的基本思路

Ip地址的表示方法

IP地址的结构-子网掩码表示网络号与主机号之间的边界。

解析器的调用方法

DNS服务器的基本工作

DNS 服务器之间的查询操作

数据通过类似管道的结构来流动

18.了解网络基础知识🙂

  • 物理层
  • 数据链路层
  • 网络层
  • 传输层
  • 会话层
  • 表示层
  • 应用层

计算机网络,可以将规模分WAN,Wide Area Network广域网,和LAN局域网。通过电脑连接交换机再到路由器的连接。

你知道计算机与网络都经历了怎么样的一个发展过程吗?
  1. 批处理就是指事先将用户程序和数据装入卡带或磁带,由计算机按照一定的顺序读取,使用户所要执行的这些程序和数据能够一并批量得到处理的方式。

  1. 分时系统,是指多个终端与同一个计算机连接,允许多个用户同时使用一台计算机的系统。

  1. 计算机网络

TCP/IP的机制是什么,TCP/IP通信协议的统称,学习这个有人一定🙅不了解什么是协议。

但我们在接触到程序时,常常听到协议如IP,TCP,HTTP等协议。记住TCP/IP就是IP,TCP,HTTP等协议的集合。协议就是计算机与计算机之间通过网络实现通信时需要达成的一种的“约定”。这些协议就是让不同厂商的设备,不同的CPU和不同的操作系统组成的计算机之间进行通信。

就是两台计算机之间都能支持相同的协议,并遵循才能实现相互通信。

分组交换协议

分组交换就是将大数据分割成一个一个叫做包的较小单位进行传输的方法。

分层模块

了解OSI参考模型

OSI将分为易于理解的7层:

1.物理层,2.数据链路层,3.网络层,4.传输层,5.会话层,6.表示层,7.应用层。

应用层:是对特定应用的协议。

表示层:设备固有数据格式和网络标准数据格式的转换。

会话层:通信管理。负责建立和断开通信连接。

传输层:管理两个节点之间的数据传输。

网络层:地址管理与路由选择。

数据链路层:互连设备之间传送和识别数据帧。

物理层:以“0”,“1”代表电压的高低,灯光的闪灭。

如何模块化通信传输

网络构成要素

网卡:

什么是网关,它是OSI参考模型中负责将从传输层到应用层的数据进行转换和转发的设备。

代理服务:

19.有哪些渲染优化呢?😝

第一,我们可以禁止使用iframe,第二,可以禁止使用gif图片来实现loading效果,降低CPU的消耗,来提升渲染性能,第三,使用CSS3代码来代替JS动画。

对于一些小图标,可以使用base64位编码,以减少网络请求,但不建议大图使用,因为比较耗费CPU,小图标优势在于,可以减少HTTP请求,避免文件跨域,修改及时生效。

页面头部的style和script会阻塞页面,在Renderer进程中的JS线程和渲染线程是互斥的。

20.学习TCP和IP的基础知识🤤

TCP/IP协议族市一组协议的集合,也称为互联网协议族。

20世纪60年代后半叶,应DoD要求,美国开始进行通信技术相关的演技,ARPANET的诞生,开发分组交互技术,在1975年,TCP/IP的诞生。1983年,ARPANET决定正式启用TCP/IP为通信协议。

TCP/IP与OSI参考模型

对于OSI七层模型太细了,而互联网协议族TCP/IP模型划分为四层。

TCP/IP模型(应用层,传输层,互联网层,网络接口层)-应用层,传输层,网络层,链路层。

传输层就是可以让应用程序之间实现通信。

在其中TCP是一种面向有连接的传输层协议,保证两端通信主机之间的通信可达。UDP是一种面向无连接的传输层协议,so,UDP用于分组数据较少或者多播,广播通信以及视频通信等领域。

应用层

21.面试题:TCP/IP市如何在媒介上进行传输的呢?😑

在不同层次的协议✍

数据包首部:

以太网包首部:IP包首部,TCP包首部,数据

IP包首部:TCP包首部,数据

TCP包首部:数据

每个分层中,都会对所发送的数据附加一个首部,它包含了该层中必要的信息。(发送的目标地址,协议相关的信息等)
  • 包是全能性术语
  • 帧是数据链路层中包的单位
  • 数据包,IP和UDP等网络层以上的分层中包的单位
  • 段,表示TCP数据流中的信息
  • 消息,应用协议中数据的单位

数据包的首部,明确表明了协议应该如何读取数据。掌握数据包首部,通常,为协议提供的信息为包首部,所要发送的内容为数据。

发送数据包,TCP/IP通信流程:🤔

  1. 应用程序处理,发送通信开始TCP/IP通信,应用程序会进行编码处理,编码相当于OSI中的表示层功能。
  2. TCP模块的处理,TCP负责建立连接,发送数据以及断开连接,TCP提供将应用层发来的数据顺利发送至对端的可靠传输。在应用层数据的前端附加一个TCP首部,它包含源端口号和目标端口号,序号以及校验和(用来判断数据是否被破坏)然后附加一个TCP首部的包再发给IP。
  3. IP模块的处理,在TCP首部的前端加上自己的IP首部,它包含接收端IP地址和发送端IP地址。若不知道接收端的MAC地址,可以用ARP查找,只要知道对端MAC地址,就可以将MAC以及IP地址交给以太网的驱动程序,来实现数据传输。
  4. 网络接口的处理,从IP传过来的IP包,然后附加上以太网首部并进行发送处理,以太网首部包含接收端的MAC地址,发送端的MAC的地址,以及标志以太网类型的以太网数据的协议。
数据包,经过以太网的数据链路时,大致上附加了以太网包首部,IP包首部,TCP包首部或者UDP包,以及应用自己的包首部和数据,最后包追加了包尾。

分层中包的结构

数据包接收流程🙄
  1. 网络接口的处理,主机收到以太网包后,从以太网的包首部找到MAC地址判断是否为发给自己的,若不是就丢弃,如果是,就查找以太网包首部中的类型域从而确定以太网协议所传送过来的数据类型。
  2. 通过IP模块处理,然后TCP模块处理(需要判断是否被破坏),检查是否按照序号接收数据。当数据接收完毕后,会发送“确认回执”给发送端。注意,这里的回执信息未能达到发送端,那么发送端会认为没有收到而一直反复发送。
  3. 应用程序的处理,接收端应用程序会直接接收发送端发送的数据信息。

22.了解一下http-http3.0😶

在http2.0中,TCP管道传输途中也会导致丢包问题,造成队头阻塞(在http2.0中的TCP建立连接三次握手,和HTTPS的TSL连接也会耗费较多时间)

其实多路复用技术可以只通过一个TCP连接就可以传输所有的请求数据。

http3中弄了一个基于UDP协议的QUIC协议,QUIC虽说基于UDP,但是在基础上添加了很多功能。QUIC(快速UDP网络连接)是一种实验性的网络传输协议,由Google开发,该协议旨在使网页传输更快。

对于在http中的缺点就是延迟,浏览器的阻塞,在对同一域名,同时只能连接4个,超过了浏览器的最大连接限数时,后面的请求就会被阻塞;DNS的查询就是将域名解析为IP,来向目标服务器的IP建立连接,其中通过DNS缓存可以达到减少时间的作用;建立连接,HTTP是基于tcp协议的,三次握手,每次连接都无法复用,so,会每次请求都要三次握手和慢启动,都会影响导致延迟。(慢启动对大量小文件请求影响较大)

http处于计算机网络中的应用层,建立在TCP协议之上。(掌握了解tcp建立连接的3次握手和断开连接的4次挥手和每次建立连接带来的RTT延迟时间)。

相对于HTTP1.0使用了header里的if-modified-since,expires来做缓存判断,在HTTP1.1中引入了entity tag,if-unmodified-since,if-match,if-none-match等更多可供选择的缓存头来控制缓存策略。

http1.0传输数据时,每次都要重新建立连接,增加延迟,http1.1加入了keep-alive可以复用部分连接,但在域名分片等情况下仍要连接夺冠时连接,耗费资源,以及给服务器带来性能压力。

http1.1尝试使用pipeling来解决问题,就是浏览器可以一次性发出多个请求,在同一个域名下,同一条TCP连接,但对于pipeling要求返回是按照顺序的,即(如果前面有个请求很耗时的话,后面的请求即使服务器已经处理完,任会等待前面的请求处理完才开始按序返回。)

在http1.x中,Header携带内容过大,增加了传输的成本,在传输的内容都是明文,在一定程度上无法保证其数据的安全性。(在http1.x问题的出现,有了SPDY协议,用于解决http/1.1效率不高的问题,降低延迟,压缩Header等)

HTTP2主要解决用户和网站只用一个连接(同域名下所有通信都只用单个连接完成,单个连接可以承载任意数量的双向数据流,数据流是以消息的形式发送,消息由一个或多个帧组成)。

so,http采用二进制格式传输数据,不像http1.x的文本格式。(二进制:http2将请求和响应数据分割成帧,并且它们采用二进制的编码),对于HTTP2的概念:(流,消息,帧)

  1. 流,它是连接中的一个虚拟信道;
  2. 消息,它是HTTP消息,请求,以及响应;
  3. 帧,它是HTTP2.0通信的最小单位。
多个帧可以乱序发送,可根据帧首部的标识流进行重新组装。

对于http2,同一域名下只需要使用一个TCP连接,那么当出现丢包时,会导致整个TCP都要开始等待重传。对于http1.1来说,可以开启多个TCP连接,出现这种情况指挥影响一个连接(或者部分),其余的TCP连接正常传输。

HTTP/2 对首部采取了压缩策略,为了减少资源消耗并提升性能。(因为在http1中,在header携带cookie下,可能每次都要重复传输数据)

so,有了QUIC协议,整合了TCP,TLS,和HTTP/2的优点,并加以优化。那么QUIC是啥,它是用来替代TCP,SSL/TLS的传输层协议,在传输层之上还有应用层。

注意,它是一个基于UDP协议的QUIC协议,使用在http3上。

QUIC 新功能


HTTPS 的一次完全握手的连接过程

QUIC可以解决传输单个数据流可以保证有序的交付,并且不会影响其他的数据流。(解决http2问题)

表示在QUIC连接中,一个连接上的多个stream,如其中stream1,stream2,stream3,stream4,其中stream2丢失(quic packet),其余UDP到达,应用层直接读取。--- 无需等待,不存在TCP队头阻塞,丢失的包需要重新传即可。

补充:

  1. TCP是基于IP和端口去识别连接的;
  2. QUIC是通过ID的方式去识别连接的

对于QUIC的包都是经过认证的,除了个别,so,这样,通过加密认证的报文,就可以降低安全风险。

HTTP2-TLS,TCP,IP

小结QUIC特点:(基于UDP)--- http3-QUIC,UDP,IP

  1. 多路数据流
  2. TLS
  3. 有序交付
  4. 快速握手
  5. 可靠性

23.网络中的UDP😛

UPD面向报文的协议,就是UDP只是报文的搬运工,不会对报文进行任何拆分和拼接操作,在发送端,应用层将数据传给传输层的UDP协议,UDP会给数据加一个UDP头标识下是UUDP协议,然后传给网络层。

接收端,网络层将数据传给传输层,UDP只去除IP报文头就传给应用层,不会任何拼接操作。

UDP是无连接,通信不需要建立和断开连接,UDP是不可靠的,不关心数据的安全等问题,UDP是没有拥塞控制,在网络条件不好的情况下可能会导致丢包。

传输:UDP 支持一对一,一对多,多对多,多对一的的传输方式, UDP 提供了单播,多播,广播的功能。

24.网络中的TCP😜

UDP没有TCP那么复杂,UDP头部开销小,但是TCP头部比UDP头部复杂得多,UDP头部只有8字节,相比TCP的至少20字节要少很多。

Sequence number

这个序号保证了TCP传输的报文都是有序的,对端可以通过序号顺序的拼接报文

Window Size

表示窗口大小,还能接收多少字节的数据

Acknowledgement Number

表示上一个序号的数据已经接收到,接收端期望接收的下一个字节的编号是多少

标识符

当ACK=1,表示确认号字段有效

当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文

当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文

当FIN=1,表示此报文段是一个释放连接的请求报文

性能指标 RTT

表示发送端发送数据到接收到对端数据所需的往返时间

小结
  1. TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议
  2. UDP(User Data Protocol,用户数据报协议)是面向非连接的协议。

25.建立连接三次握手😕

建立连接开始时,两端都是CLOSED状态,通信开始前,双方都会创建 TCB,后进入 LISTEN 状态,开始等待客户端发送数据。

第一次握手

客户端向服务器端发送连接请求报文段,请求发送后,客户端进入SYN-SENT 状态。

第二次握手

服务端收到连接请求报文段后,发送完成后便进入 SYN-RECEIVED 状态。

第三次握手

客户端收到连接同意的应答后,要向服务端发送一个确认报文。客户端发完这个报文段后便进入ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED状态,此时连接建立成功。

有人问了,两次握手就可以建立连接了,为啥要第三次呢?

因为防止失效的连接请求报文段被服务器端接收,从而导致错误。

26.http请求码有哪些?🤑

100为继续,一般发送post请求时,已经发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息;201,请求成功并且服务器创建了新的资源;202,服务器已接受请求,但未处理。

301,请求的网页已经永久移动到新的位置;302,临时性重定向;303,临时性重定向,且总是使用GET请求新的URI;304,自从上次请求后,请求的网页未修改过。

404,服务器无法理解请求;401,请求未授权;403,禁止访问。

27.面试时,简单说说TCP传输的三次握手四次挥手😲

传输,为了准确无误地把数据传输给目标,TCP协议采用了三次握手策略,用TCP协议把数据包送出去后,会向对方确认是否成功达到,发送端发送一个带SYN标志的数据包给到对方,接收端收到后,会回传一个带有SYN/ACK标志的数据包表示传送到达的确认信息,然后发送端也再次回传一个带有ACK标志的数据包,表示“握手”结束了。

握手过程中使用的标志:SYN和ACK

断开一个TCP连接需要四次挥手:

第一次挥手

主动关闭的一方,发送一个FIN(上述讲过---当FIN=1,表示此报文段是一个释放连接的请求报文),传送数据,用来告诉对方(被动关闭方),说不会再给你发送数据了。---主动关闭的一方可以接受数据。

第二次挥手

被动关闭方 收到 FIN 包,发送 ACK 给对方,确认序号。

第三次挥手

被动关闭方 发送一个 FIN,关闭方,说我不会再给你发数据了。(你不给我发送数据,我也不给你发送数据了)

第四次挥手

主动关闭一方收到 FIN ,发送要给 ACK ,用来确认序号

28.常说的HTTPS🙁

其实HTTP协议时承载于TCP协议之上的,再HTTP和TCP之间添加一个安全协议层,SSL或者TSL(ssl/tls协议传输,包含证书,卸载,流量转发,负载均衡,页面适配,浏览器适配,refer传递等),则就是常说的HTTPS。

29.GET和POST的区别,何时使用POST?😖

  1. GET是用于信息获取,使用URL传递参数,发送信息的数量有限;
  2. POST是用于修改服务器上的资源;
  3. 一般使用POST,当无法使用缓存文件,向服务器发送大量的数据,发送未知的字符

30.面试问,HTTP协议的主要特点😨

  1. 简单快速
  2. 灵活
  3. 无连接
  4. 无状态

31.面试问,说说HTTP报文的组成部分😟

HTTP报文的组成部分包含:请求报文和响应报文

请求报文: 有请求行,请求头, 空行,请求体

响应报文: 有状态行,响应头,空行,响应体

请求报文包含:

1.请求方法,2.请求URL,3.HTTP协议以及版本,4.报文头,5.报文体

  • 请求行,有请求方法,请求URL,http协议以及版本;
  • 请求头,一堆键值对
  • 空行,当服务器在解析请求头的时候,遇到了空行,表明后面的内容是请求体
  • 请求体,请求数据
响应报文包含:

1.报文协议以及版本,2,状态码以及状态描述,3,响应头,4,响应体

  • 状态行:http协议和版本,状态码以及状态描述
  • 响应头
  • 空行
  • 响应体

32.面试时问,知道哪些HTTP方法😤

  1. GET方法获取资源
  2. POST方法传输资源
  3. PUT方法更新资源
  4. DELETE方法删除资源
  5. HEAD方法获得报文首部

33.持久链接😢

在http1.0中,客户端每隔很短时间会对服务器发出请求,查看是否有新的数据,只要轮询足够快,就可以造成交互实时进行,但这个做法,会对两端造成大量的性能浪费。

对于http1.1中的长连接,使用connection:keep-alive进行长连接,客户端只请求一次,但是服务器会将继续保持连接,再次请求时,避免了重新建立连接。

注意,keep-alive不会永久保持连接,只有保持一个时间段。

34.安全问题:CSRF和XSS😭

CSRF的基本概念,攻击原理,防御措施

CSRF(Cross-site request forgery):跨站请求伪造

理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。

以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。

CSRF的原理:(要完成一次CSRF攻击)

  • 登录受信任网站A,并在本地生成Cookie。
  • 在不登出A的情况下,访问危险网站B。

XSS的基本概念,跨域脚本攻击。

xss是一种发生在web前端的漏洞,所以其危害的对象也主要是前端用户。

跨域脚本攻击是,恶意攻击者往web页面里插入恶意的script代码,在浏览器中运行script代码,达到恶意攻击用户的目的。

so,实现xss攻击具备2个条件,第一需要向web页面注入恶意的代码,第二,这些恶意代码被浏览器成功的执行。

CSRF和XSS的区别:

  1. CSRF需要登录,获取COOKIE,利用网站本身的漏洞,去请求网站的api
  2. XSS,不需要登录,向网站注入JS代码,执行JS里的代码,篡改网站的内容

35.从一个HTTP请求来看网络分层原理

一个HTTP请求的分层解析流程:

TCP,它是面向连接的,可靠的,基于字节流的传输层通信协议。

特点:

  • 基于连接,数据传输之前需要建立连接
  • 全双工的,双向传输
  • 字节流,不限制数据大小,打包成报文段,保证有序接收,重复报文自动丢弃
  • 流量缓冲,解决双方处理能力的不匹配
  • 可靠的传输服务,保证可达,丢包时通过重发机制实现可靠性
  • 拥塞控制,防止网络出现恶性拥塞

TCP连接,源地址,源端口,目的地址,目的端口

从TCP-IP协议底层

滑动窗口协议与累计确认(延时ACK)

滑动窗口大小同通过tcp三次握手和对端协商,且受网络状况影响

36.HTTPS安全加密通道原理分析

什么是HTTPS协议,由于HTTP天生“明文”的特点,整个传输过程完全透明,任何人都能够在链路中截获,修改或者伪造请求、响应报文,数据不具有可信性。

使用HTTPS时,所有的HTTP请求和响应发送到网络前,都要进行加密。

https = http + ssl/tls

对称加密:加密 解密使用同一密钥

非对称加密:公钥-随意分发,私钥-服务器自己保持

公钥加密的数据,只能通过私钥解密
私钥加密的数据,只能公钥能解密

加密算法:

对称密钥加密算法,编,解码使用相同密钥的算法

非对称密钥加密算法,一个公钥,一个私钥,两个密钥是不同的,公钥可以公开给如何人使用,私钥是严格保密的。

加密通道的建立:

数字证书的申请和验证

如何申请:

  • 生成自己的公钥和私钥,服务器自己保留私钥
  • 向CA机构提交公钥,公司,域名信息等待认证
  • CA机构通过线上,线下多种途径验证你提交信息的真实性,合法性
  • 信息审核通过,CA机构则会向你签发认证的数字证书,包含了公钥,组织信息,CA信息,有效时间,证书序列号,同时生成了一个签名
  • 签名步骤:hash(用于申请证书所提交的明文信息)= 信息摘要
  • CA再使用CA机构的私钥对信息摘要进行加密,密文就是证书的数字签名

37.https的对称加密,非对称加密,混合加密,CA认证😨

HTTPS ,超文本传输安全协议,目标是安全的HTTP通道,应用是安全数据传输。HTTP协议虽然使用广,但是存在不小的安全缺陷,主要是数据的明文传送消息完整性检测的缺乏。

HTTPS协议是由HTTP加上TLS/SSL协议构建的可进行加密传输,身份认证的网络协议。

通过, 数字证书,加密算法,非对称密钥 等技术完成互联网数据传输加密,实现互联网传输安全保护。

HTTPS主要特性:

  1. 数据保密性
  2. 数据完整性
  3. 身份校验安全性

客户端和服务器端在传输数据之前,会通过基于证书对双方进行身份认证。客户端发起SSL握手消息给服务端要求连接,服务端将证书发送给客户端。客户端检查服务器端证书,确认是否由自己信任的证书签发机构签发,如果不是,将是否继续通讯的决定权交给用户选择,如果检查无误或者用户选择继续,则客户端认可服务端的身份。

服务端要求客户端发送证书,并检查是否通过验证,失败则关闭连接,认证成功,从客户端证书中获得客户端的公钥。

HTTP原理

客户端的浏览器首先要通过网络与服务器建立连接,该连接时通过TCP来完成的,一般TCP连接的端口号是80,建立连接后,客户端发送一个请求给服务器端;服务器端接收到请求后,给予相应的响应信息。

HTTPS原理

客户端将它所支持的算法列表和一个用作产生密钥的随机数发送给服务器,服务器从算法列表中选择一种加密算法,并将它和一份包含服务器公用密钥的证书发送给客户端,该证书还包含了用于认证目的的服务器标识,服务器同时还提供了一个用作产生密钥的随机数。

客户端对服务器的证书进行验证,并抽取服务器的公用密钥,再产生一个称作pre_master_secret的随机密码串,并使用服务器的公用密钥对其进行加密,并将加密后的信息发送给服务器。

客户端与服务器端根据pre_master_secret以及客户端与服务器的随机数独立计算出加密和MAC密钥。

混合加密

在传输数据中使用对称加密,但对称加密的密钥采用非对称加密来传输,混合加密比较安全,但无法知道数据是否被篡改

CA认证

CA认证, 即是电子认证服务,指电子签名相关各方提供真实性,可靠性验证的活动。

特性:参阅百度百科—简介,点击进入

38.https对比http🥶

http传输方式:明文传输,网站或相关服务与用户之间的数据交互无加密,容易被监听,篡改。

https传输方式:在HTTP加入了SSL层,用于数据传输加密。

http身份认证:无任何身份认证,用户无法通过http辨认出网站的真实身份。

https身份认证:经过CA多重认证,包含域名管理权限认证等。

http成本:无任何使用成本,所有网站默认是http模式。

https需要成本,需要申请SSL证书来实现https。

http连接端口:80端口。

https连接端口:443端口。

39.证书如何安全传输,被掉包了怎么办?😳

40.http3中QUIC😵

QUIC是谷歌制定的一种基于UDP的低时延的互联网传输层协议。

1、避免前序包阻塞;2、零RTT建连;3、FEC前向纠错

HTTP 的历史

HTTP/2 和 HTTP/3 建立连接的差别

TCP/建立连接与QUIC建立连接

队头阻塞/多路复用

HTTP/1.1 提出了 Pipelining 技术,允许一个 TCP 连接同时发送多个请求

请求与响应,与,Pipelining

http/1.1队头阻塞

HTTP/2 的多路复用解决了队头阻塞问题

拥塞控制:

  • 慢启动
  • 拥塞避免
  • 快速重传
  • 快速恢复

41.HTTP 协议入门

HTTP 基于TCP/IP 协议的应用层协议,不涉及数据包packet传输,主要客户端和服务器之间的通信格式,默认使用80端口。TCP连接建立后,客户端向服务器请求网页,协议规定,服务器只能回应HTML格式的字符串,不能回应别的格式。

http1.0可以传输文字,传输图像,视频,二进制文件;除了GET方法,还有POST,HEAD等;每次通信都需要 头信息HTTP header,状态码,多字符集支持,缓存,权限等。

字段:ontent-Type 字段

头信息必须是 ASCII 码,后面的数据可以是任何格式,字段值:

text/plain
text/html
text/css
image/jpeg
image/png
image/svg+xml
audio/mp4
video/mp4
application/javascript
application/pdf
application/zip
application/atom+xml

客户端请求的时候,使用Accept字段,表示可以接受哪些数据格式。

Accept: */*

Content-Encoding字段,表示数据的压缩方式

Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate

客户端在请求时,用Accept-Encoding字段说明接受哪些压缩方法。

Accept-Encoding: gzip, deflate

http1.0就是每个TCP连接只能发送一个请求,发送完毕后就关闭,so,为解决问题,用了一个非标准Connection字段,Connection:keep-alive。

HTTP/1.1引入了持久连接(persistent connection),TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。

也不是永久性不关闭的,只要有一段时间没有活动,就会关闭TCP连接,一般对于同一个域名,大多数浏览器允许同时建立6个持久连接。

1.1 版引入了管道机制(pipelining),同一个TCP连接里,可以同时发送多个请求。但是还是按照顺序,一个请求回应后,再回应另一个请求。(但也减少不小的消耗时间)。

使用分块传输编码,只要请求或回应的头信息有Transfer-Encoding字段

Transfer-Encoding: chunked

什么是多工?双向,实时的通信就叫 多工。

HTTP2 复用TCP连接,在一个连接里,两端都可以同时发送多个请求或响应,而且不用按照顺序一一对应,避免了“队头堵塞”。

http2引入了头信息压缩机,头信息使用gzip或compress压缩后再发送,客户端和服务器同时维护一张头信息表,所有字段存在这个表里,生成一个索引号,以后就只发送索引号,这样就提高速度了。

HTTP/2允许服务器未经请求,主动向客户端发送资源(服务器推送)

42.什么是cookie呢🥴

cookie是某网站为了辨别用户身份,进行session跟踪而存储在用户本地终端的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。

  1. 存储在用户本地终端上的数据
  2. 用来辨别用户身份
  3. 保存在用户本地终端

    cookie是一些数据,存储在你电脑上的文本文件中,当web服务器向浏览器发送web页面时,在连接关闭后,服务端不会记录用户的信息,cookie的作用就是解决如何记录客户端的用户信息。

    场景:当用户访问web页面,用户信息记录在cookie中,当用户下一次访问页面后,可以在cookie中读取用户访问记录。

    cookie是以键值对形式存储的,当浏览器从服务器上请求web页面,该页面的cookie会被添加到请求中,服务端通过这种方式用来获取用户信息。

可以使用JavaScript来创建,读取,修改,删除cookie

使用document.cookie属性来创建,读取以及删除cookie

创建:

document.cookie = "username = dadaqianduan";

给cookie添加一个过期时间:

document.cookie = "username = dadaqianduan; expires=xxxxxx";

默认情况下,cookie属于当前页面:

document.cookie = "username = dadaqianduan; expires= ; path=/";

读取cookie

var x = document.cookie;

修改cookie

document.cookie = "username = dada; expires=xxx; path=/";

删除cookie, 把设置时间的expires 参数改为以前的时间即可。

document.cookie = "username = ; expires= xxx";

为什么会有cookie呢?因为http请求时无协议的,http1.x,无状态协议,客户端同一个请求发送多次,服务端并不能识别是不是同一个客户端发送,为了解决无状态,就有了cookie。

cookies是服务器暂存放在你的电脑里的资料,以.txt格式的文本文件,好让服务器用来辨认你的计算机,当你在浏览网站时,web服务器会发送一个小小的资料放在你的计算机上。

当你下一次访问同一个网站,web浏览器会先看看有没有它上次留下来的cookies资料,有的话就输出特定的内容给你。

cookie原理

浏览器第一次请求服务器,服务器响应请求中携带cookie,给浏览器,浏览器第二次请求,携带cookie,给服务器,服务器根据cookie辨别用户,也可以修改cookie内容。

domain时.baidu.com的cookie绑定到了域名商。跨域的域名不能写入在cookies文件里

cookie的属性有哪些

Name, Value, Domain, Path, Expires/Max-Age, Size, HttpOnly, Secure, SameSite

掌握面试中的HttpOnly,这个属性设置为true,就不能通过js脚本获取cookie的指,能有效防止xss的攻击。

Cookie中的HttpOnly和Secure中:

标记为Secure的Cookie只能被https协议加密过的请求发送给服务端。但也无法保证其安全保障。

如果cookie中设置了HttpOnly属性,通过js脚本将无法读取到cookie信息,有效防止xss的攻击,窃取cookie内容,增加了cookie的安全性,但是重要信息还是不要存储在cookie中。

因为xss为跨站脚本攻击,是web程序常见的漏洞,属于被动式且用于客户端的攻击方式

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

SameSite

SameSite Cookie允许服务器要求某个cookie在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。

示例:

Set-Cookie: key=value; SameSite=Strict

SameSite有三个值:

None: 浏览器在同站请求,跨站请求下继续发送cookies,不区分大小写。(所有三方的请求都会携带cookie)

Strict: 浏览器将只在访问相同站点时发送cookie。(所有三方的链接都不会携带cookie)

Lax: Same-site cookies 将会为一些跨站子请求保留,如图片加载或者frames的调用,但只有当用户从外部站点导航到URL时才会发送。(只有同步且是get请求才可携带cookie)

在https协议中,才能通过js去设置secure类型的cookie,在http协议的网页中是无法设置secure类型cookie的。默认情况,https协议还是http协议的请求,cookie都会被发送到服务端。

43.什么是token呢?🤬

token的出现,是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示。token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,第一登录时,服务器生成一个token,将此token返回给客户端,客户端带上这个token,无需再次带上用户名和密码了。

token的出现减轻了服务器的压力,减少频繁地数据库查询。

token的优点
  • 无状态,可扩展
  • 安全性
  • 多平台跨域
  • 基于标准

基于Token的身份验证的过程

浏览器,输入userName, Password,到mysql,校验成功 生成token,将token返回给客户端,当客户端发起请求时,每次访问api都携带token到服务器端,经过过滤器,校验token,校验成功后返回请求数据,校验失败后返回错误码。

44.cookie,session,token😷

cookie,记录访问过的网站或正在访问的网站,对于HTTP 协议是无状态的,服务器不知道浏览器上一次访问做了什么,也无法对用户会话进行跟踪连接,所以,cookie是由服务器发送到客户端浏览器的一段文本文件,包含了网站访问活动信息。Cookie 存放在客户端,用来保存客户端会话信息;由于存储在客户端,它的安全性不能完全保证。

session表示是c/s架构中服务器和客户端一次会话的过程,用来保存认证用户信息。session是一种HTTP存储机制,提供持久机制。Session存放在服务器端,用户验证客户端信息。由于存储在服务器,安全性有一定的保证。

token是一种认证方式(是“令牌”的意思,主要是用于身份的验证方式。)

45.跨域🤒

网页的URL的协议、域名、端口有一个不同,就算是跨域了

跨域:JSONP

46.思维导图http小结

47.http中的字段🤠

  1. accept,数据格式,请求accept,响应,content-type,表示收到的数据格式
  2. accept,压缩方式,请求accept-encoding,响应,content-encoding,采用什么样的压缩方式
  3. accept,支持语言,请求accept-language,响应content-language
  4. accept,字符集,请求accept-charset,响应content-type,指定字符集
  5. accept,范围请求,请求if-range和range,响应accept-anges和content-range
  6. cookie,请求时传递给服务端的cookie信息
  7. set-cookie,响应报文首部设置要传递给客户端的cookie信息
  8. allow,支持什么HTTP方法
  9. last-modified,资源的最后修改时间
  10. expires,设置资源缓存的失败日期
  11. content-language,实体的资源语言
  12. content-encoding,实体的编码格式
  13. content-length,实体主体部分的大小单位是字节
  14. content-range,返回的实体的哪些范围
  15. content-type,哪些类型
  16. accept-ranges,处理的范围请求
  17. age,告诉客户端服务器在多久前创建了响应
  18. vary,代理服务器的缓存信息
  19. location,用于指定重定向后的URI
  20. If-Match,值是资源的唯一标识
  21. User-Agent,将创建请求的浏览器和用户代理名称等信息传递给服务器
  22. Transfer-Encoding,传输报文的主体编码方式
  23. connection,管理持久连接,keep-alive , close
  24. Cache-Control,控制浏览器的强缓存

48.如果面试问HTTP报文结构是什么,你能回答上来不?

对于 TCP 而言

起始行 + 头部 + 空行 + 实体
  1. 请求报文
GET /home HTTP/1.1
  1. 响应报文
HTTP/1.1 200 OK

空行是用来分开头部和实体。

49.如果面试问HTTP请求方法有哪些,你能回答上来不?🤥

  1. GET方法,用来获取资源
  2. POST方法,用来提交数据
  3. PUT方法,用来修改数据
  4. DELETE方法,用来删除资源
  5. OPTIONS方法,用来跨域请求
  6. HEAD方法,用来获取资源的元信息
  7. CONNECT方法,用来建立连接,用于代理服务器

50.如果面试问,你对URI是如何理解的,你能回答上来不?🤫

URL统一资源定位符,URI,统一资源标识符。URI用于区分网络上不同的资源。

URI包含了URN和URL。

URL的结构:

协议名,登录主机的用户信息,主机名和端口,请求路径,查询参数,URI上定位资源内的一个锚点。

51.如果面试问,你对HTTP状态码的了解有多少,你能回答上来不?

了解一些特定的HTTP状态码:

52.如果面试问,说说HTTP特点以及缺点,你能回答上来不?

特点是:

  1. 灵活可扩展
  2. 可靠传输
  3. 无状态等

缺点是:

  1. 无状态
  2. 明文传输
  3. 队头阻塞问题

53.如果面试问,说说你对Accept字段的理解,你能回答上来不?

  • 数据格式
  • 压缩方式
  • 支持语言
  • 字符集

54.如果面试问,什么是队头阻塞问题,你能回答上来不?🤭

TCP中是报文,HTTP是请求。

对于解决HTTP的队头阻塞问题是:并发连接和域名分片。

55.如果面试问,说说你对HTTP代理的理解,你能回答上来不?🧐

代理服务器功能:1,负载均衡,2,保障安全(利用心跳机制监控服务器,一旦发现故障机就将其踢出集群。),3,缓存代理。

理解代理缓存:

  • 由一个代理服务器下载的页面存储;
  • 一个代理服务器为多个用户提供一条通道;
  • 缓冲的代理允许一个代理服务器减少对同一个网站的同样页面的请求次数
  • 一旦代理服务器的一个用户请求了某个页面,代理服务器就保存该页面以服务于它的其他用户的同样的请求
  • 代理缓存,这种处理减少了用户等待页面显示的时间

缓存的作用:

代理服务器或客户端本地磁盘内保存的资源副本,利用缓存可减少对源服务器的访问,可以节省通信流量和通信时间。

示例:

Cache-Control: max-age=300;

表示时间间隔,再次请求的时间间隔300s内,就在缓存中获取,否则就在服务器中

Cache-Control:

  • public 表示响应可被任何中间节点缓存
  • private 表示中间节点不允许缓存
  • no-cache 表示不使用Cache-Control的缓存控制方式做前置验证
  • no-store 表示真正的不缓存任何东西
  • max-age 表示当前资源的有效时间

强缓存:浏览器直接从本地存储中获取数据,不与服务器进行交互

协商缓存:浏览器发送请求到服务器,浏览器判断是否可使用本地缓存

学习了解强缓存👍:

强缓存主要学习expires和cache-control

cache-control该字段:max-age,s-maxage,public,private,no-cache,no-store。

cache-control: public, max-age=3600, s-maxage=3600 
  • 表示资源过了多少秒之后变为无效
  • s-maxage 的优先级高于 max-age
  • 在代理服务器中,只有 s-maxage 起作用

public 和 private

  • public 表示该资源可以被所有客户端和代理服务器缓存
  • private 表示该资源仅能客户端缓存

当浏览器去请求某个文件的时候,服务端就在response header里做了缓存的配置:

表现为:respone header 的cache-control

学习了解✍协商缓存:

response header里面的设置

etag: 'xxxx-xxx
last-modified: xx, 24 Dec xxx xxx:xx:xx GMT

56.如果面试问,HTTP/2,你能回答上来不?🤓

HTTP/2采用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的高压缩率。

服务器推送

57.B/S 结构定义😈

浏览器-服务器结构,B/S结构,客户端不需要安装专门的软件,只需要浏览器即可,浏览器通过web服务器与数据库进行交互,可以方便的在不同平台下工作。

B/S结构简化了客户端的工作,它是随着Internet技术兴起而产生的,对C/S技术的改进,但该结构下服务器端的工作较重,对服务器的性能要求更高。

58.URI统一资源标识符👿

统一资源标识符是一个用于标识某一互联网资源名称的字符串。该标识允许用户对网络中的资源通过特定的协议进行交互操作。URI常见形式为统一资源定位符(URL),URN为统一资源名称。用于在特定的命令空间资源的标识,以补充网址。

59.HTTP 协议👹

HTTP超文本传输协议是互联网上应用最为广泛的一种网络协议。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。通过HTTP或者HTTPS协议请求的资源由统一资源标识符来标识

HTTP 协议主要特点

60.数据链路🔪-数据链路层

数据链路层:以太网,无线LAN,PPP。。。(无线,光纤。。。)
  • 数据链路的知识对了解TCP/IP与网络起到重要的作用
  • 数据链路层的协议定义了通过通信媒介互连的设备传输的规范
  • 物理层面是将实际的通信媒介如电压的高低,电波的强弱等信号与二进制01进行转换
  • 数据链路层处理的数据是一种集合为“帧”的块
  • WLAN,无线局域网
  • PPP,点对点协议,即是1对1连接计算机的协议
  • ATM,异步传输方式
数据链路是让互联网计算机之间相互通信的一种协议,通信手段
  • MAC地址用于识别数据链路中互连的节点

  • 无线通信是使用电磁波,红外线,激光等方式进行传播数据。一般在办公室的局域网范围内组成的较高速的连接称为无线局域网。
  • IP-x-x-x,在IP网络上建立x-x-x,网络服务商提供一种在IP网络商使用MPLS技术构建x-x-x的服务。

61.TCP和UDP的区别

TCP是一个面向连接,可靠,基于字节流的传输层协议。

UDP是一个面向无连接的传输层协议。

TCP是面向连接的,客户端和服务器端的连接,双方互相通信之前,TCP需要三次握手建立连接,而UDP没有建立连接的过程

tcp是面向字节流,udp是面向报文的。UDP的数据传输是基于数据报的,TCP继承了IP层的特性,TCP为了维护状态,将一个个IP包变成了字节流。

TCP报文格式图:

  • 序号:Seq序号,占32位,标识从TCP源端口向目的端口发送的字节流,发起方发送数据时,对此进行标记
  • 确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1
  • 标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等
  1. URG,紧急指有效
  2. ACK,确认序号有效
  3. RST,重置连接
  4. SYN,发起一个新连接
  5. FIN,释放一个连接
  6. PSH,接收方应该尽快将这个报文交给应用层

62.三次握手建立连接

TCP 的三次握手的过程:

有图可知都处于closed状态,服务器开始监听某个端口进入listen状态,客户端发起请求,发送SYN,seq=x,然后状态变为syn-sent状态。

服务器端接收到返回syn和ack,seq=x,ack =x+1,然后状态变成syn-rcvd状态。

客户端收到后,发送ack,seq=x+1,ack=y+1给服务器端,状态变为established,服务器收到后,状态变成established。

在连接过程中,需要对端确认的,需要消耗TCP报文的序列号。SYN消耗一个序列号而ACK不需要。

对于连接四次握手多余,二次握手,会带来资源的浪费,当遇到丢包,重传,连接关闭后,丢包到达服务端,就默认建立连接,可客户端以及关闭,所以三次握手就可以了。

63.四次挥手断开连接

TCP 四次挥手的过程

三次挥手,当服务器将ack和fin合并为一次挥手,会导致长时间的延迟,以至于客户端误认为fin没有到达客户端,让客户端不断重发fin。

64.TCP 滑动窗口

TCP 滑动窗口:

  1. 发送窗口

  1. 接收窗口

65.TCP 的拥塞控制?

TCP连接,拥塞控制:

  1. 拥塞窗口(Congestion Window,cwnd)
  2. 慢启动阈值(Slow Start Threshold,ssthresh)
TCP/IP协议四层
  1. 应用层决定了向用户提供应用服务时通信的活动。
  2. 传输层对上层应用层,提供处于网络连接中两台计算机之间的数据传输。
  3. 网络层用来处理在网络上流动的数据包。
  4. 链路层,用来处理连接网络的硬件部分。
  • HTTP协议的职责,生成对目标web服务器的HTTP请求报文
  • tcp协议的职责,为了方便通信,将HTTP请求报文分割成报文段
  • IP协议的职责,搜索对方的地址,一边中转一边传送
  • TCP协议的职责,从对方那里接收到的报文段,重组到达的报文段,按序号以原来的顺序重组请求报文

66.了解一下DNS

DNS是域名解析系统,它的作用非常简单,就是根据域名查出对应的IP地址。

  • 从根域名服务器查到顶级域名服务器的NS记录和A记录,IP地址
  • 从顶级域名服务器查到次级域名服务器的NS记录和A记录,IP地址
  • 从次级域名服务器查出主机名的IP地址

参考文献

点关注,不迷路

愿你遇到那个心疼你付出的人~

囊括前端Vue、JavaScript、数据结构与算法、实战演练、Node全栈一线技术,紧跟业界发展步伐,一个热爱前端的达达程序员。

好了各位,以上就是这篇文章的全部内容,能看到这里的人都是人才。我后面会不断更新网络技术相关的文章,如果觉得文章对你有用,欢迎给个“赞”,也欢迎分享,感谢大家 !!

喜欢本文的朋友们,欢迎长按下图关注公众号达达前端,收看更多精彩内容

查看原文

赞 87 收藏 65 评论 3

hehe 收藏了文章 · 7月25日

连肝7个晚上,总结了计算机网络的知识点!(共66条)

前言

计算机网络知识,是面试常考的内容,在实际工作中也常常会涉及到。

最近总结了66条计算机网络相关的知识点,大家一起看一下吧:

1.比较http 0.9和http 1.0😀

  1. http0.9只是一个简单的协议,只有一个GET方法,没有首部,目标用来获取HTML。
  2. HTTP1.0协议大量内容:首部,响应码,重定向,错误,条件请求,内容编码等。

http0.9流程:

客户端,构建请求,通过DNS查询IP地址,三次握手建立TCP连接,客户端发起请求,服务器响应,四次挥手,断开TCP连接。(与服务器只有一个来回)

http1.0流程:

客户端,构建请求,通过DNS查询IP地址,三次握手建立TCP连接,客户端发起请求,服务器响应,四次挥手,断开TCP连接。(与服务器有两个来回)

因为不足缺陷,就有了http1.1。

2.关于http1.1以及http2😁

http1.1中浏览器再也不用为每个请求重新发起TCP连接了,增加内容有:缓存相关首部的扩展,OPTIONS方法,Upgrade首部,Range请求,压缩和传输编码,管道化等。但还是满足不了现在的web发展需求,so,就有了http.2版本。

http2解决了(管道化特性可以让客户端一次发送所有的请求,但是有些问题阻碍了管道化的发展,即是某个请求花了很长时间,那么队头阻塞会影响其他请求。)http中的队头阻塞问题。

使用http2会比http1.1在使用TCP时,用户体验的感知多数延迟的效果有了量化的改善,以及提升了TCP连接的利用率(并行的实现机制不依赖与服务器建立多个连接)

所以需要学习http2,了解更过的内容来掌握计算机网咯。

对于http2,你可以来运行一个http2的服务器,获取并安装一个http2的web服务器,下载并安装一张TLS证书,让浏览器和服务器通过http2来连接。(从数字证书认证机构申请一张证书)。

了解http2的协议,先让我们了解一下web页面的请求,就是用户在浏览器中呈现的效果,发生了些什么呢?

资源获取的步骤:

把待请求URL放入队列,判断URL是否已在请求队列,否的话就结束,是的话就判断请求域名是否DNS缓存中,没有的话就解析域名,有的话就到指定域名的TCP连接是否开启,没有的话就开启TCP连接,进行HTTPS请求,初始化并完成TLS协议握手,向页面对应的URL发送请求。

接收响应以及页面渲染步骤:

接收请求,判断是否HTML页面,是就解析HTML,对页面引用资源排优先级,添加引用资源到请求队列。(如果页面上的关键资源已经接收到,就开始渲染页面),判断是否有还要继续接收资源,继续解析渲染,直到结束。

3.HTTP的几种请求方法用途😂

第一种GET方法:发送一个请求来获取服务器上的某一些资源。

第二种POST方法:向URL指定的资源提交数据或附加新的数据。

第三种PUT方法:跟POST方法一样,可以向服务器提交数据,但是它们之间也所有不同,PUT指定了资源在服务器的位置,而POST没有哦。

第四种HEAD方法:指请求页面的首部。

第五种DELETE方法:删除服务器上的某资源。

第六种OPTIONS方法:它用于获取当前URL所支持的方法,如果请求成功,在Allow的头包含类似GET,POST等的信息。

第七种TARCE方法:用于激发一个远程的,应用层的请求消息回路。

第八种CONNECT方法:把请求连接转换到TCP/TP通道。

4.从浏览器地址栏输入url到显示页面的步骤🤣

简单说说,浏览器根据请求的url交给dns域名解析,查找真正的ip地址,向服务器发起请求;服务器交给后台处理后,返回数据,浏览器会接收到文件数据,比如,html,js,css,图像等;然后浏览器会对加载到的资源进行语法解析,建立相应的内部数据结构;载入解析到得资源文件,渲染页面,完成显示页面效果。

🙅不够清楚明白码?

那就再次详细一下,咳咳,从浏览器接收url,开始进行网络请求线程,发出一个完整的HTTP请求,从服务器端接收请求到对应的后台接收到请求,然后是后台和前台的http交互;其中的缓存问题(http的缓存),浏览器接收到http数据包后的解析流程,css的可视化格式模型,js引擎解析过程等;其他呈现页面效果。

🙅:这里就需要你对浏览器内核的理解:其中主要的渲染引擎和JS引擎,这里了解一下你对浏览器内核的理解。

  1. 渲染引擎,是负责取得网页的内容,整理信息,以及计算网页的显示方式,然后输出到显示器上。
  2. JS引擎是用于解析和执行javascript来实现网页的动态效果。
浏览器的内核的不同对于网页的语法解释会有不同,所以渲染的效果也不相同。其实最开始渲染引擎和JS引擎是没有区分明确的,不过后来JS引擎越来越独立,so,内核就倾向于渲染引擎。

对于资源请求/获取,资源响应/页面渲染,会给网络带宽和设备资源带来压力,这个时候就会考虑到web的性能优化。

5.web的性能优化😃

其中里面的性能关键:

什么是数据包
数据包(IP数据包),指封装在固定结构的一系列字节,它定义了数据包的长度,传输的细节,以及其他与TCP相关的信息。

延迟:指IP数据包从一个网络端点到另一个网络端点所花费的时间。(所花费时间在于往返时延,是延迟的时间的两倍)

带宽:只要带宽没有饱和,两个网络端点的连接会一次处理尽可能多的数据量(所以带宽可能会成为性能的瓶颈)

建立连接时间:在客户端和服务器之间建立连接往返数据(三次握手)

TCP三次握手过程:客户端向服务器发起一个SYN包,服务器端返回对应的SYN的ACK响应以及新的SYN包,然后客户端返回对应的ACK。(在客户端和服务器之间建立正常的TCP网络连接时,客户端首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收了这个消息,最后客户端再以ACK消息响应。)

SYN是同步序列编号,是TCP/IP建立连接时使用的握手信息。
ACK是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。在TCP/IP协议中,如果接收方成功的接收到数据,那么会回复一个ACK数据。通过ACK信号有自己固定的格式,长度大小,由接收方回复给发送方。

详解三次握手

第一次握手,建立连接时,客户端发送SYN包到服务器,并进入SYN_SENT状态,等待服务器确认,其中SYN就是同步序列编号。

第二次握手,服务器收到SYN包,必须确认客户的SYN,同时自己也发送一个SYN包,即是SYN+ACK包,此时服务器进入SYN_RECV状态。

第三次握手,客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

TLS协商时间(TLS会造成额外的往返传输)

  1. 客户端发起https连接,需要进行传输层安全协议协商
  2. TLS用来取代安全套接层SSL

除了网络,还有页面内容本身或服务器性能,如首字节时间TTFB,内容下载时间,开始渲染时间,文档加载完成的时间等。

那么什么是TTFB,它是指客户端从开始定位到web页面,至接收到主体页面响应的第一字节所耗费的时间。它是测量:从浏览器发起请求至收到其第一字节之间的耗时。

内容下载时间是等同于被请求资源的最后字节到达时间。

开始渲染时间,从客户看到空白页面的时长。

5.1web性能优化技术(减少客户端网络延迟和优化页面渲染性能来提升web性能)

优化技术:

  • DNS查询优化
  • 客户端缓存
  • 优化TCP连接
  • 避免重定向
  • 网络边缘的缓存
  • 条件缓存
  • 压缩和代码极简化
  • 图片优化

6. http1.1😄

  • 改进持久连接和CDN域名的分片机制
  • 不成熟的http管道化
  • 提供虚拟主机支持
  • 对动态生成的内容完美支持
  • 引入cookie以及安全机制

对于http1的问题,迎来了http2。其中http1的问题:

队头阻塞,大多数情况下,浏览器会希望同时获取许多资源,但http1未提供机制来同时请求这些资源,如果仅是使用一个连接,需要发起请求,等待响应,然后才能发起下一个请求。

在http1中要给特性为管道化,可以允许一次发送一组请求,但是需要按照发送顺序依次接收响应。所以在请求应答过程中,如发生什么情况,剩下的工作都会被阻塞,这就是“队头阻塞”(阻塞在那次请求应答发生错误),阻碍网络传输和web页面的渲染,指导失去响应。

低效的TCP利用,TCP协议作为最可靠的协议之一,其核心是拥塞窗口。

拥塞窗口,是卫星通信在因特网中防止通信拥塞的一种措施,它是在发端采用了一种“拥塞避免”算法和“慢速启动”算法相结合的机制。“拥塞窗口”就是“拥塞避免”的窗口,它是一个装在发送端的可滑动窗口,窗口的大小是不超过接收端确认通知的窗口。

拥塞窗口指在接收方确认数据包之前,发送方可以发送的TCP包的数据。(如拥塞窗口指定为1的情况,那么发送方就发出1哥数据包之后,只有接收方确认了那个发出的数据包,才能发送下一个)

拥塞控制能防止过多的数据注入到网络中,用于避免网络过载,TCP中可以通过慢启动探索当前连接对应拥塞窗口的合适大小。即发送者发送数据的时候并非一开始注入大量数据到网络中,而是发送一个数据包进行测试,当得到确认回复后,额外发送一个未确认包。

这意味着得到一个确认回复,可以发送两个数据包,得到两个确认回复,可以发送四个数据包,以几何形式增长很快到达协议规定的拥塞窗口大小(发包数上限),这时候连接进入拥塞避免阶段,这种机制需要往返几次才能得知最佳拥塞窗口大小,但往返几次所需的时间成本不可忽略。

  • 拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口。如果再考虑到接收方的接收能力,那么发送窗口还可能小于拥塞窗口。
  • 发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减少一些,以减少注入到网络中的分组数。
tcp中的慢启动概念,是用来探索当前连接对应拥塞窗口的合适大小。用来弄清楚新连接当前的网络情况。“慢速启动”是在连接建立后,每收到一个来自收端的确认,就控制窗口增加一个段值大小,当窗口值达到“慢速启动”的限值后,慢速启动便停止工作,避免了网络发生拥塞。

TCP传输控制协议的设计思路是,对假设情况很保守情况下,能够公平对待同一网络的不同流量的应用,它的避免拥塞机制被设计城即使在最差的网络情况下也可以起作用。

臃肿的消息首部,HTTP/1.1能压缩请求内容,但是消息首部却不能压缩。它可能占据请求的绝大部分(也可能是全部)也是比较常见了。(在这里如果能压缩请求首部,把😙请求变得更小,就能够缓解带宽压力了,降低系统的总负载)

受限的优先级设置,即如果浏览器针对指定域名开启多个socket请求,若web页面某些资源会比另外一些资源重要,会加重资源的排队效应,会延迟请求其他的资源,优先级高的资源先获取,优先级低的资源会在资源高的资源处理完成,(在处理过程中,浏览器不会发起新的资源请求)等待高的完成后再发起请求,(这就会让总的页面下载时间延长)。

在请求优先级高的资源的时间区间内浏览器并不会发起优先级较低的新请求

小结:HTTP1.1慢启动影响资源首次加载速度,TCP建立连接后,会开始请求传输,开始比较慢,然后不断加快,为了防止出现网络拥堵,会让页面的首次渲染时间变长。开始多个tcp,如出现网络下降,无法识别资源的优先级,会出现竞态问题。

7.如何进行网站性能优化😅

  1. 内容方面,减少Http请求(合并文件,css精灵,inline Image),减少DNS查询(DNS缓存,将资源分布到合适的数量的主机名),减少DOM元素的数量。
  2. Cookie方面,可以减少Cookie的大小。
  3. css方面,将样式表放到页面顶部;不使用css表达式;使用<link>不使用@import;可将css从外部引入;压缩css。
  4. JavaScript方面,将脚本放到页面底部;将JavaScript从外部引入;压缩JavaScript,删除不需要的脚本,减少DOM的访问。
  5. 图片方面,可优化css精灵,不要再HTML中拉伸图片,优化图片(压缩)。

8.http状态码以及含义😆

  1. 对于1xx的状态码,为信息状态码,100 为继续,表示确认,成功返回具体参数信息。
  2. 对于2xx的状态码,200 表示正常返回信息,201表示请求成功并且服务器创建了新的资源,202表示服务器已接受请求,但尚未处理。
  3. 对于3xx,重定向,301表示,请求的网页已永久移动到新位置,302表示,临时性重定向,303表示临时性重定向,且总是使用 GET 请求新的 URI。304表示,自从上次请求后,请求的网页未修改过。
  4. 对于4xx,客户端错误,404,服务器无法理解请求的格式,客户端不应当尝试再次使用相同的内容发起请求,401,请求未授权,403,禁止访问,404,找不到如何与 URI 相匹配的资源。
  5. 对于5xx,服务器错误,500,最常见的服务器端错误,503,服务器端暂时无法处理请求,可能是过载或维护。

9.http-数据压缩😉

数据压缩,在浏览器中发送请求时会带着Content-Encoding: gzip,里面时浏览器支持的压缩格式列表,有多种如,gzip,deflate,br等。这样服务器就可以从中选择一个压缩算法,放进Content-Encoding响应头里,再把原数据压缩后发给浏览器。

10.http-分块传输😊

分块传输,就是将传输的文件分解成多个小块,然后分发给浏览器,浏览器收到后再重新组装复原。

每个分开包含两个部分,分块长度和分块数据(长度头和数据块),长度头以CRLF结尾的一行明文,数据块紧跟在长度头后面,也是用CRLF结尾,最后用一个长度为0的块表示结束。

在响应报文里用头字段Transfer-Encoding:chunked表示报文里的body部分不是一次性发送过来的,而是分成了许多块逐个发送的。

在Transfer-Encoding:chunked和Content-Length中,这两个字段是互斥的。

一个响应报文的传输长度要么已知,要么长度未知(chunked)。

Content-Length: 299

11.http-范围请求😋

断点续传

要实现该功能需要制定下载的实体范围,这种制定范围发送请求叫做范围请求。

Accept-Ranges:服务器使用http响应头Accept-Ranges标识自身支持范围请求,字段的具体值用于定义范围请求的单位。

语法

Accept-Ranges: bytes,范围请求的单位是 bytes (字节)
Accept-Ranges: none,不支持任何范围请求单位

范围请求时用于不需要全部数据,只需要其中的部分请求时,可以使用范围请求,允许客户端在请求头里使用专用字段来表示只获取文件的一部分。

Range的格式,请求头Range是HTTP范围请求的专用字段,格式是“bytes=x-y”,以字节为单位的数据范围。

  1. “0-”表示从文档起点开始到文档结束的整个文件。
  2. “100-”表示从第100哥字节开始到文档末尾。
  3. “-10”表示从文档末尾倒数的第10个字节开始。

示例:

执行范围时会使用头部字段 Range 来指定资源 byte 的范围。
Range格式:
5001-10000字节
Range : byte = 5001-10000
5000之后的
Range : byte = 5001-
0-3000字节,5001-10000字节
Range : byte=-3000,5001-10000

上图表示服务器收到Range字段后,检测范围合法性,范围越界,就会返回状态码416,如你的文件只有1000个字节,但请求范围在20000-3000,就会导致这个状态码的出现。

如果成功读取文件,范围正确,返回状态码“206”。服务器要添加一个响应头字段Content-Range,告诉片段的实际偏移量和资源的总大小。

最后是发送数据,直接把片段用TCP发给客户端,一个范围请求就算是处理完了。

格式是“bytes x-y/length”,与Range头区别在没有“=”

Content-Range: bytes 0-4395719/4395720

12.http-多段数据😎

多端数据,就是在Range头里使用多个“x-y",一次性获取多个片段数据。使用一种特殊的MIME类型:“multipart/byteranges”,用来表示响应报文包含了多个范围时使用。多重范围请求 响应会在头部 Content-Type 表明 multipart-byteranges。

多段数据图:分隔标记boundary来区分不同的分段

13.说一说cookies,sessionStorage 和 localStorage 的区别?😍

  • cookie是网站用来标识用户身份而存储在用户本地终端上的数据
  • cookie数据始终在同源的http请求中携带,即使是不需要的情况,so,会在浏览器和服务器间来回传递
  • sessionStorage和localStorage不会自动把数据发送给服务器,仅仅在本地保存
存储的大小

cookie的数据大小不能超过4k;sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或者更大。

有限期时间
  1. localStorage存储持久数据,浏览器关闭后数据不会丢失,除了主动删除数据
  2. sessionStorage数据在当前浏览器窗口关闭后自动删除
  3. 设置得cookie过期时间之前都有效,就算窗口或者是浏览器关闭

14.为什么说利用多个域名来存储网站资源会更有效?😘

因为CDN缓存更方便;突破浏览器并发限制;节约cookie带宽;节约主域名得连接数,优化页面响应速度;防止不必要得安全性问题。

15.http2.0的内容🥰

http2是超文本传输协议的第二版,相比http1协议的文本传输格式,http2是以二进制的格式进行数据传输的,具有更小的传输体积以及负载。

http2.0分层,分帧层(http2多路复用能力的核心部分),数据或http层(包含传统上被认为是 HTTP 及其关联数据的部分)。

HTTP2.0:

  • 多路复用机制,引入了二进制的分帧层机制来实现多路复用。(分帧层是基于帧的二进制协议。这方便了机器解析。请求和响应交织在一起。)
  • 可以设置请求的优先级(客户端的分帧层对分割块标上请求的优先级)。
  • 头部压缩 请求头压缩,增加传输效率。
HTTP/2较HTTP/1.1优化亮点
  • 多路复用的流
  • 头部压缩
  • 资源优先级和依赖设置
  • 服务器推送
  • 流量控制
  • 重置消息

多路复用的实现:

在单个域名下仍可以建立一个TCP管道,使用一个TCP长连接,下载整个资源页面,只需要一次慢启动,并且避免了竞态,浏览器发起请求,分帧层会对每个请求进行分割,将同一个请求的分割块打上相同的id编号,然后通过协议栈将所有的分割体发送给服务器,然后通过服务器的分帧层根据id编号进行请求组装,服务器的分帧层将回应数据分割按同一个回应体进行ID分割回应给客户端,客户端拼装回应。

对于http2中的帧(frame),http1不是基于帧(frame)的,是文本分隔的。

GET/HTTP/1.1 <crlf>

这样,对于http1的请求或者是响应可能有的问题:

  1. 一次只能处理一个请求或者是响应,完成之前是不能停止解析的。
  2. 无法预判解析需要多少内层。

HTTP/1 的请求和响应报文,是由起始行、首部和正文组成,换行符分隔;HTTP/2是将请求和响应数据分割成更小的帧,采用二进制编码,易于解析的。

参考图片:

帧结构总结
所有的帧都包含一个9 byte的帧头 + 可边长的正文不同。根据帧的类型不同,正文部分的结构也不一样。

帧头:

16.http2-幕后😗

http2作为一个二进制协议,拥有包含轻量型,安全和快速在内的所有优势,保留了原始的http协议语义,对于http2更改了在系统之间传输数据的方式。

二进制分帧层(binary framing layer),所有通信都在单个TCP连接上执行,该连接在整个对话期间一直处于打开状态,主要是二进制协议将通信分解为帧的方式,这些帧交织在客户端与服务器之间的双向逻辑流中。

HTTP/2 连接的拓扑结构(展示了一个用于建立多个流的连接)

在流 1 中,发送了一条请求消息,并返回了相应的响应消息。

HTTP/2 帧结构

前9个字节对于每个帧是一致的。解析时只需要读取这些字节,就可以准确地知道在整个帧中期望的字节数。

帧首部字段表格:

名称长度描述
length3字节表示帧负载的长度
type1字节当前帧类型
Flags1字节具体帧类型的标识
R1位保留位,不要设置,否则会带来严重后果
Stream Identifier31位每个流的唯一ID
Frame Payload长度可变真实的帧内容,长度是在length字段中设置的

备注:流Id是用来标识帧所属的流。流看作在连接上的一系列帧,它们构成了单独的 HTTP 请求和响应。

对于http1 的请求和响应都分成消息首部和消息体两部分;http2 从上面一张图可以知道,http2的请求和响应分成HEADERS 帧和 DATA 帧。

比较一下:👇

http2的一个重要特性是基于流的流量控制。提供了客户端调整传输速度的能力。其中WINDOW_UPDATE 帧用来指示流量控制信息。

有了多路复用,客户端可以一次发出多有资源的请求,不用像http1那样,发出对新对象请求之前,需要等待前一个响应完成。所以浏览器失去了在Http1中的默认资源请求优先级策略。

17.浏览器生成http请求消息😙

http的头字段

头字段类型含义
Date表示请求和响应生成的日期
Pragma表示数据是否允许缓存的通信选项
Cache-Control控制缓存的相关信息
Connection设置发送响应之后TCP连接是否继续保持的通信选项
Transfer-Encoding表示消息主体的编码格式
Via记录途中经过的代理和网关
Authorization身份认证数据
From请求发送者的邮件地址
Referer当通过点击超级链接进入下一个页面时,在这里会记录下上一个页面的URI
User-Agent客户端软件的名称和版本号等相关信息
Accept客户端可支持的数据类型,以MIME类型来表示
Accept-Charset客户端可支持的字符集
Accept-Language客户端可支持的语言
Host接收请求的服务器ip地址和端口号
Range当需要只获取部分数据而不是全部数据时,可通过这个字段指定要获取的数据范围
Location表示信息的准确位置
Server服务器程序的名称和版本号等相关信息
Allow表示指定的URI支持
Content-Encoding当消息体经过压缩等编码处理时,表示其编码格式
Content-Length表示消息体的长度
Content-Type表示消息体的数据类型,以MIME规格定义的数据类型来表示
Expires表示消息体的有效期
Last-Modified数据的最后更新日期
Content-Language表示消息体的语言
Content-Location表示消息体在服务器上的位置
Content-Range当仅请求部分数据时,表示消息体包含的数据范围

HTTP消息示例:

  1. HTTP,超文本传送协议。
  2. 协议,通信操作的规则定义称为协议。
  3. URI,统一资源标识符。
  4. 1 条请求消息中只能写 1 个 URI。如果需要获取多个文件,必须

对每个文件单独发送 1 条请求。

IP 的基本思路

Ip地址的表示方法

IP地址的结构-子网掩码表示网络号与主机号之间的边界。

解析器的调用方法

DNS服务器的基本工作

DNS 服务器之间的查询操作

数据通过类似管道的结构来流动

18.了解网络基础知识🙂

  • 物理层
  • 数据链路层
  • 网络层
  • 传输层
  • 会话层
  • 表示层
  • 应用层

计算机网络,可以将规模分WAN,Wide Area Network广域网,和LAN局域网。通过电脑连接交换机再到路由器的连接。

你知道计算机与网络都经历了怎么样的一个发展过程吗?
  1. 批处理就是指事先将用户程序和数据装入卡带或磁带,由计算机按照一定的顺序读取,使用户所要执行的这些程序和数据能够一并批量得到处理的方式。

  1. 分时系统,是指多个终端与同一个计算机连接,允许多个用户同时使用一台计算机的系统。

  1. 计算机网络

TCP/IP的机制是什么,TCP/IP通信协议的统称,学习这个有人一定🙅不了解什么是协议。

但我们在接触到程序时,常常听到协议如IP,TCP,HTTP等协议。记住TCP/IP就是IP,TCP,HTTP等协议的集合。协议就是计算机与计算机之间通过网络实现通信时需要达成的一种的“约定”。这些协议就是让不同厂商的设备,不同的CPU和不同的操作系统组成的计算机之间进行通信。

就是两台计算机之间都能支持相同的协议,并遵循才能实现相互通信。

分组交换协议

分组交换就是将大数据分割成一个一个叫做包的较小单位进行传输的方法。

分层模块

了解OSI参考模型

OSI将分为易于理解的7层:

1.物理层,2.数据链路层,3.网络层,4.传输层,5.会话层,6.表示层,7.应用层。

应用层:是对特定应用的协议。

表示层:设备固有数据格式和网络标准数据格式的转换。

会话层:通信管理。负责建立和断开通信连接。

传输层:管理两个节点之间的数据传输。

网络层:地址管理与路由选择。

数据链路层:互连设备之间传送和识别数据帧。

物理层:以“0”,“1”代表电压的高低,灯光的闪灭。

如何模块化通信传输

网络构成要素

网卡:

什么是网关,它是OSI参考模型中负责将从传输层到应用层的数据进行转换和转发的设备。

代理服务:

19.有哪些渲染优化呢?😝

第一,我们可以禁止使用iframe,第二,可以禁止使用gif图片来实现loading效果,降低CPU的消耗,来提升渲染性能,第三,使用CSS3代码来代替JS动画。

对于一些小图标,可以使用base64位编码,以减少网络请求,但不建议大图使用,因为比较耗费CPU,小图标优势在于,可以减少HTTP请求,避免文件跨域,修改及时生效。

页面头部的style和script会阻塞页面,在Renderer进程中的JS线程和渲染线程是互斥的。

20.学习TCP和IP的基础知识🤤

TCP/IP协议族市一组协议的集合,也称为互联网协议族。

20世纪60年代后半叶,应DoD要求,美国开始进行通信技术相关的演技,ARPANET的诞生,开发分组交互技术,在1975年,TCP/IP的诞生。1983年,ARPANET决定正式启用TCP/IP为通信协议。

TCP/IP与OSI参考模型

对于OSI七层模型太细了,而互联网协议族TCP/IP模型划分为四层。

TCP/IP模型(应用层,传输层,互联网层,网络接口层)-应用层,传输层,网络层,链路层。

传输层就是可以让应用程序之间实现通信。

在其中TCP是一种面向有连接的传输层协议,保证两端通信主机之间的通信可达。UDP是一种面向无连接的传输层协议,so,UDP用于分组数据较少或者多播,广播通信以及视频通信等领域。

应用层

21.面试题:TCP/IP市如何在媒介上进行传输的呢?😑

在不同层次的协议✍

数据包首部:

以太网包首部:IP包首部,TCP包首部,数据

IP包首部:TCP包首部,数据

TCP包首部:数据

每个分层中,都会对所发送的数据附加一个首部,它包含了该层中必要的信息。(发送的目标地址,协议相关的信息等)
  • 包是全能性术语
  • 帧是数据链路层中包的单位
  • 数据包,IP和UDP等网络层以上的分层中包的单位
  • 段,表示TCP数据流中的信息
  • 消息,应用协议中数据的单位

数据包的首部,明确表明了协议应该如何读取数据。掌握数据包首部,通常,为协议提供的信息为包首部,所要发送的内容为数据。

发送数据包,TCP/IP通信流程:🤔

  1. 应用程序处理,发送通信开始TCP/IP通信,应用程序会进行编码处理,编码相当于OSI中的表示层功能。
  2. TCP模块的处理,TCP负责建立连接,发送数据以及断开连接,TCP提供将应用层发来的数据顺利发送至对端的可靠传输。在应用层数据的前端附加一个TCP首部,它包含源端口号和目标端口号,序号以及校验和(用来判断数据是否被破坏)然后附加一个TCP首部的包再发给IP。
  3. IP模块的处理,在TCP首部的前端加上自己的IP首部,它包含接收端IP地址和发送端IP地址。若不知道接收端的MAC地址,可以用ARP查找,只要知道对端MAC地址,就可以将MAC以及IP地址交给以太网的驱动程序,来实现数据传输。
  4. 网络接口的处理,从IP传过来的IP包,然后附加上以太网首部并进行发送处理,以太网首部包含接收端的MAC地址,发送端的MAC的地址,以及标志以太网类型的以太网数据的协议。
数据包,经过以太网的数据链路时,大致上附加了以太网包首部,IP包首部,TCP包首部或者UDP包,以及应用自己的包首部和数据,最后包追加了包尾。

分层中包的结构

数据包接收流程🙄
  1. 网络接口的处理,主机收到以太网包后,从以太网的包首部找到MAC地址判断是否为发给自己的,若不是就丢弃,如果是,就查找以太网包首部中的类型域从而确定以太网协议所传送过来的数据类型。
  2. 通过IP模块处理,然后TCP模块处理(需要判断是否被破坏),检查是否按照序号接收数据。当数据接收完毕后,会发送“确认回执”给发送端。注意,这里的回执信息未能达到发送端,那么发送端会认为没有收到而一直反复发送。
  3. 应用程序的处理,接收端应用程序会直接接收发送端发送的数据信息。

22.了解一下http-http3.0😶

在http2.0中,TCP管道传输途中也会导致丢包问题,造成队头阻塞(在http2.0中的TCP建立连接三次握手,和HTTPS的TSL连接也会耗费较多时间)

其实多路复用技术可以只通过一个TCP连接就可以传输所有的请求数据。

http3中弄了一个基于UDP协议的QUIC协议,QUIC虽说基于UDP,但是在基础上添加了很多功能。QUIC(快速UDP网络连接)是一种实验性的网络传输协议,由Google开发,该协议旨在使网页传输更快。

对于在http中的缺点就是延迟,浏览器的阻塞,在对同一域名,同时只能连接4个,超过了浏览器的最大连接限数时,后面的请求就会被阻塞;DNS的查询就是将域名解析为IP,来向目标服务器的IP建立连接,其中通过DNS缓存可以达到减少时间的作用;建立连接,HTTP是基于tcp协议的,三次握手,每次连接都无法复用,so,会每次请求都要三次握手和慢启动,都会影响导致延迟。(慢启动对大量小文件请求影响较大)

http处于计算机网络中的应用层,建立在TCP协议之上。(掌握了解tcp建立连接的3次握手和断开连接的4次挥手和每次建立连接带来的RTT延迟时间)。

相对于HTTP1.0使用了header里的if-modified-since,expires来做缓存判断,在HTTP1.1中引入了entity tag,if-unmodified-since,if-match,if-none-match等更多可供选择的缓存头来控制缓存策略。

http1.0传输数据时,每次都要重新建立连接,增加延迟,http1.1加入了keep-alive可以复用部分连接,但在域名分片等情况下仍要连接夺冠时连接,耗费资源,以及给服务器带来性能压力。

http1.1尝试使用pipeling来解决问题,就是浏览器可以一次性发出多个请求,在同一个域名下,同一条TCP连接,但对于pipeling要求返回是按照顺序的,即(如果前面有个请求很耗时的话,后面的请求即使服务器已经处理完,任会等待前面的请求处理完才开始按序返回。)

在http1.x中,Header携带内容过大,增加了传输的成本,在传输的内容都是明文,在一定程度上无法保证其数据的安全性。(在http1.x问题的出现,有了SPDY协议,用于解决http/1.1效率不高的问题,降低延迟,压缩Header等)

HTTP2主要解决用户和网站只用一个连接(同域名下所有通信都只用单个连接完成,单个连接可以承载任意数量的双向数据流,数据流是以消息的形式发送,消息由一个或多个帧组成)。

so,http采用二进制格式传输数据,不像http1.x的文本格式。(二进制:http2将请求和响应数据分割成帧,并且它们采用二进制的编码),对于HTTP2的概念:(流,消息,帧)

  1. 流,它是连接中的一个虚拟信道;
  2. 消息,它是HTTP消息,请求,以及响应;
  3. 帧,它是HTTP2.0通信的最小单位。
多个帧可以乱序发送,可根据帧首部的标识流进行重新组装。

对于http2,同一域名下只需要使用一个TCP连接,那么当出现丢包时,会导致整个TCP都要开始等待重传。对于http1.1来说,可以开启多个TCP连接,出现这种情况指挥影响一个连接(或者部分),其余的TCP连接正常传输。

HTTP/2 对首部采取了压缩策略,为了减少资源消耗并提升性能。(因为在http1中,在header携带cookie下,可能每次都要重复传输数据)

so,有了QUIC协议,整合了TCP,TLS,和HTTP/2的优点,并加以优化。那么QUIC是啥,它是用来替代TCP,SSL/TLS的传输层协议,在传输层之上还有应用层。

注意,它是一个基于UDP协议的QUIC协议,使用在http3上。

QUIC 新功能


HTTPS 的一次完全握手的连接过程

QUIC可以解决传输单个数据流可以保证有序的交付,并且不会影响其他的数据流。(解决http2问题)

表示在QUIC连接中,一个连接上的多个stream,如其中stream1,stream2,stream3,stream4,其中stream2丢失(quic packet),其余UDP到达,应用层直接读取。--- 无需等待,不存在TCP队头阻塞,丢失的包需要重新传即可。

补充:

  1. TCP是基于IP和端口去识别连接的;
  2. QUIC是通过ID的方式去识别连接的

对于QUIC的包都是经过认证的,除了个别,so,这样,通过加密认证的报文,就可以降低安全风险。

HTTP2-TLS,TCP,IP

小结QUIC特点:(基于UDP)--- http3-QUIC,UDP,IP

  1. 多路数据流
  2. TLS
  3. 有序交付
  4. 快速握手
  5. 可靠性

23.网络中的UDP😛

UPD面向报文的协议,就是UDP只是报文的搬运工,不会对报文进行任何拆分和拼接操作,在发送端,应用层将数据传给传输层的UDP协议,UDP会给数据加一个UDP头标识下是UUDP协议,然后传给网络层。

接收端,网络层将数据传给传输层,UDP只去除IP报文头就传给应用层,不会任何拼接操作。

UDP是无连接,通信不需要建立和断开连接,UDP是不可靠的,不关心数据的安全等问题,UDP是没有拥塞控制,在网络条件不好的情况下可能会导致丢包。

传输:UDP 支持一对一,一对多,多对多,多对一的的传输方式, UDP 提供了单播,多播,广播的功能。

24.网络中的TCP😜

UDP没有TCP那么复杂,UDP头部开销小,但是TCP头部比UDP头部复杂得多,UDP头部只有8字节,相比TCP的至少20字节要少很多。

Sequence number

这个序号保证了TCP传输的报文都是有序的,对端可以通过序号顺序的拼接报文

Window Size

表示窗口大小,还能接收多少字节的数据

Acknowledgement Number

表示上一个序号的数据已经接收到,接收端期望接收的下一个字节的编号是多少

标识符

当ACK=1,表示确认号字段有效

当SYN=1,ACK=0时,表示当前报文段是一个连接请求报文

当SYN=1,ACK=1时,表示当前报文段是一个同意建立连接的应答报文

当FIN=1,表示此报文段是一个释放连接的请求报文

性能指标 RTT

表示发送端发送数据到接收到对端数据所需的往返时间

小结
  1. TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议
  2. UDP(User Data Protocol,用户数据报协议)是面向非连接的协议。

25.建立连接三次握手😕

建立连接开始时,两端都是CLOSED状态,通信开始前,双方都会创建 TCB,后进入 LISTEN 状态,开始等待客户端发送数据。

第一次握手

客户端向服务器端发送连接请求报文段,请求发送后,客户端进入SYN-SENT 状态。

第二次握手

服务端收到连接请求报文段后,发送完成后便进入 SYN-RECEIVED 状态。

第三次握手

客户端收到连接同意的应答后,要向服务端发送一个确认报文。客户端发完这个报文段后便进入ESTABLISHED 状态,服务端收到这个应答后也进入 ESTABLISHED状态,此时连接建立成功。

有人问了,两次握手就可以建立连接了,为啥要第三次呢?

因为防止失效的连接请求报文段被服务器端接收,从而导致错误。

26.http请求码有哪些?🤑

100为继续,一般发送post请求时,已经发送了http header之后服务端将返回此信息,表示确认,之后发送具体参数信息;201,请求成功并且服务器创建了新的资源;202,服务器已接受请求,但未处理。

301,请求的网页已经永久移动到新的位置;302,临时性重定向;303,临时性重定向,且总是使用GET请求新的URI;304,自从上次请求后,请求的网页未修改过。

404,服务器无法理解请求;401,请求未授权;403,禁止访问。

27.面试时,简单说说TCP传输的三次握手四次挥手😲

传输,为了准确无误地把数据传输给目标,TCP协议采用了三次握手策略,用TCP协议把数据包送出去后,会向对方确认是否成功达到,发送端发送一个带SYN标志的数据包给到对方,接收端收到后,会回传一个带有SYN/ACK标志的数据包表示传送到达的确认信息,然后发送端也再次回传一个带有ACK标志的数据包,表示“握手”结束了。

握手过程中使用的标志:SYN和ACK

断开一个TCP连接需要四次挥手:

第一次挥手

主动关闭的一方,发送一个FIN(上述讲过---当FIN=1,表示此报文段是一个释放连接的请求报文),传送数据,用来告诉对方(被动关闭方),说不会再给你发送数据了。---主动关闭的一方可以接受数据。

第二次挥手

被动关闭方 收到 FIN 包,发送 ACK 给对方,确认序号。

第三次挥手

被动关闭方 发送一个 FIN,关闭方,说我不会再给你发数据了。(你不给我发送数据,我也不给你发送数据了)

第四次挥手

主动关闭一方收到 FIN ,发送要给 ACK ,用来确认序号

28.常说的HTTPS🙁

其实HTTP协议时承载于TCP协议之上的,再HTTP和TCP之间添加一个安全协议层,SSL或者TSL(ssl/tls协议传输,包含证书,卸载,流量转发,负载均衡,页面适配,浏览器适配,refer传递等),则就是常说的HTTPS。

29.GET和POST的区别,何时使用POST?😖

  1. GET是用于信息获取,使用URL传递参数,发送信息的数量有限;
  2. POST是用于修改服务器上的资源;
  3. 一般使用POST,当无法使用缓存文件,向服务器发送大量的数据,发送未知的字符

30.面试问,HTTP协议的主要特点😨

  1. 简单快速
  2. 灵活
  3. 无连接
  4. 无状态

31.面试问,说说HTTP报文的组成部分😟

HTTP报文的组成部分包含:请求报文和响应报文

请求报文: 有请求行,请求头, 空行,请求体

响应报文: 有状态行,响应头,空行,响应体

请求报文包含:

1.请求方法,2.请求URL,3.HTTP协议以及版本,4.报文头,5.报文体

  • 请求行,有请求方法,请求URL,http协议以及版本;
  • 请求头,一堆键值对
  • 空行,当服务器在解析请求头的时候,遇到了空行,表明后面的内容是请求体
  • 请求体,请求数据
响应报文包含:

1.报文协议以及版本,2,状态码以及状态描述,3,响应头,4,响应体

  • 状态行:http协议和版本,状态码以及状态描述
  • 响应头
  • 空行
  • 响应体

32.面试时问,知道哪些HTTP方法😤

  1. GET方法获取资源
  2. POST方法传输资源
  3. PUT方法更新资源
  4. DELETE方法删除资源
  5. HEAD方法获得报文首部

33.持久链接😢

在http1.0中,客户端每隔很短时间会对服务器发出请求,查看是否有新的数据,只要轮询足够快,就可以造成交互实时进行,但这个做法,会对两端造成大量的性能浪费。

对于http1.1中的长连接,使用connection:keep-alive进行长连接,客户端只请求一次,但是服务器会将继续保持连接,再次请求时,避免了重新建立连接。

注意,keep-alive不会永久保持连接,只有保持一个时间段。

34.安全问题:CSRF和XSS😭

CSRF的基本概念,攻击原理,防御措施

CSRF(Cross-site request forgery):跨站请求伪造

理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。

以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账……造成的问题包括:个人隐私泄露以及财产安全。

CSRF的原理:(要完成一次CSRF攻击)

  • 登录受信任网站A,并在本地生成Cookie。
  • 在不登出A的情况下,访问危险网站B。

XSS的基本概念,跨域脚本攻击。

xss是一种发生在web前端的漏洞,所以其危害的对象也主要是前端用户。

跨域脚本攻击是,恶意攻击者往web页面里插入恶意的script代码,在浏览器中运行script代码,达到恶意攻击用户的目的。

so,实现xss攻击具备2个条件,第一需要向web页面注入恶意的代码,第二,这些恶意代码被浏览器成功的执行。

CSRF和XSS的区别:

  1. CSRF需要登录,获取COOKIE,利用网站本身的漏洞,去请求网站的api
  2. XSS,不需要登录,向网站注入JS代码,执行JS里的代码,篡改网站的内容

35.从一个HTTP请求来看网络分层原理

一个HTTP请求的分层解析流程:

TCP,它是面向连接的,可靠的,基于字节流的传输层通信协议。

特点:

  • 基于连接,数据传输之前需要建立连接
  • 全双工的,双向传输
  • 字节流,不限制数据大小,打包成报文段,保证有序接收,重复报文自动丢弃
  • 流量缓冲,解决双方处理能力的不匹配
  • 可靠的传输服务,保证可达,丢包时通过重发机制实现可靠性
  • 拥塞控制,防止网络出现恶性拥塞

TCP连接,源地址,源端口,目的地址,目的端口

从TCP-IP协议底层

滑动窗口协议与累计确认(延时ACK)

滑动窗口大小同通过tcp三次握手和对端协商,且受网络状况影响

36.HTTPS安全加密通道原理分析

什么是HTTPS协议,由于HTTP天生“明文”的特点,整个传输过程完全透明,任何人都能够在链路中截获,修改或者伪造请求、响应报文,数据不具有可信性。

使用HTTPS时,所有的HTTP请求和响应发送到网络前,都要进行加密。

https = http + ssl/tls

对称加密:加密 解密使用同一密钥

非对称加密:公钥-随意分发,私钥-服务器自己保持

公钥加密的数据,只能通过私钥解密
私钥加密的数据,只能公钥能解密

加密算法:

对称密钥加密算法,编,解码使用相同密钥的算法

非对称密钥加密算法,一个公钥,一个私钥,两个密钥是不同的,公钥可以公开给如何人使用,私钥是严格保密的。

加密通道的建立:

数字证书的申请和验证

如何申请:

  • 生成自己的公钥和私钥,服务器自己保留私钥
  • 向CA机构提交公钥,公司,域名信息等待认证
  • CA机构通过线上,线下多种途径验证你提交信息的真实性,合法性
  • 信息审核通过,CA机构则会向你签发认证的数字证书,包含了公钥,组织信息,CA信息,有效时间,证书序列号,同时生成了一个签名
  • 签名步骤:hash(用于申请证书所提交的明文信息)= 信息摘要
  • CA再使用CA机构的私钥对信息摘要进行加密,密文就是证书的数字签名

37.https的对称加密,非对称加密,混合加密,CA认证😨

HTTPS ,超文本传输安全协议,目标是安全的HTTP通道,应用是安全数据传输。HTTP协议虽然使用广,但是存在不小的安全缺陷,主要是数据的明文传送消息完整性检测的缺乏。

HTTPS协议是由HTTP加上TLS/SSL协议构建的可进行加密传输,身份认证的网络协议。

通过, 数字证书,加密算法,非对称密钥 等技术完成互联网数据传输加密,实现互联网传输安全保护。

HTTPS主要特性:

  1. 数据保密性
  2. 数据完整性
  3. 身份校验安全性

客户端和服务器端在传输数据之前,会通过基于证书对双方进行身份认证。客户端发起SSL握手消息给服务端要求连接,服务端将证书发送给客户端。客户端检查服务器端证书,确认是否由自己信任的证书签发机构签发,如果不是,将是否继续通讯的决定权交给用户选择,如果检查无误或者用户选择继续,则客户端认可服务端的身份。

服务端要求客户端发送证书,并检查是否通过验证,失败则关闭连接,认证成功,从客户端证书中获得客户端的公钥。

HTTP原理

客户端的浏览器首先要通过网络与服务器建立连接,该连接时通过TCP来完成的,一般TCP连接的端口号是80,建立连接后,客户端发送一个请求给服务器端;服务器端接收到请求后,给予相应的响应信息。

HTTPS原理

客户端将它所支持的算法列表和一个用作产生密钥的随机数发送给服务器,服务器从算法列表中选择一种加密算法,并将它和一份包含服务器公用密钥的证书发送给客户端,该证书还包含了用于认证目的的服务器标识,服务器同时还提供了一个用作产生密钥的随机数。

客户端对服务器的证书进行验证,并抽取服务器的公用密钥,再产生一个称作pre_master_secret的随机密码串,并使用服务器的公用密钥对其进行加密,并将加密后的信息发送给服务器。

客户端与服务器端根据pre_master_secret以及客户端与服务器的随机数独立计算出加密和MAC密钥。

混合加密

在传输数据中使用对称加密,但对称加密的密钥采用非对称加密来传输,混合加密比较安全,但无法知道数据是否被篡改

CA认证

CA认证, 即是电子认证服务,指电子签名相关各方提供真实性,可靠性验证的活动。

特性:参阅百度百科—简介,点击进入

38.https对比http🥶

http传输方式:明文传输,网站或相关服务与用户之间的数据交互无加密,容易被监听,篡改。

https传输方式:在HTTP加入了SSL层,用于数据传输加密。

http身份认证:无任何身份认证,用户无法通过http辨认出网站的真实身份。

https身份认证:经过CA多重认证,包含域名管理权限认证等。

http成本:无任何使用成本,所有网站默认是http模式。

https需要成本,需要申请SSL证书来实现https。

http连接端口:80端口。

https连接端口:443端口。

39.证书如何安全传输,被掉包了怎么办?😳

40.http3中QUIC😵

QUIC是谷歌制定的一种基于UDP的低时延的互联网传输层协议。

1、避免前序包阻塞;2、零RTT建连;3、FEC前向纠错

HTTP 的历史

HTTP/2 和 HTTP/3 建立连接的差别

TCP/建立连接与QUIC建立连接

队头阻塞/多路复用

HTTP/1.1 提出了 Pipelining 技术,允许一个 TCP 连接同时发送多个请求

请求与响应,与,Pipelining

http/1.1队头阻塞

HTTP/2 的多路复用解决了队头阻塞问题

拥塞控制:

  • 慢启动
  • 拥塞避免
  • 快速重传
  • 快速恢复

41.HTTP 协议入门

HTTP 基于TCP/IP 协议的应用层协议,不涉及数据包packet传输,主要客户端和服务器之间的通信格式,默认使用80端口。TCP连接建立后,客户端向服务器请求网页,协议规定,服务器只能回应HTML格式的字符串,不能回应别的格式。

http1.0可以传输文字,传输图像,视频,二进制文件;除了GET方法,还有POST,HEAD等;每次通信都需要 头信息HTTP header,状态码,多字符集支持,缓存,权限等。

字段:ontent-Type 字段

头信息必须是 ASCII 码,后面的数据可以是任何格式,字段值:

text/plain
text/html
text/css
image/jpeg
image/png
image/svg+xml
audio/mp4
video/mp4
application/javascript
application/pdf
application/zip
application/atom+xml

客户端请求的时候,使用Accept字段,表示可以接受哪些数据格式。

Accept: */*

Content-Encoding字段,表示数据的压缩方式

Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate

客户端在请求时,用Accept-Encoding字段说明接受哪些压缩方法。

Accept-Encoding: gzip, deflate

http1.0就是每个TCP连接只能发送一个请求,发送完毕后就关闭,so,为解决问题,用了一个非标准Connection字段,Connection:keep-alive。

HTTP/1.1引入了持久连接(persistent connection),TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。

也不是永久性不关闭的,只要有一段时间没有活动,就会关闭TCP连接,一般对于同一个域名,大多数浏览器允许同时建立6个持久连接。

1.1 版引入了管道机制(pipelining),同一个TCP连接里,可以同时发送多个请求。但是还是按照顺序,一个请求回应后,再回应另一个请求。(但也减少不小的消耗时间)。

使用分块传输编码,只要请求或回应的头信息有Transfer-Encoding字段

Transfer-Encoding: chunked

什么是多工?双向,实时的通信就叫 多工。

HTTP2 复用TCP连接,在一个连接里,两端都可以同时发送多个请求或响应,而且不用按照顺序一一对应,避免了“队头堵塞”。

http2引入了头信息压缩机,头信息使用gzip或compress压缩后再发送,客户端和服务器同时维护一张头信息表,所有字段存在这个表里,生成一个索引号,以后就只发送索引号,这样就提高速度了。

HTTP/2允许服务器未经请求,主动向客户端发送资源(服务器推送)

42.什么是cookie呢🥴

cookie是某网站为了辨别用户身份,进行session跟踪而存储在用户本地终端的数据(通常经过加密),由用户客户端计算机暂时或永久保存的信息。

  1. 存储在用户本地终端上的数据
  2. 用来辨别用户身份
  3. 保存在用户本地终端

    cookie是一些数据,存储在你电脑上的文本文件中,当web服务器向浏览器发送web页面时,在连接关闭后,服务端不会记录用户的信息,cookie的作用就是解决如何记录客户端的用户信息。

    场景:当用户访问web页面,用户信息记录在cookie中,当用户下一次访问页面后,可以在cookie中读取用户访问记录。

    cookie是以键值对形式存储的,当浏览器从服务器上请求web页面,该页面的cookie会被添加到请求中,服务端通过这种方式用来获取用户信息。

可以使用JavaScript来创建,读取,修改,删除cookie

使用document.cookie属性来创建,读取以及删除cookie

创建:

document.cookie = "username = dadaqianduan";

给cookie添加一个过期时间:

document.cookie = "username = dadaqianduan; expires=xxxxxx";

默认情况下,cookie属于当前页面:

document.cookie = "username = dadaqianduan; expires= ; path=/";

读取cookie

var x = document.cookie;

修改cookie

document.cookie = "username = dada; expires=xxx; path=/";

删除cookie, 把设置时间的expires 参数改为以前的时间即可。

document.cookie = "username = ; expires= xxx";

为什么会有cookie呢?因为http请求时无协议的,http1.x,无状态协议,客户端同一个请求发送多次,服务端并不能识别是不是同一个客户端发送,为了解决无状态,就有了cookie。

cookies是服务器暂存放在你的电脑里的资料,以.txt格式的文本文件,好让服务器用来辨认你的计算机,当你在浏览网站时,web服务器会发送一个小小的资料放在你的计算机上。

当你下一次访问同一个网站,web浏览器会先看看有没有它上次留下来的cookies资料,有的话就输出特定的内容给你。

cookie原理

浏览器第一次请求服务器,服务器响应请求中携带cookie,给浏览器,浏览器第二次请求,携带cookie,给服务器,服务器根据cookie辨别用户,也可以修改cookie内容。

domain时.baidu.com的cookie绑定到了域名商。跨域的域名不能写入在cookies文件里

cookie的属性有哪些

Name, Value, Domain, Path, Expires/Max-Age, Size, HttpOnly, Secure, SameSite

掌握面试中的HttpOnly,这个属性设置为true,就不能通过js脚本获取cookie的指,能有效防止xss的攻击。

Cookie中的HttpOnly和Secure中:

标记为Secure的Cookie只能被https协议加密过的请求发送给服务端。但也无法保证其安全保障。

如果cookie中设置了HttpOnly属性,通过js脚本将无法读取到cookie信息,有效防止xss的攻击,窃取cookie内容,增加了cookie的安全性,但是重要信息还是不要存储在cookie中。

因为xss为跨站脚本攻击,是web程序常见的漏洞,属于被动式且用于客户端的攻击方式

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

SameSite

SameSite Cookie允许服务器要求某个cookie在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。

示例:

Set-Cookie: key=value; SameSite=Strict

SameSite有三个值:

None: 浏览器在同站请求,跨站请求下继续发送cookies,不区分大小写。(所有三方的请求都会携带cookie)

Strict: 浏览器将只在访问相同站点时发送cookie。(所有三方的链接都不会携带cookie)

Lax: Same-site cookies 将会为一些跨站子请求保留,如图片加载或者frames的调用,但只有当用户从外部站点导航到URL时才会发送。(只有同步且是get请求才可携带cookie)

在https协议中,才能通过js去设置secure类型的cookie,在http协议的网页中是无法设置secure类型cookie的。默认情况,https协议还是http协议的请求,cookie都会被发送到服务端。

43.什么是token呢?🤬

token的出现,是在客户端频繁向服务端请求数据,服务端频繁的去数据库查询用户名和密码并进行对比,判断用户名和密码正确与否,并作出相应提示。token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,第一登录时,服务器生成一个token,将此token返回给客户端,客户端带上这个token,无需再次带上用户名和密码了。

token的出现减轻了服务器的压力,减少频繁地数据库查询。

token的优点
  • 无状态,可扩展
  • 安全性
  • 多平台跨域
  • 基于标准

基于Token的身份验证的过程

浏览器,输入userName, Password,到mysql,校验成功 生成token,将token返回给客户端,当客户端发起请求时,每次访问api都携带token到服务器端,经过过滤器,校验token,校验成功后返回请求数据,校验失败后返回错误码。

44.cookie,session,token😷

cookie,记录访问过的网站或正在访问的网站,对于HTTP 协议是无状态的,服务器不知道浏览器上一次访问做了什么,也无法对用户会话进行跟踪连接,所以,cookie是由服务器发送到客户端浏览器的一段文本文件,包含了网站访问活动信息。Cookie 存放在客户端,用来保存客户端会话信息;由于存储在客户端,它的安全性不能完全保证。

session表示是c/s架构中服务器和客户端一次会话的过程,用来保存认证用户信息。session是一种HTTP存储机制,提供持久机制。Session存放在服务器端,用户验证客户端信息。由于存储在服务器,安全性有一定的保证。

token是一种认证方式(是“令牌”的意思,主要是用于身份的验证方式。)

45.跨域🤒

网页的URL的协议、域名、端口有一个不同,就算是跨域了

跨域:JSONP

46.思维导图http小结

47.http中的字段🤠

  1. accept,数据格式,请求accept,响应,content-type,表示收到的数据格式
  2. accept,压缩方式,请求accept-encoding,响应,content-encoding,采用什么样的压缩方式
  3. accept,支持语言,请求accept-language,响应content-language
  4. accept,字符集,请求accept-charset,响应content-type,指定字符集
  5. accept,范围请求,请求if-range和range,响应accept-anges和content-range
  6. cookie,请求时传递给服务端的cookie信息
  7. set-cookie,响应报文首部设置要传递给客户端的cookie信息
  8. allow,支持什么HTTP方法
  9. last-modified,资源的最后修改时间
  10. expires,设置资源缓存的失败日期
  11. content-language,实体的资源语言
  12. content-encoding,实体的编码格式
  13. content-length,实体主体部分的大小单位是字节
  14. content-range,返回的实体的哪些范围
  15. content-type,哪些类型
  16. accept-ranges,处理的范围请求
  17. age,告诉客户端服务器在多久前创建了响应
  18. vary,代理服务器的缓存信息
  19. location,用于指定重定向后的URI
  20. If-Match,值是资源的唯一标识
  21. User-Agent,将创建请求的浏览器和用户代理名称等信息传递给服务器
  22. Transfer-Encoding,传输报文的主体编码方式
  23. connection,管理持久连接,keep-alive , close
  24. Cache-Control,控制浏览器的强缓存

48.如果面试问HTTP报文结构是什么,你能回答上来不?

对于 TCP 而言

起始行 + 头部 + 空行 + 实体
  1. 请求报文
GET /home HTTP/1.1
  1. 响应报文
HTTP/1.1 200 OK

空行是用来分开头部和实体。

49.如果面试问HTTP请求方法有哪些,你能回答上来不?🤥

  1. GET方法,用来获取资源
  2. POST方法,用来提交数据
  3. PUT方法,用来修改数据
  4. DELETE方法,用来删除资源
  5. OPTIONS方法,用来跨域请求
  6. HEAD方法,用来获取资源的元信息
  7. CONNECT方法,用来建立连接,用于代理服务器

50.如果面试问,你对URI是如何理解的,你能回答上来不?🤫

URL统一资源定位符,URI,统一资源标识符。URI用于区分网络上不同的资源。

URI包含了URN和URL。

URL的结构:

协议名,登录主机的用户信息,主机名和端口,请求路径,查询参数,URI上定位资源内的一个锚点。

51.如果面试问,你对HTTP状态码的了解有多少,你能回答上来不?

了解一些特定的HTTP状态码:

52.如果面试问,说说HTTP特点以及缺点,你能回答上来不?

特点是:

  1. 灵活可扩展
  2. 可靠传输
  3. 无状态等

缺点是:

  1. 无状态
  2. 明文传输
  3. 队头阻塞问题

53.如果面试问,说说你对Accept字段的理解,你能回答上来不?

  • 数据格式
  • 压缩方式
  • 支持语言
  • 字符集

54.如果面试问,什么是队头阻塞问题,你能回答上来不?🤭

TCP中是报文,HTTP是请求。

对于解决HTTP的队头阻塞问题是:并发连接和域名分片。

55.如果面试问,说说你对HTTP代理的理解,你能回答上来不?🧐

代理服务器功能:1,负载均衡,2,保障安全(利用心跳机制监控服务器,一旦发现故障机就将其踢出集群。),3,缓存代理。

理解代理缓存:

  • 由一个代理服务器下载的页面存储;
  • 一个代理服务器为多个用户提供一条通道;
  • 缓冲的代理允许一个代理服务器减少对同一个网站的同样页面的请求次数
  • 一旦代理服务器的一个用户请求了某个页面,代理服务器就保存该页面以服务于它的其他用户的同样的请求
  • 代理缓存,这种处理减少了用户等待页面显示的时间

缓存的作用:

代理服务器或客户端本地磁盘内保存的资源副本,利用缓存可减少对源服务器的访问,可以节省通信流量和通信时间。

示例:

Cache-Control: max-age=300;

表示时间间隔,再次请求的时间间隔300s内,就在缓存中获取,否则就在服务器中

Cache-Control:

  • public 表示响应可被任何中间节点缓存
  • private 表示中间节点不允许缓存
  • no-cache 表示不使用Cache-Control的缓存控制方式做前置验证
  • no-store 表示真正的不缓存任何东西
  • max-age 表示当前资源的有效时间

强缓存:浏览器直接从本地存储中获取数据,不与服务器进行交互

协商缓存:浏览器发送请求到服务器,浏览器判断是否可使用本地缓存

学习了解强缓存👍:

强缓存主要学习expires和cache-control

cache-control该字段:max-age,s-maxage,public,private,no-cache,no-store。

cache-control: public, max-age=3600, s-maxage=3600 
  • 表示资源过了多少秒之后变为无效
  • s-maxage 的优先级高于 max-age
  • 在代理服务器中,只有 s-maxage 起作用

public 和 private

  • public 表示该资源可以被所有客户端和代理服务器缓存
  • private 表示该资源仅能客户端缓存

当浏览器去请求某个文件的时候,服务端就在response header里做了缓存的配置:

表现为:respone header 的cache-control

学习了解✍协商缓存:

response header里面的设置

etag: 'xxxx-xxx
last-modified: xx, 24 Dec xxx xxx:xx:xx GMT

56.如果面试问,HTTP/2,你能回答上来不?🤓

HTTP/2采用哈夫曼编码来压缩整数和字符串,可以达到50%~90%的高压缩率。

服务器推送

57.B/S 结构定义😈

浏览器-服务器结构,B/S结构,客户端不需要安装专门的软件,只需要浏览器即可,浏览器通过web服务器与数据库进行交互,可以方便的在不同平台下工作。

B/S结构简化了客户端的工作,它是随着Internet技术兴起而产生的,对C/S技术的改进,但该结构下服务器端的工作较重,对服务器的性能要求更高。

58.URI统一资源标识符👿

统一资源标识符是一个用于标识某一互联网资源名称的字符串。该标识允许用户对网络中的资源通过特定的协议进行交互操作。URI常见形式为统一资源定位符(URL),URN为统一资源名称。用于在特定的命令空间资源的标识,以补充网址。

59.HTTP 协议👹

HTTP超文本传输协议是互联网上应用最为广泛的一种网络协议。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。通过HTTP或者HTTPS协议请求的资源由统一资源标识符来标识

HTTP 协议主要特点

60.数据链路🔪-数据链路层

数据链路层:以太网,无线LAN,PPP。。。(无线,光纤。。。)
  • 数据链路的知识对了解TCP/IP与网络起到重要的作用
  • 数据链路层的协议定义了通过通信媒介互连的设备传输的规范
  • 物理层面是将实际的通信媒介如电压的高低,电波的强弱等信号与二进制01进行转换
  • 数据链路层处理的数据是一种集合为“帧”的块
  • WLAN,无线局域网
  • PPP,点对点协议,即是1对1连接计算机的协议
  • ATM,异步传输方式
数据链路是让互联网计算机之间相互通信的一种协议,通信手段
  • MAC地址用于识别数据链路中互连的节点

  • 无线通信是使用电磁波,红外线,激光等方式进行传播数据。一般在办公室的局域网范围内组成的较高速的连接称为无线局域网。
  • IP-x-x-x,在IP网络上建立x-x-x,网络服务商提供一种在IP网络商使用MPLS技术构建x-x-x的服务。

61.TCP和UDP的区别

TCP是一个面向连接,可靠,基于字节流的传输层协议。

UDP是一个面向无连接的传输层协议。

TCP是面向连接的,客户端和服务器端的连接,双方互相通信之前,TCP需要三次握手建立连接,而UDP没有建立连接的过程

tcp是面向字节流,udp是面向报文的。UDP的数据传输是基于数据报的,TCP继承了IP层的特性,TCP为了维护状态,将一个个IP包变成了字节流。

TCP报文格式图:

  • 序号:Seq序号,占32位,标识从TCP源端口向目的端口发送的字节流,发起方发送数据时,对此进行标记
  • 确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1
  • 标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等
  1. URG,紧急指有效
  2. ACK,确认序号有效
  3. RST,重置连接
  4. SYN,发起一个新连接
  5. FIN,释放一个连接
  6. PSH,接收方应该尽快将这个报文交给应用层

62.三次握手建立连接

TCP 的三次握手的过程:

有图可知都处于closed状态,服务器开始监听某个端口进入listen状态,客户端发起请求,发送SYN,seq=x,然后状态变为syn-sent状态。

服务器端接收到返回syn和ack,seq=x,ack =x+1,然后状态变成syn-rcvd状态。

客户端收到后,发送ack,seq=x+1,ack=y+1给服务器端,状态变为established,服务器收到后,状态变成established。

在连接过程中,需要对端确认的,需要消耗TCP报文的序列号。SYN消耗一个序列号而ACK不需要。

对于连接四次握手多余,二次握手,会带来资源的浪费,当遇到丢包,重传,连接关闭后,丢包到达服务端,就默认建立连接,可客户端以及关闭,所以三次握手就可以了。

63.四次挥手断开连接

TCP 四次挥手的过程

三次挥手,当服务器将ack和fin合并为一次挥手,会导致长时间的延迟,以至于客户端误认为fin没有到达客户端,让客户端不断重发fin。

64.TCP 滑动窗口

TCP 滑动窗口:

  1. 发送窗口

  1. 接收窗口

65.TCP 的拥塞控制?

TCP连接,拥塞控制:

  1. 拥塞窗口(Congestion Window,cwnd)
  2. 慢启动阈值(Slow Start Threshold,ssthresh)
TCP/IP协议四层
  1. 应用层决定了向用户提供应用服务时通信的活动。
  2. 传输层对上层应用层,提供处于网络连接中两台计算机之间的数据传输。
  3. 网络层用来处理在网络上流动的数据包。
  4. 链路层,用来处理连接网络的硬件部分。
  • HTTP协议的职责,生成对目标web服务器的HTTP请求报文
  • tcp协议的职责,为了方便通信,将HTTP请求报文分割成报文段
  • IP协议的职责,搜索对方的地址,一边中转一边传送
  • TCP协议的职责,从对方那里接收到的报文段,重组到达的报文段,按序号以原来的顺序重组请求报文

66.了解一下DNS

DNS是域名解析系统,它的作用非常简单,就是根据域名查出对应的IP地址。

  • 从根域名服务器查到顶级域名服务器的NS记录和A记录,IP地址
  • 从顶级域名服务器查到次级域名服务器的NS记录和A记录,IP地址
  • 从次级域名服务器查出主机名的IP地址

参考文献

点关注,不迷路

愿你遇到那个心疼你付出的人~

囊括前端Vue、JavaScript、数据结构与算法、实战演练、Node全栈一线技术,紧跟业界发展步伐,一个热爱前端的达达程序员。

好了各位,以上就是这篇文章的全部内容,能看到这里的人都是人才。我后面会不断更新网络技术相关的文章,如果觉得文章对你有用,欢迎给个“赞”,也欢迎分享,感谢大家 !!

喜欢本文的朋友们,欢迎长按下图关注公众号达达前端,收看更多精彩内容

查看原文

hehe 关注了用户 · 7月25日

程序员哆啦A梦 @dashucoding

关注 111

hehe 关注了专栏 · 7月25日

达达前端

达达前端技术社群:囊括前端Vue、JavaScript、数据结构与算法、实战演练、Node全栈一线技术,紧跟业界发展步伐,一个热爱前端的达达程序员。

关注 1048

hehe 赞了文章 · 6月10日

小蝌蚪日记:通过console.log高仿FBI Warning

作者:第一名的小蝌蚪

微信公众号:前端屌丝

blog & github: https://github.com/airuikun/blog

深夜,十二点

昏暗的房间,一卷餐巾纸

注定这是个不平凡的夜

。。。

小蝌蚪打开了珍藏在C盘深处的隐藏文件夹

里面罗列着许多晦涩难懂的文件名

"日理万机.mp4"、"操劳过度.mp4"

“多人运动.mp4”、“2v2敏捷开发.mp4”

别问这些是什么,问就是前端开发教学视频

。。。

小蝌蚪在现实生活中,是一个低端屌丝

受尽女神的冷嘲热讽

但今夜,我的世界我做主

小蝌蚪点开了"采蘑菇的小姑娘.mp4"

笑容逐渐癫狂

。。。

打开后,映入眼帘的是一副FBI Warning大图

众所周知,FBI Warning是东方神秘图腾

它象征着一场大战前,冲锋的号角

小蝌蚪起身

对自己的双手鞠躬:“对不起,今晚又要麻烦你们俩了”

作为一名低级前端工程师

小蝌蚪将用双手

展示如何通过console.log高仿FBI Warning大图

效果如下:

代码如下:

源码:fbi-warning

实现原理很简单

console.log里是可以解析css代码的

格式为:console.log("%c内容1 %c内容2", "css代码1","css代码2");

。。。

实现一个FBI Warning需要4个%c

第一个%c对应的css代码为

    background: #000; 
    font-size: 18px; 
    font-family: monospaces;

这是一个纯黑色背景

黑色象征神秘与性感

似乎为接下来的内容打下了铺垫

第二个%c对应的css代码为

    background: #f33; 
    font-size: 18px; 
    font-family: monospace;
    color: #eee; 
    text-shadow:0 0 1px #fff;

它为“FBI Warning”标题添加了红色背景

红色代表加强

警示每一位看客要遵纪守法

让心怀不轨的人产生羞耻感和罪恶感

第三个%c也是黑色背景

第四个%c对应的css为:

    background: #000; 
    font-size: 18px; 
    font-family: monospace; 
    color: #ddd; 
    text-shadow:0 0 2px #fff;

黑色背景配上白色的字

就好像一个落魄的屌丝

但是内心纯洁无瑕

。。。

以上就是整个高仿FBI Waring的实现了

我敲了6年代码

曾被3位女神抛弃

被2个渣女玩弄过

当过1年小三

再看这幅FBI Waring图 感慨万千

它其实是当代屌丝的一个缩影

描述了一个想爱但不敢爱

只能靠想象来和女神为爱鼓掌的落魄屌丝

卑微且虔诚

。。。

言归正传

在平时开发中console.log搭配一些工具

还能有一些好玩的功能

比如转化'FBI WARNING'成一个字符画

网址:http://patorjk.com/software/t...

。。。

console.log还能画流程图

网址:http://stable.ascii-flow.apps...

。。。

console.log里添加emoji表情包

网址:http://getemoji.com/

。。。

把一张图片转成字符画,如下:

网址:https://www.degraeve.com/img2...

。。。

最后一个技能

把js代码,转成漂亮的代码图片

网址:https://carbon.now.sh

以上就是所有console.log技能了

虽然没什么用

嘻嘻

。。。

。。。

。。。

深夜,凌晨二点

小蝌蚪看了一整晚带马赛克的葫芦娃

随着女蛇妖被爷爷打败

小蝌蚪打翻了牛奶

擦拭干净后

在床边点了根烟

强烈的孤独感袭卷背后

世间一切都变得索然无味

寂静的夜 又归于平静

作者:第一名的小蝌蚪

微信公众号:前端屌丝

blog & github: https://github.com/airuikun/blog

查看原文

赞 16 收藏 8 评论 7

hehe 收藏了文章 · 4月12日

2020年的12个Vue.js开发技巧和窍门

我真的很喜欢使用Vue.js,每次使用框架时,我都会喜欢深入研究其功能和特性。通过这篇文章,我向你介绍了十个很酷的提示和技巧,你可能尚未意识到这些技巧和窍门,以帮助你成为更好的Vue开发人员。

更漂亮的插槽语法

随着Vue 2.6的推出,已经引入了插槽的简写方式,可用于事件(例如,@click 表示 v-on:click 事件)或冒号表示方式用于绑定(:src)。例如,如果你有一个表格组件,你可以使用这个功能如下:

<template>
  ...
    <my-table>
    <template #row={ item }>
            /* 一些内容,你可以在这里自由使用“item” */
        </template>
  </my-table>  
    ...
</template>

$on(‘hook:’) 可以帮助你简化代码

删除事件监听器是一种常见的最佳实践,因为它有助于避免内存泄露并防止事件冲突。

如果你想在 createdmounted 的钩子中定义自定义事件监听器或第三方插件,并且需要在 beforeDestroy 钩子中删除它以避免引起任何内存泄漏,那么这是一个很好的特性。下面是一个典型的设置:

mounted () {
    window.addEventListener('resize', this.resizeHandler);
},
beforeDestroy () {
    window.removeEventListener('resize', this.resizeHandler);
}

使用 $on('hook:')方法,你可以仅使用一种生命周期方法(而不是两种)来定义/删除事件。

mounted () {
  window.addEventListener('resize', this.resizeHandler);
  this.$on("hook:beforeDestroy", () => {
    window.removeEventListener('resize', this.resizeHandler);
  })
}

$on 还可以侦听子组件的生命周期钩子

最后一点,生命周期钩子发出自定义事件这一事实意味着父组件可以监听其子级的生命周期钩子。

它将使用正常模式来监听事件(@event),并且可以像其他自定义事件一样进行处理。

<child-comp @hook:mounted="someFunction" />

使用 immediate: true 在初始化时触发watcher

Vue Watchers 是添加高级功能(例如,API调用)的好方法,该功能可以在观察值发生变化时运行。

默认情况下,观察者不会在初始化时运行。根据你的功能,这可能意味着某些数据不会完全初始化。

watch: {
    title: (newTitle, oldTitle) => {
      console.log("Title changed from " + oldTitle + " to " + newTitle)
    }
}

如果你需要 wather 在实例初始化后立即运行,那么你所要做的就是将 wather 转换为具有 handler(newVal, oldVal) 函数以及即时 immediate: true 的对象。

watch: {
    title: {
      immediate: true,
      handler(newTitle, oldTitle) {
        console.log("Title changed from " + oldTitle + " to " + newTitle)
      }
    }
}

你应该始终验证你的Prop

验证 Props 是 Vue 中的基本做法之一。

你可能已经知道可以将props验证为原始类型,例如字符串,数字甚至对象。你也可以使用自定义验证器——例如,如果你想验证一个字符串列表:

props: {
  status: {
    type: String,
    required: true,
    validator: function (value) {
      return [
        'syncing',
        'synced',
        'version-conflict',
        'error'
      ].indexOf(value) !== -1
    }
  }
}

动态指令参数

Vue 2.6的最酷功能之一是可以将指令参数动态传递给组件。假设你有一个按钮组件,并且在某些情况下想监听单击事件,而在其他情况下想监听双击事件。这就是这些指令派上用场的地方:

<template>
    ...
    <aButton @[someEvent]="handleSomeEvent()" />...
</template>
<script>
  ...
  data(){
    return{
      ...
      someEvent: someCondition ? "click" : "dbclick"
    }
  },
  methods: {
    handleSomeEvent(){
      // handle some event
    }
  }  
</script>

而且,这实际上也很整洁-你可以将相同的模式应用于动态HTML属性,props等。

重用相同路由的组件

开发人员经常遇到的情况是,多个路由解析为同一个Vue组件。问题是,Vue出于性能原因,默认情况下共享组件将不会重新渲染,如果你尝试在使用相同组件的路由之间进行切换,则不会发生任何变化。

const routes = [
  {
    path: "/a",
    component: MyComponent
  },
  {
    path: "/b",
    component: MyComponent
  },
];

如果你仍然希望重新渲染这些组件,则可以通过在 router-view 组件中提供 :key 属性来实现。

<template>
    <router-view :key="$route.path"></router-view>
</template>

把所有Props传到子组件很容易

这是一个非常酷的功能,可让你将所有 props 从父组件传递到子组件。如果你有另一个组件的包装组件,这将特别方便。所以,与其把所有的 props 一个一个传下去,你可以利用这个,把所有的 props 一次传下去:

<template>
  <childComponent v-bind="$props" />
</template>

代替:

<template>
  <childComponent :prop1="prop1" :prop2="prop2" :prop="prop3" :prop4="prop4" ... />
</template>

把所有事件监听传到子组件很容易

如果子组件不在父组件的根目录下,则可以将所有事件侦听器从父组件传递到子组件,如下所示:

<template>
    <div>
    ...
        <childComponentv-on="$listeners" />...    
  <div>
</template>

如果子组件位于其父组件的根目录,则默认情况下它将获得这些组件,因此不需要使用这个小技巧。

$createElement

默认情况下,每个Vue实例都可以访问 $createElement 方法来创建和返回虚拟节点。例如,可以利用它在可以通过v-html指令传递的方法中使用标记。在函数组件中,可以将此方法作为渲染函数中的第一个参数进行访问。

使用JSX

由于Vue CLI 3默认支持使用JSX,因此现在(如果愿意)你可以使用JSX编写代码(例如,可以方便地编写函数组件)。如果尚未使用Vue CLI 3,则可以使用 babel-plugin-transform-vue-jsx 获得JSX支持。

自定义 v-model

默认情况下,v-model@input 事件侦听器和 :value 属性上的语法糖。但是,你可以在你的Vue组件中指定一个模型属性来定义使用什么事件和value属性——非常棒!

export default: {
  model: {
    event: 'change',
    prop: 'checked'  
  }
}

总结

这绝不是VueJS技巧的完整列表,这些只是我个人认为最有用的一些,其中一些技巧使我花了很长时间才在Vue中进行实践,因此我认为我可以与大家分享这些知识。

希望他们像我一样对你有帮助!

你最喜欢的VueJS技巧和窍门是什么?我也很想向你学习!


参考链接:


现在关注 《前端全栈开发者》 微信公众号,还送某网精品视频课程网盘资料啊,准能为你节省不少钱!
subscribe2.png

查看原文

认证与成就

  • 获得 3 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-05-23
个人主页被 75 人浏览