Skip to content

一、前端知识深度-全栈

1. 移动端 H5 click 有 300ms 延迟,如何解决

链接参考

  • 背景:double tap to zoom
    • 初期解决方案 FastClick

      • 使用
        javascript
        // FastClick 使用非常简单
        window.addEventListener("load", function () {
          FastClick.attach(document.body)
        }, false)
      • 原理
        • 监听 touchend 事件(touchstart touchend 会优先 click 触发)
        • 使用 自定义 DOM 事件 模拟一个 click 事件
        • 把默认的 click 事件(300ms之后触发) 禁止掉
    • 现代浏览器的改进 300ms.png

答案:

  • cookie:HTTP 标准;跨域限制;配合 session 使用

  • token:无标准;无跨域限制;用于 JWT

  • 重点:

    • cookie 的知识点很多,对于 HTTP 也很重要
    • session 存在的价值
    • token 和 cookie 要对比理解,否则容易混淆
  • 连环问:

    • session 和 JWT 哪个更好?
      • 答案:

        • 如有严格管理用户信息的要求(保密、快速封禁)推荐 session
        • 如没有特殊要求,则使用 JWT(如创业初期的网站)
      • session 优点

        • 原理简单,易于学习
        • 用户信息存储在服务端,可快速封禁某个用户
      • session 缺点

        • 占用服务端内存,硬件成本高
        • 多进程,多服务器时,不好同步,需要使用第三方缓存,如 redis
        • 默认有跨域限制
      • JWT 优点

        • 不占用服务端内存
        • 多进程、多服务器 不受影响
        • 没有跨域限制
      • JWT 缺点

        • 用户信息存储在客户端,无法快速封禁某用户
        • 万一服务器秘钥被泄漏,则用户信息全部丢失
        • token 体积一般大于 cookie,会增加请求的数据量
  • cookie

    • http 无状态,每次请求都要带 cookie,以帮助识别身份
    • 服务端也可以向客户端 set-cookie,cookie 大小限制 4kb
    • 默认有跨域限制:不可跨域共享、传递 cookie cookie.png
      • 解决方案:withCredentials
  • cookie 本地存储

    • HTML5 之前 cookie 常被用于本地存储
    • HTML5 之后推荐使用 localStorage 和 sessionStorage
  • 现在浏览器开始禁止第三方 cookie

    • 和跨域限制不同。这里是:禁止网页引入的第三方 JS 设置 cookie
    • 打击第三方广告,保护用户隐私
    • 新增属性 SameSite: Strict/Lax/None; 值可自己选择 forbid-cookie.png
  • cookie 和 session

    • cookie 用于登录验证,存储用户标识(如 userId)。
    • session 在服务端,存储用户详细信息,和 cookie 信息一一对应。
    • cookie + session 是常见登录验证解决方案 cookie-session.png
  • token vs cookie

    • cookie 是 HTTP 规范,而 token 是自定义传递。
    • cookie 会默认被浏览器存储,而 token 需要自己存储。
    • token 默认没有跨域限制
  • JWT(JSON Web Token)

    • 前端发起登录,后端验证成功之后,返回一个加密的 token
    • 前端自行存储这个 token (其中包含了用户信息,加密了)
    • 以后访问服务端接口,都带着这个 token ,作为用户信息。
  • 连环问:如何实现 SSO 单点登录 答案:

    • 主域名相同,则可共享 cookie

    • 主域名不同,则需要使用 SSO

    • 基于 cookie

      • cookie 默认不可跨域共享,但有些情况下可设置为共享
      • 主域名相同,如www.baidu.com image.baidu.com
      • 设置 cookie domain 为主域名,即可共享 cookie sso-cookie.png
    • SSO

      • 主域名完全不同,则 cookie 无法共享
      • 可使用 SSO 技术方案
      • OAuth 2.0 第三方 授权登录 OAuth2.0png

3. HTTP 协议和 UDP 协议有什么区别?

