Generate and Debug with Source Map

2014-01-06 W.Y. 更多博文 » 博客 » GitHub »

Source Map JavaScript SASS Debug Tools

原文链接 https://bubkoo.github.io/2014/01/06/generate-and-debug-with-source-map/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。


上一篇文章简单介绍了 Source Map,接下来我们来看看如何利用各种工具来生成 Source Map。

什么是 Source Map?

Source Map 提供了一个与语言无关的方式,来将生产环境中的代码映射回开发环境中的原始代码。

在现代的开发流程中,我们的开发环境和实际线上环境的代码通常都不一样。在应用上线部署前,我们通常都要对我们的代码进行编译、合并、压缩或者其他方面的优化,这使得我们非常困难来准确定位会原始代码。但是,在生成过程中,Source Map 文件储存了这些位置信息,因此,当我们查找一行中的某个位置时,Source Map 文件可以准确定位到原始文件中的位置。这使得我们线上环境中的代码变得可读,甚至可调试,为开发者提供了极大的便利。这就是 Source Map 的用武之地。

在这篇介绍性的教程中,我们利用一个非常简单的 JavaScript 和 SASS 代码,通过各种编译器运行它们,然后在 Source Map 的帮助下,在浏览器中查看我们的原始文件。文中示例代码可以在这里【下载】。 <!--more-->

Source Map 文件包含的信息

Source Map 文件包含了从优化后的文件到原始文件的映射信息,Source Map 文件的结构通常是JSON格式的,使用 V3 规范。它通常包含以下属性:

  1. version:Source Map 的版本号,通常是 3
  2. file:优化文件的文件名
  3. sources:原始文件的文件名
  4. names:映射所需要的符号
  5. mappings:映射数据

浏览器支持

Chrome 或 WebKit 内核浏览器已经支持 JavaScript Source Maps,Chrome 甚至支持 SASS Source MapsFirefox 23 以及以上的版本的开发者工具中默认开启了 Source Maps 功能。下面图片中 Firefox 默认开启了对 Source Map 的支持。

本文中以 Chrome 为调试浏览器,通过下面步骤来开启你的开发者工具对 Source Map 的支持:

  • 打开 Chrome 的开发人员工具:菜单 -> 工具 -> 开发者工具 或者直接按 F12
  • 点击右下角的“设置”齿轮
  • 选择“General”,并选择“Enable source maps”

下载并运行示例

从【这里】下载示例,解压后打开“start”目录,里面的文件和目录结构非常清晰,有一些简单的 JavaScript 文件,例如 scripts/script.js,用 Chrome 打开index.html,可以在文本框中输入一些CSS颜色名称或十六进制颜色值来修改背景颜色。

$ start 目录结构
.
├── index.html
├── scripts
│   ├── jquery.d.ts
│   ├── script.coffee.coffee
│   ├── script.js
│   └── script.typescript.ts
└── styles
    ├── style.css
    └── style.sass

用你常用的编辑器,快速预览一下文件夹中的纯JavaScript、TypeScript 和 CoffeeScript 文件。我们将创建一个生产版本,以及生成相应的 Source Map。

接下来,我将用五种不同的方式来生成一个编译和压缩后的script.js,同时生成对应的 Source Map 文件。你可以选择测试所有的方法,或者选择一个你已经熟悉的方法。这五种方法包括:

使用 Closure Compiler

Closure Compiler 是Google推出优化 JavaScript 的一个工具,它通过分析代码,删除无关的部分,然后将剩余部分压缩,除那以外,它也可以生成 Source Map 文件

我们可以使用 Closure compiler 按照以下步骤来生成优化后的 script.js

  1. 点击这里下载最新版本的 Closure compiler
  2. 负责下载的 compiler.jar 文件到 /start/scripts/ 目录
  3. /start/scripts/ 目录中,按住 Shift 点击右键,在弹出菜单中选择“在此处打开命令窗口”,输入下面的命令并执行,这样就创建了一个优化后的 script.closure.js 文件
  java -jar compiler.jar
      --js script.js
      --js_output_file script.closure.js
  1. 在编辑器中打开 index.html 文件,修改引用的 JavaScript 文件为刚刚创建的 scripts/script.closure.js
  <script src="scripts/script.jsmin-grunt.js"></script>

简单说明:--js 表示需要优化的 JavaScript 文件,--js_output_file 表示输出文件名,这里是 script.closure.js 。

