# 上报方式

这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向web服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而,对于开发者来说保证在文档卸载期间发送数据一直是一个困难。因为用户代理通常会忽略在 unload (en-US) 事件处理器中产生的异步 XMLHttpRequest。

过去,为了解决这个问题, 统计和诊断代码通常要在

发起一个同步 XMLHttpRequest 来发送数据。 创建一个 "" 元素并设置 src,大部分用户代理会延迟卸载(unload)文档以加载图像。 创建一个几秒的 no-op 循环。 上述的所有方法都会迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚。下一个页面对于这种较差的载入表现无能为力。

这就是 sendBeacon() 方法存在的意义。使用 sendBeacon() 方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能,这意味着:

  • 数据发送是可靠的。
  • 数据异步传输。
  • 不影响下一导航的载入。

数据是通过 HTTP POST 请求发送的。

网站通常希望在用户完成页面浏览后向服务器发送分析或诊断数据,最可靠的方法是在 visibilitychange (en-US) 事件发生时发送数据:

document.addEventListener('visibilitychange', function logData() {
  if (document.visibilityState === 'hidden') {
    navigator.sendBeacon('/log', analyticsData);
  }
});
1
2
3
4
5

# GIF

首先,1x1像素是最小的合法图片。而且,因为是通过图片打点,所以图片最好是透明的,这样一来不会影响页面本身展示效果,二者表示图片透明只要使用一个二进制位标记图片是透明色即可,不用存储色彩空间数据,可以节约体积。因为需要透明色,所以可以直接排除JEPG。 ​ 同样的响应,GIF可以比BMP节约41%的流量,比PNG节约35%的流量。GIF才是最佳选择。

  • 可以进行跨域
  • 不会携带cookie
  • 不需要等待服务器返回数据

# google 开发者推荐的上报方式

# 上报策略

采集性能指标后,最好先对异常数据处理(抹平异步数据)

  • 设置抽样策略

  • 强网情况下直接上报,弱网存储在本地,网络情况好的时候再上报

  • 可以采用延迟上报和批量上报

# 上报时机

  • 及时上送

  • 延时上报

    • 页面离开时上报
    • 下次打开时上报

# Sourcemap自动上传

为了我们每一次构建服务端能拿到最新的map文件,我们编写一个插件让webpack在打包完成后触发一个钩子实现文件上传

  • webpack 配置 插件
let SourceMapUploader = require("./source-map-upload");
module.exports = {
    configureWebpack: {
        resolve: {
            alias: {
                "@": resolve("src"),
            },
        },
        plugins: [
             new SourceMapUploader({url: "http://localhost:3000/upload"})
        ],
    }
    //   chainWebpack: (config) => {},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • source-map-upload 插件
//source-map-upload.js
const fs = require("fs");
const http = require("http");
const path = require("path");
class SourceMapUploader {
  constructor(options) {
    this.options = options;
  }
  /**
   * 用到了hooks,done表示在打包完成之后
   * status.compilation.outputOptions就是打包的dist文件
   */
  apply(compiler) {
    if (process.env.NODE_ENV == "production") {
      compiler.hooks.done.tap("sourcemap-uploader", async (status) => {
        // console.log(status.compilation.outputOptions.path);
        // 读取目录下的map后缀的文件
        let dir = path.join(status.compilation.outputOptions.path, "/js/");
        let chunks = fs.readdirSync(dir);
        let map_file = chunks.filter((item) => {
          return item.match(/\.js\.map$/) !== null;
        });
        // 上传sourcemap
        while (map_file.length > 0) {
          let file = map_file.shift();
          await this.upload(this.options.url, path.join(dir, file));
        }
      });
    }
  }
  //调用upload接口,上传文件
  upload(url, file) {
    return new Promise((resolve) => {
      let req = http.request(`${url}?name=${path.basename(file)}`, {
        method: "POST",
        headers: {
          "Content-Type": "application/octet-stream",
          Connection: "keep-alive",
        },
      });

      let fileStream = fs.createReadStream(file);
      fileStream.pipe(req, { end: false });
      fileStream.on("end", function() {
        req.end();
        resolve();
      });
    });
  }
}
module.exports = SourceMapUploader;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

# 参考