答案:

  • HTTP 是应用层,TCP UDP是传输层

  • TCP 有连接,有断开,稳定传输

  • UDP 无连接,无断开,不稳定传输,但效率高

  • 网络协议

    • HTTP 协议在应用层
    • TCP UDP 协议在传输层
    • 严格来说,应该拿 TCP 和 UDP 进行比较 OSI.0png
  • TCP 协议

    • 有连接(三次握手)
    • 有断开(四次挥手)
    • 稳定传输
  • UDP 协议

    • 无连接,无断开
    • 不稳定传输,但效率高
    • 如视频会议、语音通话
  • HTTP 协议 1.0 1.1 2.0 有什么区别?

    • HTTP 1.0
      • 最基础的 HTTP 协议
      • 支持基本的 GET POST 方法
    • HTTP 1.1
      • 缓存策略 cache-control E-tag 等
      • 支持长连接 Connection: keep-alive, 一次 TCP 连接多次请求
      • 断点续传,状态码 206
      • 支持新的方法 PUT DELETE 等,可用于 Restful API
    • HTTP 2.0
      • 可压缩 header, 减少体积
      • 多路复用,一次 TCP 连接中可以多个 HTTP 并行请求
      • 服务端推送

4. 什么是 HTTPS 中间人攻击? 如何预防?

  • HTTP 明文传输
  • HTTPS 加密传输 HTTP + TLS/SSL 参考链接
    • 对称加密
      • 秘钥相同
    • 非对称加密
      • 公钥加密
      • 私钥解密
    • HTTPS 加密过程 https.png
    • 中间人攻击 MITM.png

5. <script> defer 和 async 有什么区别?

答案:

  • 没有:HTML 暂停解析,下载 JS ,执行 JS , 再继续解析 HTML

  • defer: HTML 继续解析,并行下载 JS , HTML 解析完再执行 JS

  • async: HTML 继续解析,并行下载 JS , 执行 JS ,再解析 HTML defer.png

  • 连环问:prefetch 和 dns-prefetch 有什么区别? 答案:

  • prefetch 是资源预获取(和 preload 相关)

  • dns-prefetch 是 DNS 预查询(和 preconnect 相关)

  • 都是使用标签 <link rel="prefetch">

  • preload 和 prefetch

    • preload 资源在当前页面使用,会优先加载
    • prefetch 资源在未来页面使用,空闲时加载
  • dns-prefetch 和 preconnect

    • dns-prefetch 即 DNS 预查询
    • preconnect 即 DNS 预连接

6. 你知道哪些前端攻击? 该如何预防?

  • XSS
    • Cross Site Script 跨站脚本攻击
    • 手段:黑客将 js 代码插入到网页内容中,渲染时执行 JS 代码
    • 预防:特殊字符替换(前端或者后端)
    • Vue React 默认规避 XSS 攻击
      • 除了 Vue v-html React dangerouslySetInnerHTML

-CSRF - Cross Site Request Forgery 跨站请求伪造 - 手段:黑客诱导用户访问另一个网站的接口,伪造请求。 - 详细过程 - 用户登录了 A 网站,有了 cookie - 黑客诱导用户到 B 网站,并发起 A 网站的请求 - A 网站的 API 发现有 cookie,认为是用户自己操作的 - 预防手段 - 严格的跨域请求限制,如判断 referrer (请求资源) - 为 cookie 设置 SameSite,禁止跨域传递 cookie - 关键接口中使用短信验证码

  • 点击劫持

    • Click Jacking
    • 手段:诱导界面上蒙一个透明的 iframe, 诱导用户点击。
    • 预防:让 iframe 不能跨域加载 jacking.png
  • DDos

    • Distribute denial-of-service 分布式拒绝服务
    • 手段:分布式的、大规模的流量访问,使服务器瘫痪
    • 预防:软件层不好做,需要硬件预防(如阿里云 WAF)
  • SQL注入

    • 手段:黑客提交内容时,写入SQL语句,破坏数据库。
    • 预防:处理输入的内容,替换特殊字符。