在 Chrome 中打开 index.html,然后开打开发者工具,选择 Sources 选项卡,可以看到 index.html 只引用了优化后的 script.closure.js,我们没有办法看到我们最初创建的有适当的缩进 JavaScript 文件。接下来我们在 /start/scripts/ 目录中执行下面的命令来创建 Source Map 文件。

java -jar compiler.jar
      --js script.js 
      --create_source_map script.closure.js.map 
      --source_map_format=V3 
      --js_output_file script.closure.js

注意 Closure Compiler 中的新增的两个设置项,--create_source_map 代表要创建的Source Map 文件的文件名,这里是:script.closure.js.map--source_map_format 代表 Source Map 的版本是 V3。然后在 script.closure.js 文件的末尾添加 Source Map 文件的 URL,这样优化后的 JavaScript 就包含了 Source Map 的位置信息,添加 Source Map 文件的 URL 代码如下:

//@ sourceMappingURL=script.closure.js.map

刷新页面,在开发者工具中我们可以看到 “scripts” 目录下包含我们的原始代码文件 “script.js” 和优化后的文件 “script.closure.js” ,但浏览器实际运行的是在 index.html 中我们引用的优化后的文件,这样 Source Map 就为我们建立了一个指向源文件的连接。

同时,你也可以尝试在源文件中断点来调试代码,需要注意的是 “计算表达式” 和 “变量” 在 Source Map 中是不可用的,希望将来他们也被支持。

使用 Grunt 的插件 JSMin

如果你已经在使用 Grunt 来构建项目,那么使用 Grunt 的插件 JSMin source maps 对你来说就是信手拈来的事。这个插件不仅会优化你的代码,而且也会生成 Source Map 文件。

下面将演示如何利用 JSMin 来压缩你的 script.js 文件:

  1. 打开控制台窗口,运行 npm install -g grunt 命令来安装 Grunt
  2. /start/ 目录下执行 npm install grunt-jsmin-sourcemap 命令来安装 Grunt 的插件 grunt-jsmin-sourcemap
  3. 编辑新创建的 grunt.js 文件,为了简便我们只创建了 jsmin-sourcemap 这一个任务
  module.exports = function(grunt) {
    grunt.loadNpmTasks('grunt-jsmin-sourcemap');
    grunt.initConfig({
      'jsmin-sourcemap': {
        all: {
          src: ['scripts/script.js'],
          dest: 'scripts/script.jsmin-grunt.js',
          destMap: 'scripts/script.jsmin-grunt.js.map'
        }
      }
    });
    grunt.registerTask('default', 'jsmin-sourcemap');
  };
  1. 返回控制台,运行 grunt 命令,这将默认运行 jsmin-sourcemap 这个任务,因为在上一步的配置中我们将 jsmin-sourcemap 设置成了默认任务
  2. 打开新创建的 script.grunt-jsmin.js.map 文件,可以查看到源文件是 "sources":["script.js"]
  3. 在编辑器中打开 index.html 文件,修改引用的 JavaScript 文件为刚刚创建的 script.grunt-jsmin.js
  <script src="scripts/script.jsmin-grunt.js"></script>
  1. 刷新,在浏览器中查看效果

利用 Grunt 和它的插件 jsmin-sourcemap 创建了压缩后的文件:script.jsmin-grunt.js 和 Source Map 文件:script.jsmin-grunt.js.map。

使用 UglifyJS

UglifyJS2 是另一个压缩 JavaScript 的工具,和上面的两个工具一样,UglifyJS2 将创建压缩后的文件,同时在文件末尾加上 Source Map 文件的 URL,同时也创建了 Source Map 文件。在 /start/ 目录中执行下面的命令来使用 UglifyJS2:

  1. 安装 UglifyJS2
  npm install uglify-js
  1. 在 /start/scripts/ 目录中执行以下命令来压缩原文件,同时创建 Source Map 文件
  uglifyjs --source-map script.uglify.js.map --output script.uglify.js script.js
  1. 在编辑器中打开 index.html 文件,修改引用的 JavaScript 文件为刚刚创建的 script.uglify.js
  <script src="scripts/script.uglify.js"></script>

使用 CoffeeScript Redux##

前面的例子中我们只进行了压缩这样一步操作,但是,对于 CoffeeScript 这样的语言,我们需要两步操作:CoffeeScript -> JavaScript -> 压缩后 JavaScript。这一部分,我将介绍怎么样通过 CoffeeScript Redux 来创建 Multi-Level Source Maps

