【webpack】你所不知道的sourceMap

20,715 阅读7分钟

sourceMap的作用

通常,js代码出错,控制台会提示第几行第几列代码出错。但是webpack打包压缩后的代码,都被压缩到了一行,变量也变成了a,b,c,d。代码出错,控制台就没法正确的提示错误位置。

sourceMap就可以解决这个问题。sourceMap就是一个信息文件,里面储存着打包前的位置信息。也就是说,转换后的代码的每一个位置,所对应的转换前的位置。具体如何编码,可以看下阮一峰的文章

有了它,出错的时候,浏览器控制台将直接显示原始代码出错的位置,而不是转换后的代码,点击出错信息将直接跳转到原始代码位置。

sourceMap的调试

  1. Console直接点击错误提示跳转到源码(如上图所示)

一般浏览器会默认开启sourceMap调试,如果控制台不能显示源码错误位置,则需要打开配置。

打开浏览器的这个配置后,控制台的Console面板的错误提示,直接点击就会跳转到对应的源文件出错位置。

  1. Sources面板找到源文件,进行断点调试

如果没Console没报错,但是页面显示不正确。可以点击控制台的Sources面板,源文件都在 webpack:// 目录下,或者直接搜索文件,打开源文件后进行断点调试。

按需加载的路由,只有页面加载了,源文件才会在这个目录下显示。

webpack配置

vue-cli3的vue.config.js文件:

module.exports = {
    productionSourceMap: false, //默认是true
}

打包后的文件:

对于打包后的sourceMapwebpack提供多种类型的配置

其中,开发环境使用:

  • eval:会把每个模块封装到 eval 里包裹起来执行,并且会在末尾追加map文件的地址。map文件映射到转换后的代码(可执行的js文件),而不是映射到原始代码(vue文件),所以不能正确的显示错误行数。

  • eval-source-map: 和 eval 类似,为每个模块生成原始的sourceMapmap文件会以dataURL的形式添加到js中(类似于图片的base64形式)。原始的sourceMap可以正确提示错误行数。

  • eval-cheap-source-map: 跟eval-source-map相同,唯一不同的就是增加了cheapcheap是指忽略了列信息(绝大部分时候列信息对于错误提示没啥用,只需要提示行数就行)。

  • cheap-module-eval-source-map: 与eval-cheap-source-map相同,但是包含了不同loader模块之间的sourceMap。例如借助babel编译ES6,如果生成不包含loadersourcemap,此时debug到的将是编译后的代码,而非原始代码。

生产环境使用:

  • source-map: map文件包含完整的原始代码,但是打包会很慢。打包后的js最后一行是map文件地址的注释

  • hidden-source-map:与 sourceMap 相同,也生成map文件,但是打包后的js最后没有map文件地址的引用。浏览器不会主动去请求map文件,一般用于网站错误分析,需要让错误分析工具按名称匹配到map文件。

  • nosources-source-map:生成的map文件不包含源码,但是会正确提示错误的行数。另外项目的目录结构和文件名称会暴露在Sources面板

特定环境用(针对一些第三方工具,用的很少,暂不详细讨论):

  • inline-source-map
  • inline-cheap-source-map
  • inline-cheap-module-source-map
  • cheap-source-map
  • cheap-module-source-map

其实就是inline,cheap,module,source-map的自由组合。

  • inline 是以dateURL的形式添加map,不额外生成map文件
  • cheap 是没有列信息
  • module 是包含了loadersourcemap
  • source-map 则是映射到源文件

不同的参数,生成的sourceMap不同,打包速度、体积、错误提示的效果也不同。而vue-cli3帮我们预选了一种模式:

开发环境,配置文件位置:node_modules\\@vue\cli-service\lib\config\dev.js,第5行

生产环境,配置文件位置: \node_modules\\@vue\cli-service\lib\config\prod.js,第11行

另外:css同样有sourceMap不过vue-cli3是默认关闭的

module.exports = {
    css: {
        sourceMap: false, //默认false
    }
}

sourceMap的安全问题

事情都是具有两面性的,方便调试的同时,也将源码暴露在控制台,可能会导致代码泄露的安全问题。

