分析 crisp-client l.js 过程

2023-08-02

脚本: https://client.crisp.chat/l.js

初始化的调用链条:

  1. new function 进入到构造函数。用 new 关键字的时候,会把 function 的 this 指向它本身。
  2. 构造函数里面进行了环境变量定义。
  3. 如果已经 readyState 在 "interactive" 状态和 readyState 在 "complete" 则直接初始化。
  4. 在 window 上监听 DOMContentLoaded 事件,进行初始化。
  5. 在 document.onreadystatechange 的时候 判断 readyState 不在 "interactive" 状态和 readyState 不在 "complete" 状态则初始化。

init 做了什么事情

  • 环境变量的定义

  • 一些特性检查

    • 是否是真实客户端

      • innerWidth 窗口太小,不再加载
      • 如果是域名黑名单里的,不再加载
      • 不支持 WebSocket,不再加载
    • 是否支持存储能力

      • navigator.cookieEnabled
      • window.localStorage
        • 检查 setItem 是否存在
        • 检查 getItem 是否存在
        • 检查 removeItem 是否存在
    • 获取浏览器语言 navigator.languages

    • 检查是否是现代浏览器

  • 远程加载脚本

搜索机器人
[
    "Trident",
    "Googlebot",
    "Bingbot",
    "Slurp",
    "DuckDuckBot",
    "Baiduspider",
    "YandexBot",
    "GTmetrix",
    "Lighthouse",
    "Acunetix",
    "Ahrefs",
    "SemrushBot",
    "SiteAuditBot",
    "SplitSignalBot"
]
判断 audio 是否支持

window.MediaRecorder && window.File && navigator.mediaDevices && "function" == typeof window.MediaRecorder.isTypeSupported && "function" == typeof navigator.mediaDevices.enumerateDevices && navigator.mediaDevices.enumerateDevices()

ws 的处理
{
    key: "addEventListeners",
    value: function() {
        var i = this;
        this.ws.onopen = function() {
            i.opts.autoUnref && i.ws.qs.unref(),
            i.onOpen()
        }
        ,
        this.ws.onclose = function(t) {
            return i.onClose({
                description: "websocket connection closed",
                context: t
            })
        }
        ,
        this.ws.onmessage = function(t) {
            return i.onData(t.data)
        }
        ,
        this.ws.onerror = function(t) {
            return i.onError("websocket error", t)
        }
    }
},


{
    key: "ondata",
    value: function(t) {
        this.decoder.add(t)
    }
},

this.ws.onmessage 都绑定到 this onData 事件里执行 this.decoder.add(t) 有专门的解码器 Decoder 解开数据

遇到错误不阻塞执行

用 try catch 包裹。 如果一定需要返回,可以在 finally 作用领域返回。

远程加载
css
            try {
                var t = this.m_()
                  , i = document.createElement("link");
                i.href = [this._e + "/", this.w_ + "/", "client_" + t + ".css?" + this._f].join(""),
                i.type = "text/css",
                i.rel = "stylesheet",
                this.__(i),
                document.getElementsByTagName("head")[0].appendChild(i)
            } catch (t) {}
script

            try {
                var t = document.createElement("script");
                t.src = [this._e + "/", this.y_ + "/", "client.js?" + this._f].join(""),
                t.type = "text/javascript",
                t.async = 1,
                this.__(t),
                document.getElementsByTagName("head")[0].appendChild(t)
            } catch (t) {}

dns-prefetch
            try {
                var n = document.createElement("link");
                n.setAttribute("href", i),
                n.setAttribute("rel", t),
                n.setAttribute("crossorigin", ""),
                this.__(n),
                document.getElementsByTagName("head")[0].appendChild(n)
            } catch (t) {}

dns-prefetch 只执行 DNS 查询,而 preconnect 则是建立与服务器的连接 减少跨源请求的感知延迟

注意点

"function" == typeof Symbol

判断是否有 Symbol 语法

"symbol" == typeof Symbol.iterator

判断是否有 Symbol 语法

window.getComputedStyle(i).getPropertyValue("display")

获取 window 的样式

MIME 嗅探
X-Content-Type-Options: nosniff

用于确定服务端 MIME 类型不可变更且可信

在缺失 MIME 类型或客户端认为文件设置了错误的 MIME 类型时,浏览器可能会通过查看资源来进行 MIME 嗅探。每一个浏览器在不同的情况下会执行不同的操作。因为这个操作会有一些安全问题,有的 MIME 类型表示可执行内容而有些是不可执行内容。浏览器可以通过请求头 Content-Type 来设置 X-Content-Type-Options 以阻止 MIME 嗅探。

copyright ©2019-2024 shenzhen
粤ICP备20041170号-1