步骤一:从 CoffeeScript 到纯 JavaScript

  1. 全局安装 CoffeeScript:
  npm install -g coffee-script
  1. 使用下面命令编译 CoffeeScript 文件:script.coffee.coffee,生成纯 JavaScript 代码:
  coffee -c scripts/script.coffee.coffee
  1. 安装 CoffeeScript Redux:
  git clone https://github.com/michaelficarra/CoffeeScriptRedux.git coffee-redux
  cd coffee-redux
  npm install
  make -j test
  cd ..
  1. 接下来,创建 Source Map 文件:script.coffee.js.map,这个文件包含从生成的纯 JavaScript 到原始的 CoffeeScript 文件的位置信息:
  coffee-redux/bin/coffee --source-map -i scripts/script.coffee.coffee > scripts/script.coffee.js.map
  1. 查看生成的 script.coffee.js 文件,确保文件末尾包含 Source Map 信息:
  //@ sourceMappingURL=script.coffee.js.map
  1. 查看 script.coffee.js.map 文件,确保引用文件是:"file":"script.coffee.coffee",源文件是:"sources":["script.coffee.coffee"]

步骤二:从纯 JavaScript 文件到压缩的 JavaScript 文件

  1. 我们再一次使用 UglifyJS 来压缩 JavaScript 文件和生成 Source Map 文件,这一次我们需要指定一个 Source Map 文件来确保可以回到原始的 CoffeeScript 文件,在 /start/script/目录下执行下面命令:
  cd scripts/
  uglifyjs script.coffee.js -o script.coffee.min.js --source-map script.coffee.min.js.map --in-source-map script.coffee.js.map
  1. 打开 script.coffee.min.js.map 文件,确保里面包含正确的引用文件:"file":"script.coffee.min.js",和正确的源文件:"sources":["script.coffee.coffee"]。

使用 TypeScript

和 CoffeeScript 一样,TypeScript 也需要两步操作:TypeScript -> JavaScript -> 压缩的 JavaScript。由于使用的是 jQuery 插件,我们需要两个 TypeScript 文件:script.typescript.ts 和 jquery.d.ts。这两个文件在示例中 /complete/scripts/目录下。

步骤一:从 TypeScript 文件到纯 JavaScript 文件 在 /start/scripts/ 文件夹下执行下面命令,这将创建一个新的 JavaScript 文件:script.typescript.js,文件末尾包含 Source Map 信息://@ sourceMappingURL=script.typescript.js.map,执行这个命令的同时也创建了 Source Map 文件:script.typescript.js.map。

  tsc script.typescript.ts -sourcemap

步骤二:从纯 JavaScript 文件到压缩的 JavaScript 文件 和 CoffeeScript 的例子一样,我们使用 UglifyJS 来压缩 JavaScript 文件

  uglifyjs script.typescript.js -o script.typescript.min.js --source-map script.typescript.min.js.map --in-source-map script.typescript.js.map

最后,在编辑器中打开 index.html 文件,修改引用的 JavaScript 文件为刚刚创建的 scripts/script.typescript.min.js

  <script src="scripts/scripts/script.typescript.min.js"></script>

在 SASS 中使用 Source Map

除了 JavaScript,Chrome 还支持 SASS 和 SCSS 的 Source Map,为了演示 SASS 的 Source Map 功能,我们需要修改一下 Chrome 的设置,然后将 SASS 编译为带条件参数的 CSS 文件:

  1. 在修改任何设置前,在开发人员工具中监视一个元素,这将只显示 CSS 文件的引用。对于 SASS 来说意义并不是很大。

  1. 打开 chrome://flags/,启用开发者工具实验,重启 Chrome(最新版本的Chrome 无需设置,直接跳过)

  1. 开发者工具 > 设置 > Experiments > 选中 “Support for SASS” (最新版本的Chrome 无需设置,直接跳过)

  1. 开发者工具 > 设置 > General > 选中 “Enable source maps” 和 “Auto-reload CSS upon Sass save”

  1. 安装 SASS 预览版:
  gem install sass --version 3.3.0.alpha.243
  1. 在 /start/styles/ 目录下运行以下命令来编译 SASS 文件,同时生成一个 Source Map 文件,“--watch” 表示监视 SASS 文件的变化,然后自动生成 CSS 文件和对应的 Source Map 文件。
  sass --watch --sourcemap sass/styles.scss:styles.css
  1. 重启开发者工具并刷新页面

除了可以在浏览器中查看到 SASS 文件之外,如果你正在使用 LiveReload,任何对 SASS 文件的修改将立刻显示到浏览器中。

参考资源

Source Map 目前仍在迅速发展,网络上已经有一些很好的资源。如果你想了解更多信息,请参考以下链接。