万字前端效率大提速系列 ? :六、前端需要了解的后端知识、计算机网络知识及衍生面试题

我们的头衔是 Web 前端开发工程师,也就是 Web 开发的子集。其实不论前端后端,最终都会掌握前后端的开发技能,只是熟练度、深度、细节上有所侧重。掌握后端开发知识、计算机网络知识对工作中的问题排查、协作交流帮助非常大。下面从一个超常见的面试题开始了解:

面试题:从输入 URL 到展示页面经过了哪些步骤?

简单版例子:

  1. 用户输入了 www.baidu.com
  2. 浏览器要向百度发送一个网页请求,要先知道百度的 IP 地址,于是向 DNS 服务器询问,DNS 服务器返回了 IP:180.101.49.11:443
  3. 请求数据包被 HTTP、SSL、TCP、IP、以太网协议包装后准备传输。
  4. 因为双方不属于同一个子网络,传输需要通过网卡转发,从本机的网卡 MAC 地址发送到百度服务器的网关 MAC 地址(通过 ARP 协议得到)
  5. 服务器与客户端建立 TCP 连接,收到请求,返回一个 HTTP 响应,内容是 HTML 页面
  6. 浏览器收到响应,从上到下解析 HTML 页面,遇到不同的标签做不同处理。例如 script 标签会执行其中的 JS 代码,style 标签会被加入样式表等等,其中又会根据标签发出数个请求,请求对应的资源文件。
  7. 浏览器解析处理完成后,展示页面。

可以看到整个过程有两个大环节:一.网络安全准确完整的传输数据;二.浏览器解析处理展示各类资源。一就像菜鸟驿站,不论你的快递是啥,用协议包一层贴上地址联系方式,准确的把快递投放到目的地。二则是将代码运行成应用让用户使用。

想更多了解一的过程,可以参考阮一峰 – 互联网协议入门(一)阮一峰 – 互联网协议入门(二)网络是怎样连接的

想更多了解二的过程,可以参考你不知道的浏览器页面渲染机制浏览器环境

上面步骤中衍生的面试题:

  • 步骤 2 中,DNS 做了些什么:

    • DNS(Domain Name System)做的事情非常简单,就是根据域名查出IP地址。你可以把它想象成一本巨大的电话本。
    • 在没有缓存的情况下,DNS 查询顺序是 根域名 -> 顶级域名 -> 次级域名 -> 三级域名,例如 www.baidu.com,查询顺序是 .root -> .com -> .baidu -> .www。其中根域名平时是省略的。
    • 更多知识可以参考:DNS 原理入门
  • CDN 是什么,它做了什么事情

    • CDN(Content Delivery Network)全称内容分发网络,最大的好处是让用户就近获取资源,提高访问效率。
    • CDN 会接管步骤 2 中的 DNS 查询,按就近原则和负载均衡,向用户返回当前效率最高的节点(通常是最近的),从而提高传输效率。
  • 步骤 3 中 HTTP 是什么,它的 1.0 1.1 2.0 版本有什么区别

    • HTTP 协议是一种无状态的协议,浏览器中 JS 调用 XHR 时会向服务器发送 HTTP 请求。协议就是为了前后端可以相互理解、沟通。HTTP 报文由 header、body 组成,请求报文和响应报文内容上有区别
    • HTTP 1.1 相比 HTTP 1.0 主要有以下优化:1.更多控制缓存策略的请求头;2.支持断点续传;3.更多状态码;4.传递了 hostname 字段;5.支持长链接
    • HTTP 2.0 相比 HTTP 1.1 主要有以下优化:1.以二进制格式(Binary Format)解析报文;2.多路复用,一个连接上的多个请求相互独立不阻塞;3.头部压缩,避免重复传输 header;4.支持服务端推送,而不是只能客户端发起请求。
    • 更多可以参考HTTP1.0、HTTP1.1 和 HTTP2.0 的区别
  • 步骤 3 中 HTTPS 具体做了什么

    • HTTPS 其实是 HTTP + SSL/TLS,由 SSL 将 HTTP 报文内容处理包装然后传输。将传输内容加密,防止被劫持。
    • SSL/TLS 通过非对称加密传输对称加密秘钥,再通过对称加密传输内容。非对称加密需要信任证书获取公钥。
  • 步骤 5 中 TCP 建立连接的过程是什么

    • 三次握手四次挥手,三次握手是为了确保双方能正常应答,四次挥手是为了确保双方正常断开连接。
    • 有一种 DDoS 攻击就是利用三次握手,大量客户端发起第一次握手后再不应答,服务端为了维护连接消耗了巨大的资源,导致其他正常用户无法成功连接。
    • 更多可以参考图解 TCP/IP(第 5 版)
  • 步骤 6 中 JS 的执行顺序是怎样的,script 标签的 defer async属性的作用是什么

    • 浏览器解析 HTML 时,遇到没有特殊属性的 script 标签,会暂停解析,立即执行 JS 代码。如果 有 src 属性则会等下载后执行完,再继续往下解析 HTML。此时如果下载或者执行时耗时太长,会影响到之后的页面渲染。为了解决这个问题,可以带上 script 标签的defer async属性
    • 加上 async 属性,则下载 JS 代码的时候不会阻塞解析,但是等下载完后,如果还没有解析完成,会暂停解析执行代码。
    • 加上 defer 属性,则下载 JS 代码的时候不会阻塞解析,下载完后,如果还没有解析完成,也不会暂停解析 HTML,等到 HTML 处理完后再执行。多个带 deferscript 标签会按顺序执行。
    • 也就是说 async 属性,可能阻塞解析也可能不阻塞,defer 属性一定不阻塞。

