脚本: https://client.crisp.chat/l.js
初始化的调用链条:
- new function 进入到构造函数。用 new 关键字的时候,会把 function 的 this 指向它本身。
- 构造函数里面进行了环境变量定义。
- 如果已经 readyState 在 "interactive" 状态和 readyState 在 "complete" 则直接初始化。
- 在 window 上监听 DOMContentLoaded 事件,进行初始化。
- 在 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 则是建立与服务器的连接 减少跨源请求的感知延迟
注意点
- Cache-Control public, max-age=86400 缓存一天
- Vary 存在的话,需要 Vary 符合, cache 才会生效
- Content-Encoding 使用的是 br 压缩方式
- https://github.com/caddyserver/caddy/issues/4751
"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 类型表示可执行内容而有些是不可执行内容。浏览器可以通过请求头 Content-Type 来设置 X-Content-Type-Options 以阻止 MIME 嗅探。