7. WebSocket 和 HTTP 有什么区别?

  • WebSocket
    • 支持端对端通讯
    • 可以有 client 发起,也可以由 server 发起
    • 用于:消息通知,直播间讨论区,聊天室,协同编辑
  • WebSocket 的连接过程
    • 先发起一个 HTTP 请求
    • 成功之后再升级到 WebSocket 协议,再通讯 websocket.png
  • WebSocket 和 HTTP 区别
    • WebSocket 协议名是 ws://,可双端发起请求
    • WebSocket 没有跨域限制
    • 通过 send 和 onmessage 通讯(HTTP 通过 req 和 res)
  • ws 可升级为 wss
    javascript
    import { createServer } from 'https';
    import { readFileSync } from "fs";
    import { WebSocketServer } from "ws";
    
    const server = createServer({
      cert: readFileSync('/path/to/cret.pem'),
      key: readFileSync('/path/to/key.pem')
    })
    
    const wss = new WebSocketServer({ server })
  • 扩展:实际项目推荐 socket.io API简介
  • 也可以使用聊天室 这种场景
  • 连环问:WebSocket 和 HTTP 长轮询的区别?
    • HTTP 长轮询:客户端发起请求,服务端阻塞,不会立即返回。
    • WebSocket:客户端可发起请求,服务端也可发起请求。 websocket-http.png
    • 注意:
      • HTTP 长轮询,需要处理 timeout,即 timeout 之后重新发请求。

8. 描述从输入 url 到页面展示的完整过程。

答案:

  • 网络请求:DNS 解析,HTTP 请求

  • 解析:DOM 树,CSSDOM 树,Render Tree

  • 渲染:计算、绘制,同时执行 JS

  • 网络请求

    • DNS 查询(得到IP),建立 TCP 连接(三次握手)
    • 浏览器发起 HTTP 请求
    • 收到请求响应,得到 HTML 源代码
      • 继续网络请求
        • 解析 HTML 过程中,遇到 静态资源还会继续发起网络请求
        • JS CSS 图片 视频等
        • 注意:静态资源可能有强缓存,此时不必请求
  • 解析:字符串 -> 结构化数据

    • HTML 构建 DOM 树
    • CSS 构建 CSSDOM 树(style tree)
    • 两者结合,形成 render tree dom-render.png
      • 解析过程很复杂
        • CSS 可能来自 <style><link>
        • JS 可能内嵌、或外链,还有 defer async 逻辑
        • img 可能内嵌(base64),可能外链
      • 优化解析
        • CSS 放在<head> 中,不要异步加载 CSS
        • JS 放在 <body> 最下面(或合理使用 defer async)
        • <img> 提前定义 width height
  • 渲染:Render Tree 绘制到页面

    • 计算各个 DOM 的尺寸、定位、最后绘制到页面
    • 遇到 JS 可能会执行(参考 defer async)
    • 异步 CSS、图片加载,可能会触发重新渲染。
  • 连环问:重绘 repaint 重排 reflow 有什么区别

    • 动态网页,随时都会重绘、重排
      • 网页动画
      • Modal Dialog 弹窗
      • 增加、删除一个元素,显示、隐藏一个元素
    • 重绘 repaint
      • 元素外观改变,如颜色、背景色
      • 但元素的尺寸、定位不变,不会影响其他元素的位置。
    • 重排 reflow
      • 重新计算尺寸和布局,可能会影响其他元素的位置。
      • 如元素高度增加,可能会使相邻元素位置下移。
    • 区别:
      • 重排比重绘要影响更大,消耗也更大
      • 所以,要尽量避免无意义的重排
    • 减少重排的方法
      • 集中修改样式,或直接切换 css class
      • 修改之前设置 display:none,脱离文档流
      • 使用 BFC 特性,不影响其他元素位置
      • 频繁触发(resize scroll)使用节流和防抖
      • 使用 createDocumentFragment 批量操作 DOM
      • 优化动画,使用 CSS3 和 requestAnimationFrame
    • 扩展:BFC
      • Block Format Context 块级格式化上下文
      • 内部的元素无论如何改动,都不会影响其他元素的位置
    • 触发 BFC 的条件
      • 根节点 <html>
      • float:left/right
      • overflow:auto/scroll/hidden
      • display:inline-block/table/table-row/table-cell
      • display: flex/grid 的直接子元素
      • position:absolute/fixed

9. 如何实现网页多标签通讯?