后端在 Web 应用中通常负责什么?

在 Web 应用中,后端负责根据 Request,返回 Response。早年的 Web 应用开发没有前后端分离的概念,通过 JSP、ThinkPHP 等模板语言返回数据渲染好的页面,或者通过接口返回数据。由此可见前端应用部署后也是一个 Web 服务,不论静态构建还是服务端渲染,同样是根据 Request,返回 Response,返回的是各类资源。

以常见前后端分离的应用举例,用户访问网页时,前端返回 HTML、CSS、JS、IMG 等资源;浏览器解析后执行 JS 中的 XHR 请求,后端接受到请求,根据请求中的鉴权信息、参数等,将对应的数据返回给前端,前端通过请求回调的数据来渲染页面。

接口文档及前端请求

当前主流的后端应用都是提供接口,举例极简的用户登录获取用户信息两个接口文档:

// 接口文档 - 用户登录
url: http://www.example.com/api/user/login
method: POST
params:
  - name: username
    type: string
    required: true
  - name: password
    type: string
    required: true
response:
  - code: 200
    msg: success
    data:
      - user-token: xxxxxyyyy
复制代码

前端通过 axios 发起请求:

axios
  .post("http://www.example.com/api/user/login", {
    username: "admin",
    password: "123456",
  })
  .then((res) => {
    console.log(res.data);
    localStorage.setItem("user-token", res.data.userToken);
    // { code: 200, msg: success, data: [{ user-token: xxxxxyyyy }] }
  });
复制代码

这一步用户通过登录获取到了登录凭证 user-token ,我们将他保存在 localStorage 中。再设置 axios 的拦截器,每次请求自动带上登录凭证,以供服务端鉴权,axios 拦截器示例如下:

axios.interceptors.request.use((config) => {
  config.headers["user-token"] = localStorage.getItem("user-token");
  return config;
});
复制代码

此时我们发起其他请求,都会在 header 中带上 user-token 字段,使服务端能够对当前用户进行验证鉴权。此时我们再请求用户信息接口:

// 接口文档 - 获取用户信息
url: http://www.example.com/api/user/info
method: GET
response:
  - code: 200
    msg: success
    data:
      - username: admin
        avatar: http://www.example.com/avatar.png
复制代码
axios.get("http://www.example.com/api/user/info").then((res) => {
  console.log(res.data);
  // { code: 200, msg: success, data: [{ username: admin, avatar: http://www.example.com/avatar.png }] }
});
复制代码

通过user-token,接口就能判断出用户身份,每个用户返回不同的信息。我们看下怎样编写出这样的接口:

编写接口的过程

后端同学开发业务的时候喜欢笑称自己是 CRUD(增删改查) 工具人,说明大部分业务开发都得基于数据的增删改查完成。要实现一个登录(查)的接口,首先要实现注册(增)接口。同样还会有修改密码(改)、用户注销(删)的接口。

在编写接口前,应该对接口的功能有清晰的认识,能初步定下接口的接收参数和返回值,写出接口文档,以注册接口为例:

// 接口文档 - 注册
url: http://www.example.com/api/user/register
method: POST
params:
  - name: username
    type: string
    required: true
  - name: password
    type: string
    required: true
response:
    - code: 200
      msg: success
      data:
        user-token: xxxxxyyyy
复制代码

有了接口文档,可以看出注册接口接收用户名和密码,返回注册成功。为了储存用户数据,后端服务需要连接数据库并创建数据表,这里我们使用 NodeJS 环境、Koa 框架、MySQL 数据库、用 Sequelize 来操作数据库。首先创建 user 表:

// 注意 业务实践中应该将密码脱敏处理
CREATE TABLE user (
  id INT NOT NULL AUTO_INCREMENT,
  username VARCHAR(255) NOT NULL,
  password VARCHAR(255) NOT NULL,
  PRIMARY KEY (id)
);
复制代码

然后连接数据库:

const Sequelize = require("sequelize");
const sequelize = new Sequelize("database", "username", "password", {
  host: "localhost",
  dialect: "mysql",
  pool: {
    max: 5,
    min: 0,
    idle: 10000,
  },
});
复制代码

编写注册接口

// 定义 User 模型
const User = sequelize.define("user", {
  username: Sequelize.STRING,
  password: Sequelize.STRING,
});

User.sync({ force: true }).then(() => {
  // User表中创建一条数据
  User.create({
    username: "admin",
    password: "123456",
  });
  ctx.body = {
    code: 200,
    message: "注册成功",
  };
});
复制代码

想了解更多后端实战的同学,可以参考 基于 Node.js Koa2 实战开发的一套完整的博客项目网站

寥寥几笔,只能简单描述业务接口开发过程,实际上后端的知识网包括且不限于:操作系统、运维、网络、数据库等等等等(参考后端架构师技术图谱)。感兴趣的同学也只是建议在有限的精力下,努力做到其中的一精多通。

上一篇:五、前端应具备的NodeJS知识及常用工具

—小尾巴— 我叫葛圣明,擅长前端,有想法有问题欢迎私聊讨论 ^_^

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享