资讯专栏INFORMATION COLUMN

细说 js 压缩、sourcemap、通过 sourcemap 查找原始报错信息

douzifly / 1931人阅读

摘要:文件就是记录了从源代码文件到压缩文件的一个代码对应关系记录表,通过压缩文件和文件可以原原本本找出源代码文件。

细说 js 压缩、sourcemap、通过 sourcemap 查找原始报错信息 1. js 压缩

js 压缩对前端开发者来说是一门必修课。

一般来说,压缩 js 主要出于以下两个目的:

减小代码体积,加快前端资源加载速度

保护源代码不被别人获取

压缩 js 使用的工具库:

UglifyJS2: 压缩 es5

uglify-es: 压缩 es6+

closure-compiler、closure-compiler-js: googlejs 压缩、优化工具

压缩 js 的主要过程:

移除无用代码

混淆代码中变量名称、函数名称等

预编译代码

对结构进行扁平化处理

1. 移除无用代码

去掉所有对解析引擎来说无用的字符,包括空格、注释、换行、没有用的变量声明、函数声明等。

2. 混淆代码中变量名称、函数名称等

把一些局部变量名称、函数名称等用 a, b, ...$1, $2, ..._1, _2, ... 之类的简略字符进行替换,达到混淆的目的。

源代码

(function () {
  var hello = "hi";
  var print = function (str) {
    console.log(str);
  };

  print(hello);
})();

压缩后的代码(仅演示混淆功能)

(function () {
  var a = "hi";
  var b = function (c) {
    console.log(c);
  };

  b(a);
})();
3. 预编译代码

把不依赖外部环境的逻辑提前进行运算,并把运算结果替换到相应的源码处,然后从源码中移除这段逻辑。

源代码

(function () {
  var hello = "hi" + " everyone, ";
  var count = 3 * 5;

  console.log(hello + count + " girls");
})();

压缩后的代码(仅演示预编译功能)

(function () {
  var hello = "hi everyone, ";
  var count = 15;

  console.log(hello + count + " girls");
})();
4. 对结构进行扁平化处理

对于 js 来说,嵌套越深,执行越慢,对代码进行扁平化处理也是优化代码的一种方式。

源代码

(function () {
  var say = {
    hello: function (str) {
      console.log("hello " + str);
    }
  };

  say.hello("everyone");
})();

压缩后的代码(仅演示扁平化结构功能)

!function(str){console.log("hello "+str)}("everyone");
完整示例

源代码

(function () {
  var say = {
    hello: function (str) {
      console.log("hello " + str);
    }
  };

  say.hello("everyone");
})();

压缩后的代码

!function(l){console.log("hello "+l)}("50 girls");
2. sourcemap

通常 js 压缩后只有一行代码,并且里面的变量名与函数名等都是混淆了的,这在实际运行中会有一个问题,就是 js 的报错信息将会失真,无法追踪到是在源代码哪一行哪一列报的错。

sourcemap 便是为了解决这个问题而生的。

sourcemap 文件就是记录了从源代码文件到压缩文件的一个代码对应关系记录表,通过压缩文件和 sourcemap 文件可以原原本本找出源代码文件。

查看阮一峰老师的 JavaScript Source Map 详解 了解 sourcemap 的原理与格式。

一般在压缩 js 的过程中,会生成相应的 sourcemap 文件,并且在压缩的 js 文件末尾追加 sourcemap 文件的链接 //# sourceMappingURL=bundle-file-name.js.map。这样,浏览器在加载这个压缩 js 的时候,就知道还有一个相应的 sourcemap 文件,也一并加载下来,运行的过程中如果 js 报错,也会给出相应源代码的行号与列号,而非压缩文件的。

比如,对下面的源码进行压缩:

(function () {
  var say = {
    hi: function () {
      console.log("hi");
    }
  };

  say.hello();

  return say;
})();

未加 sourcemap 文件时,报错信息是:

加上 sourcemap 文件时,报错信息是:

sourcemap 扩展

webpack 对 sourcemap 做了扩展,定义在 devtool 配置项中:

eval: 每个模块都使用 eval() 执行,并且都有 //@ sourceURL,构建很快,但无法正确显示行号

eval-source-map: 每个模块使用 eval() 执行,并且 source map 转换为 DataUrl 后添加到 eval() 中,一般开发模式中使用这种方式

cheap-eval-source-map: 类似 eval-source-map,但只映射行,不映射列,并忽略源自 loadersource map,仅显示转译后的代码

cheap-module-eval-source-map: 类似 cheap-eval-source-map,但会保留源自 loadersource map

inline-source-map: source map 转换为 DataUrl 后添加到 bundle

cheap-source-map: 只映射行,不映射列,并忽略源自 loadersource map,仅显示转译后的代码

inline-cheap-source-map: inline-source-mapcheap-source-map 的结合

cheap-module-source-map: 类似 cheap-module-eval-source-map,但不使用 eval() 执行

inline-cheap-module-source-map: inline-source-mapcheap-module-source-map 的结合

source-map: 整个 source map 作为一个多带带的文件生成,产品环境一般使用这种模式

hidden-source-map: 类似 source-map,但不会把 //# sourceMappingURL=bundle-file-name.js.map 追加到压缩文件后面

nosources-source-map: 类似 source-map,但只有堆栈信息,没有源码信息

更详细信息可以参考:

webpack devtool(英文)

webpack devtool(中文)

使用建议

对于使用 webpack 来构建项目,建议在开发时使用 eval-source-map,产品环境使用 source-map