虽然说前端代码是公开的,但是代码的压缩混淆也一定程度上提高了安全性。

代码泄露可能导致的问题:

  1. 代码被抄袭

场景:给A客户写了一个地图功能,A客户展示给B客户,B客户想要写地图,但是不会,查看源码后,抄走了地图功能

  1. 业务流失

场景:竞争对手拿到了源码,挑出其中的漏洞或者缺陷,大肆宣传。或者直接写一份后台,成本少,价格低导致业务被抢走

  1. 系统被攻击

场景:由于项目急,鉴权是通过前端动态生成路由控制的,后台没做判断,导致修改源码后,可以获得更多的权限(数据被删除)

例如常见的视频播放网站,如果不加密,直接一个<video>标签,那么就很容易被人拿到视频资源,自己的会员业务也会损失。

sourceMap的处理

如果无特殊需求,生产环境是需要关闭这个选项的,vue-cli3直接配置productionSourceMap: false即可。或者不关闭但是在测试环境迁移到正式环境时删掉map文件。也可以通过服务器配置,特殊账号(调试专用)能访问到map文件,其他用户则不行。

如果需要控制台能正确提示报错的位置而不暴露源码,推荐用nosources-source-map模式,但是这个模式会暴露源码的目录结构与文件命名。一般测试环境用这个比较好,QA测试出来的问题能正确提示错误,即使运维忘了删除sourceMap文件,也不会暴露源码。

// vue.config.js
module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {//只修改生产环境配置
        return {
           devtool: 'nosources-source-map',
        };
    }
  }
}

补充:本地sourceMap调试

  1. 办法1 : 网络拦截

webpack打包仍然生成sourceMap,但是将map文件挑出放到本地服务器,将不含有map文件的部署到服务器,借助第三方软件(例如fiddler),将浏览器对map文件的请求拦截到本地服务器,就可以实现本地sourceMap调试。

  1. 办法2 : 修改webpack打包,测试环境不放map文件,修改sourceMap的引用到本地服务器

可行性:本地调试专用,由于测试环境和本机属于同一个局域网,所以测试环境可以请求到本地服务器上的sourceMap文件,部署之后,客户访问则不能访问到本机的局域网IP,也就无法拿到sourceMap文件。

缺点:其他人打包需要修改电脑IP和本地服务器文件地址,无法直接复用,优点是绝对安全。或者可以提供另一台专门放map文件的服务器,打包之后自动上传map文件到这个服务器。

借助SourceMapDevToolPlugin修改打包后的文件对map文件的引用地址,同时借助filemanager-webpack-pluginmap文件移到到本地服务器上。代码如下(vue.config.js):

const webpack = require('webpack')
const FileManagerPlugin = require('filemanager-webpack-plugin');

module.exports = {
  configureWebpack: config => {
    if (process.env.NODE_ENV === 'production') {
      return {
        devtool: false, // 注意SourceMapDevToolPlugin和devtool不可共存
        plugins: [
          new webpack.SourceMapDevToolPlugin({
            append: '\n//# sourceMappingURL=http://192.168.23.230/sourceMap/[url]', // 192.168.23.230是本机局域网IP
            filename: '[file].map',
          }),
          new FileManagerPlugin({
            onEnd: {
              copy: [{ 
                source: './dist/js/*.map', 
                destination: 'D:/xampp/htdocs/sourceMap', // D:/xampp/htdocs/是本地服务器的文件地址
              }],
              delete: ['./dist/js/*.map'],
              archive: [{ //顺便把网站文件压缩一下
                source: './dist',
                destination: './dist/dist.zip',
              }]
            }
          })
        ]
      };
    }
  }
}

这个插件主要是实现了对sourceMap的更精细控制,比如打包之后的位置,以及修改js/css文件最后面对sourceMap的引用地址。 这里主要用到修改sourceMap引用地址的功能。

使用这个必须关闭devtool

这个插件的主要功能是打包之后文件的移动,删除,拷贝,压缩等功能,使用之前需要先安装: npm install filemanager-webpack-plugin --save-dev

注意: 这个插件有bug,移动操作不支持模糊匹配,只能改成复制+删除

最后

有什么问题,欢迎指出。