答案:

  • WebSocket 需要服务端,成本较高

  • localStorage 简单易用,推荐

  • SharedWorker 调试不方便,不兼容 IE11

  • 使用 WebSocket

    • 无跨域限制
    • 需要服务端支持,成本高
  • 通过 localStorage 通讯

    • 同域的 A 和 B 两个页面

    • A 页面设置 localStorage

    • B 页面可监听 localStorage 值的修改

      html
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>msg-localStorage-detail</title>
      </head>
      <body>
          <p>localStorage message - detail page</p>
          <button id="btn"> 修改标题 </button>
      
          <script>
              const btn = document.getElementById('btn');
              btn.addEventListener('click', () => {
                const info = {
                  id: 100,
                  name: '标题' + new Date()
                };
                localStorage.setItem('changeInfo', JSON.stringify(info))
              })
      
          </script>
      </body>
      </html>
      html
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>msg-localStorage-list</title>
      </head>
      <body>
          <p>localStorage message - list page</p>
          <button id="btn"> 修改标题 </button>
      
          <script>
              window.addEventListener('storage', event => {
                console.log('key', event.key);
                console.log('value', event.newValue)
              })
          </script>
      </body>
      </html>
  • 通过 SharedWorker 通讯 [代码调试需要开启浏览器隐私模式]

    • SharedWorker 是 WebWorker 的一种

    • WebWorker 可开启子进程执行 JS,但不能操作 DOM

    • SharedWorker 可单独开启一个进程,用于同域页面通讯

      html
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>msg-sharedworker-detail</title>
      </head>
      <body>
          <p>msg-sharedworker-detail page</p>
          <button id="btn">修改标题</button>
          <script>
              const worker = new SharedWorker('./worker.js')
      
              const btn = document.getElementById('btn');
              btn.addEventListener('click', () => {
                console.log('clicked');
                worker.port.postMessage('detail go...')
              })
          </script>
      </body>
      </html>
      html
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>msg-sharedworker-list</title>
      </head>
      <body>
          <p>msg-sharedworker-list page</p>
          <button id="btn">修改标题</button>
          <script>
              const worker = new SharedWorker('./worker.js')
      
              worker.port.onmessage = e => console.info('list', e.data)
          </script>
      </body>
      </html>
      js
      /**
       * @description for sharedWorker
       */
      
      const set = new Set()
      
      onconnect = event => {
        const port = event.ports[0];
        set.add(port);
      
        // 接受信息
        port.onmessage = e => {
          // 广播消息
          set.forEach(p => {
            if (p === port) return
            p.postMessage(e.data)
          })
        }
        // 发送信息
        port.postMessage('worker.js done')
      }
  • 连环问:网页和 iframe 如何通讯? 答案:

  • 使用 postMessage 通讯

  • 注意跨域的限制和判断

    html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>iframe-index</title>
    </head>
    <body>
        <p>iframe-index</p>
        <button id="btn">发送消息</button>
    
        <iframe id="iframe" src="./iframe-child.html"></iframe>
        <script>
            const btn = document.getElementById('btn');
            btn.addEventListener('click', () => {
              console.log('index clicked');
              window.iframe.contentWindow.postMessage('hello', '*')
            })
    
            window.addEventListener('message', event => {
              console.log('origin', event.origin);
              console.log('index received', event.data)
            })
        </script>
    </body>
    </html>
    html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>iframe-child</title>
    </head>
    <body>
        <p>iframe-child</p>
        <button id="btn">发送消息</button>
    
        <script>
            const btn = document.getElementById('btn');
            btn.addEventListener('click', () => {
              console.log('child clicked');
              window.parent.postMessage('world', '*')
            })
            window.addEventListener('message', event => {
              console.log('origin', event.origin);
              console.log('child received', event.data)
            })
        </script>
    </body>
    </html>

10. 请描述 koa2 洋葱圈模型

  • koa2

    • 一个简约、流行的 node.js 框架

    • 通过中间件阻止代码

    • 多个中间件以 "洋葱圈模型" 执行

      js
      const Koa = require('koa');
      const app = new Koa();
      
      // logger
      app.use(async (ctx, next) => {
        await next(); // 先执行下一步 x-response-time,执行完再继续执行
        const rt = ctx.response.get('X-Response-Time');
        console.log(`${ctx.method} ${ctx.url} - ${rt}`);
      });
      
      // x-response-time
      app.use(async (ctx, next) => {
        const start = Date.now();
        await next(); // 先执行下一步 response,执行完再继续执行
        const ms = Date.now() - start;
        ctx.set('X-Response-Time', `${ms}ms`);
      });
      
      // response
      app.use(async ctx => {
        ctx.body = 'Hello World';
      });
      
      app.listen(3000);

      koa2.png

  • 重点:JS 异步编程

Released under the MIT License.