因为用压缩文件与 sourcemap 文件是可以原原本本的找到源代码的,所以,为了保护源代码,可以这样隐藏 sourcemap 文件:

web 服务器设置外部不能访问 sourcemap 文件,只能内部访问

直接把 sourcemap 文件存放到其他地方

3. 通过 sourcemap 查找原始报错信息

一般而言,在产品阶段,我们会用 window.onerror 来捕获 js 报错,然后上报到服务器,以此来收集用户使用时发生的 bug

window.onerror = function(message, source, lineno, colno, error) {
  // message: 错误信息
  // source: 报错脚本的 url 地址
  // lineno: 行号
  // colno: 列号
  // error: 错误对象
  // 上报必要的信息到服务器
}

但产品环境的代码都是压缩的,行号和列号都是失真的,所以就需要用 sourcemap 文件来找到错误对应源代码的行号与列号,以及其他的信息。

使用工具: mozilla/source-map

源代码

(function () {
  var say = {
    hi: function () {
      console.log("hi");
    }
  };

  say.hello();

  return say;
})();

压缩后报错信息

window.onerror = function(message, source, lineno, colno, error) {
  console.log(`message: ${message}`);
  console.log(`source: ${source}`);
  console.log(`lineno: ${lineno}`);
  console.log(`colno: ${colno}`);
  console.log(`error: ${error}`);
}

// message: Uncaught TypeError: e.hello is not a function
// source: url/to/bundle.min.js
// lineno: 1
// colno: 982
// error: TypeError: e.hello is not a function

通过 source-map 查找原始报错信息

const fs = require("fs");
const SourceMap = require("source-map");

const { readFileSync } = fs;
const { SourceMapConsumer } = SourceMap;

const rawSourceMap = JSON.parse(readFileSync("path/to/js/map/file", "utf8"));

SourceMapConsumer.with(rawSourceMap, null, consumer => {
  const pos = consumer.originalPositionFor({
    line: 1,
    column: 982
  });

  console.log(pos);
});

查找到的原始信息

{ 
  source: "path/to/index.js",
  line: 8,
  column: 7,
  name: "hello" 
}

这样,便找到了原始报错信息:

原始报错文件:path/to/index.js

原始报错行号:8

原始报错列号:7

原始对象名称:hello

如此,便能一下子就找到错误在哪里了。

更多用法,参考 mozilla/source-map

后续

更多博客,查看 https://github.com/senntyou/blogs

作者:深予之 (@senntyou)

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99228.html

相关文章

  • 从 0 到 1 再到 100, 搭建、编写、构建一个前端项目

    摘要:从到再到搭建编写构建一个前端项目选择现成的项目模板还是自己搭建项目骨架搭建一个前端项目的方式有两种选择现成的项目模板自己搭建项目骨架。使用版本控制系统管理源代码项目搭建好后,需要一个版本控制系统来管理源代码。 从 0 到 1 再到 100, 搭建、编写、构建一个前端项目 1. 选择现成的项目模板还是自己搭建项目骨架 搭建一个前端项目的方式有两种:选择现成的项目模板、自己搭建项目骨架。 ...

    call_me_R 评论0 收藏0
  • 从 0 到 1 再到 100, 搭建、编写、构建一个前端项目

    摘要:从到再到搭建编写构建一个前端项目选择现成的项目模板还是自己搭建项目骨架搭建一个前端项目的方式有两种选择现成的项目模板自己搭建项目骨架。使用版本控制系统管理源代码项目搭建好后,需要一个版本控制系统来管理源代码。 从 0 到 1 再到 100, 搭建、编写、构建一个前端项目 1. 选择现成的项目模板还是自己搭建项目骨架 搭建一个前端项目的方式有两种:选择现成的项目模板、自己搭建项目骨架。 ...

    wzyplus 评论0 收藏0
  • 从 0 到 1 再到 100, 搭建、编写、构建一个前端项目

    摘要:从到再到搭建编写构建一个前端项目选择现成的项目模板还是自己搭建项目骨架搭建一个前端项目的方式有两种选择现成的项目模板自己搭建项目骨架。使用版本控制系统管理源代码项目搭建好后,需要一个版本控制系统来管理源代码。 从 0 到 1 再到 100, 搭建、编写、构建一个前端项目 1. 选择现成的项目模板还是自己搭建项目骨架 搭建一个前端项目的方式有两种:选择现成的项目模板、自己搭建项目骨架。 ...

    aristark 评论0 收藏0
  • 谈谈前端异常捕获与上报

    摘要:另外这样的异常捕获不能捕获的异常错误信息,这点需要注意。最终大致的流程图如下结语前端异常捕获与上报是前端异常监控的前提,了解并做好了异常数据的收集和分析才能实现一个完善的错误响应和处理机制,最终达成数据可视化。 关于 微信公众号:前端呼啦圈(Love-FED) 我的博客:劳卜的博客 知乎专栏:前端呼啦圈 前言 Hello,大家好,又与大家见面了,这次给大家分享下前端异常监控中需...

    Kosmos 评论0 收藏0
  • vue-cli解析

    摘要:在中,设置了一些配置,代码如下通过它的注释,我们可以理解它在中配置了静态路径本地服务器配置项等参数。下面还有一个的对象,它是在本地服务器启动时,打包的一些配置,代码如下其中包括模版文件的修改,打包完目录之后的一些路径设置,压缩等。 前言 这段时间,算是空出手来写几篇文章了。由于很久都没有时间整理现在所用的东西了,所以,接下来会慢慢整理出一些文档来记录前段时间的工作和生活。 这篇文章的主...

    KunMinX 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<