1.SourceMap-使用教程
2.认识 sourceMap
3.2022年末了,react拖拽组件和最牛的代码调试技巧一起学!
4.何为SourceMap?讲讲SourceMap食用姿势
5.放弃 console.log 吧!用 Debugger 你能读懂各种源码
6.source-map原理及应用
SourceMap-使用教程
源码映射(SourceMap)是一个存储源代码与编译代码对应位置映射的信息文件,主要在前端开发中解决以下三个方面的问题:
a. 代码压缩混淆后
b. 利用sass、typescript等其他语言编译成css或JS后
c. 利用webpack等打包工具进行多文件合并后
使用源码映射可以在控制台中将编译后的高端德州APP源码代码转换为源代码,方便进行调试。
源码映射实际上是一个JSON键值对,使用VLQ编码与特定规则存储位置信息,原理了解具体实现即可,因为它是工具生成的文件,不需要手动编写。
在Chrome中启用源码映射功能,进入开发者模式设置,勾选允许JS和css源码映射。生成源码映射文件可以通过多种方法,如使用Google的Closure编译器、Gulp、Grunt等工具。在Gulp中,通过使用gulp-sourcemaps插件来生成源码映射文件。
在Gulp中使用源码映射文件,首先需要在文件中添加注释以指示源码映射文件的位置,当打开原文件时可以查看到该注释。使用源码映射文件时,需要在Chrome开发者模式下查看Sources中的文件,理解其三个感叹号代表的内容。
了解gulp-sourcemaps API可以进一步优化源码映射的使用,包括初始化、生成、源路径定义和映射生成等操作。熟悉API用法,可以更好地管理和优化源码映射。
在使用Gulp-sourcemaps插件时,需要注意其支持的插件类型,如通用、JS和CSS等,并可添加插件以扩展功能。目前,了解详细插件用法和制作插件的步骤仍需进一步探索。
综上所述,源码映射是前端开发中解决代码压缩混淆、编译和其他语言转换后调试问题的重要工具。理解其原理和使用方法,可以显著提升开发效率和调试体验。希望本文提供的内容能够帮助您更好地理解和利用源码映射技术。
认识 sourceMap
sourceMap是什么?
当我们的代码在浏览器中运行时,实际上是通过打包压缩后的代码来执行的,并不是我们编写的原始源代码。然而,当遇到代码报错需要进行调试时,这个过程变得相当繁琐。那么,如何来调试这种经过转换的代码呢?答案是使用sourceMap。
sourceMap的作用是从已转换的代码中映射到原始的源文件。换句话说,转换后的代码中的每一个位置,都对应着转换前的确切位置。
例如,没有使用sourceMap时,调试起来非常困难,因为代码已经被压缩。而使用sourceMap后,一旦代码出现报错,就会直接指向源文件,帮助我们迅速定位到错误的准确位置。
如何使用sourceMap?
在webpack打包过程中,我们可以通过配置来生成sourceMap。具体来说,需要在打包配置中设置devtool属性。打包完成后,会在文件夹下面生成一个以.map为后缀名的文件。这个文件是一个对象,其中包含了version、sources、风云2赵文卓源码mappings、names等属性。在出口文件的最后,会添加一个注释,指向sourceMap的路径,例如://# sourceMappingURL=hh.js.map
年末了,react拖拽组件和最牛的代码调试技巧一起学!
年末了,react 拖拽组件和最牛的代码调试技巧一起学!前言
最近刷到了利用 H5drag、dropapi 进行拖拽组件实现的代码示例,于是想学习一下业界知名的一些拖拽组件。于是想从学习成本较低的react-sortable-hoc开始看起。那么对于一个学习者而言,我们应该如何地去优雅地学习第三方库呢?
当然是「调试」啦。
首先第一步,我们随便创建一个 react 项目,并且按照react-sortable-hoc的最简单的案例编写后准备调试。
比如说我们想看看SortableHandler里面的具体实现,我们给它打个断点,并且创建一个 vscode debug 配置:
按F5开启调试后我们进入SortableHandler中,看到的却是经过打包后的产物:
这显然非常不利于去读懂代码。那么我们该如何将它变成我们能看得懂的源码呢?答案就是sourcemap!
sourcemap 就是用于表示打包后代码和源码的映射关系。因此我们只需要开启 sourcemap 就可以进行 debug 的源码的映射。
我们将react-sortable-hoc项目 clone 下来(这里只拉取一层 commit、一个 master 分支):
我们可以发现整个项目是使用rollup进行打包的,我们只需要配置一下 sourcemap 开启:
然后执行npm run build,将打包好的 dist 文件夹替换至node_modules/react-sortable-hoc/dist目录下。接着在我们测试项目中将其引入路径改为:
然后我们再来运行一下 debug 试试看:
瞧!这是不是非常熟悉呢?利用调试我们可以随时随地打断点,知道变量的运行时,读起源码来是不是非常轻松呢?
注有的小伙伴可能会发现在调试的时候,打开的源码文件是只读模式,这是为什么呢?
我们可以在 vscode 左侧的CALL STACK中找到当前文件映射到的目录。
如果是node_modules/react-sortable-hoc/src/.../xxx.js,就证明你映射到的只是node_modules中的路径,是无法更改的。
这时候,你可以点击该文件对应的.js.map文件,将其中的../src/xxx.js路径改成你克隆下来的react-sortable-hoc的路径。这样的话,映射到的目录就是你本地的文件,就可以编辑啦!!~
我们修改过node_modules下的文件但又不想被覆盖,可以使用patch-package这个包。 npx patch-package react-sortable-hoc 可以生成一个 diff 文件,上传至 GitHub 上,别人 clone 后只需要运行npx patch-package即可将 diff 覆盖到node_modules下
源码阅读组件的初始化
我们首先来梳理一下示例代码的组件嵌套:
SortableContainer >> SortableElement >> SortableHandler
我们先从组件的初始化入手,从外到内一层一层解析:
SortableContainer
可以发现SortableContainer来初始化的时候,获取了各种 dom 结构以及绑定好了事件。
除此之外,它 new 了一个Manager作为总的拖拽管理中心。其主要功能如下:「注册并储存可拖拽的子节点」,「记录当前激活节点的 index」,「根据 index 进行 sort」:
最后,它渲染函数是这样的:
即通过Provider将全局 Manager 对象传递给了子组件。
SortableElement
我们可以看到,其实SortableElement的初始化只是将自身节点以及一些属性信息注册到了全局Manager对象中。
SortableHandle
SortableHandle的代码就更简单了,只是在自身 dom 上添加了一个sortableHandle的标识,用于判断用户当前点击的节点是否是SortableHandle。这部分逻辑我们在下面就可以看到~
事件触发
了解了各个组件的初始化流程之后,我们可以开始调试拖拽的整个过程的实现逻辑了~
首先我们要知道,所有的事件都是注册在SortableContainer中的,因此我们只需要对其进行调试即可。
拖拽触发事件顺序如下图:
下面让我们来看一下各种事件的逻辑吧:
handleStart
在handleStart的这个回调函数中,我们可以发现它主要做了一下事情:
handlePress
注意看,这个函数有一个比较关键的思想:就是利用克隆节点来模拟正在拖拽的节点。计算并记录好所需要的图形指标并且赋值到新节点上,并且设置position:fixed。
最后在绑定上move事件的监听----handleSortMove.
handleSortMove
函数本身很简洁,首先是彩虹5.0破解源码updateHelperPosition。
updateHelperPosition
updateHelperPosition的代码经过清理后,核心就在于对克隆元素设置translate,来模拟拖拽的过程。
其次就是最重要的animateNodes函数了。
这里包含了拖拽排序最核心的节点移动逻辑。核心思想如下:
遍历所有sortableElement,如果是当前激活节点,则把原有节点透明化。(因为有克隆节点了);如果不是,则判断激活节点的坐标以及当前遍历元素的坐标的大小,依此来进行translate3d的动画。
handleSortEnd
最后,当拖拽结束后,触发handleSortEnd。主要逻辑是做一些善后处理,清理各种事件监听器,全局 Manager 的变化,本身被拖拽元素恢复透明度等。。
总结
到这里,整个react-sortable-hoc实现的大致思想就全部介绍完毕啦。它并没有利用 h5 的dragapi,而是利用mousemove、touchmove之类的事件实现 h5 和移动端的兼容。利用 css3 的动画来实现 sort 效果。
但实现过程中也有一些缺点。
比如reactDom.findDomNodeapi,react 并不推荐使用它来去获取 dom,可以换成ref。
比如只能在react类组件中使用。
其他
觉得封装的比较好的工具函数用于学习记录:
2. 获取当前元素距离窗口的偏移值(也可以使用elm.getBoundingClientRect())
3.移动数组内元素
4. 过滤对象某些属性
何为SourceMap?讲讲SourceMap食用姿势
在前端开发中,我们经常面临代码错误定位的问题。尽管打包后的代码难以直接阅读,但神奇的是,我们仍能在控制台找到问题的根源。这一切得益于幕后英雄——source map。它是一种机制,帮助我们在编译和压缩后的代码中追溯到原始代码的位置。
source map的工作原理是通过生成一份映射文件,将编译后的代码与原始代码建立起对应关系。举个例子,当你使用babel编译代码时,会同时生成一份包含原始信息的.sourcemap文件。这个文件中,如names数组、mappings字段等属性记录了代码转换过程中的详细映射,包括原始代码的行号和字符位置。
生成source map的过程涉及对原始代码和编译后的代码进行一一对应,记录下每个编译后的代码片段在原始代码中的位置。通过一系列的优化,如省去行号、使用分隔符和VLQ编码等技术,使得这个映射文件尽可能地紧凑。
在webpack等打包工具中,devtool属性决定如何生成和处理source map。常见的模式有source-map、inline-source-map、eval-source-map等,各有优缺点。开发环境中推荐使用eval-source-map,它提供了原始代码的可见性;生产环境则倾向于hidden-source-map,以保护源代码隐私。
source map的精度有时可能会受到影响,这可能是由于打包过程中的配置问题或者在某些模式下牺牲了一些信息。一旦遇到定位不准,尝试调整devtool模式或检查打包过程中的代码转换设置通常能解决问题。
放弃 console.log 吧!用 Debugger 你能读懂各种源码
很多同学不清楚为什么要使用debugger进行调试,难道console.log不行吗?
即使学会了使用debugger,还是有很多代码看不懂,如何调试复杂的源码呢?
这篇文章将为你讲解为什么要使用这些调试工具:console.log vs Debugger。
相信绝大多数同学都会使用console.log进行调试,将想查看的变量值打印在控制台。
这种方法可以满足基本需求,crm设计源码下载但遇到对象打印时就无法胜任了。
比如,我想查看webpack源码中的compilation对象的值,我尝试打印了一下:
但你会发现,当对象的值也是对象时,它不会展开,而是打印一个[Object] [Array]这样的字符串。
更严重的是,打印的内容过长会超过缓冲区的大小,在terminal中显示不全:
而使用debugger来运行,在这里设置一个断点查看,就没有这些问题了:
有些同学可能会说,那打印一个简单的值时使用console.log还是很方便的。
比如这样:
真的吗?
那还不如使用logpoint:
代码执行到这里就会打印:
而且没有污染代码,使用console.log的话,调试完成后这个console也不得不删除掉。
而logpoint不需要,它就是一个断点的设置,不在代码中。
当然,最重要的是debugger调试可以看到调用栈和作用域!
首先是调用栈,它就是代码的执行路线。
比如这个App的函数组件,你可以看到渲染这个函数组件会经历workLoop、beginWork、renderWithHooks等流程:
你可以点击调用栈的每一帧,查看都执行了什么逻辑,用到了什么数据。比如可以看到这个函数组件的fiber节点:
再就是作用域,点击每一个栈帧就可以看到每个函数的作用域中的变量:
使用debugger可以看到代码的执行路径,每一步的作用域信息。而你使用console.log呢?
只能看到那个变量的值而已。
得到的信息量差距不是一点半点,调试时间长了,别人会对代码的运行流程越来越清晰,而你使用console.log呢?还是老样子,因为你看不到代码执行路径。
所以,不管是调试库的源码还是业务代码,不管是调试Node.js还是网页,都推荐使用debugger打断点,别再用console.log了,即使想打印日志,也可以使用LogPoint。
而且在排查问题的时候,使用debugger的话可以加一个异常断点,代码跑到抛异常的地方就会断住:
可以看到调用栈来理清出错前都走了哪些代码,可以通过作用域来看到每一个变量的值。
有了这些,排查错误就变得轻松多了!
而你使用console.log呢?
什么也没有,只能自己猜。
Performance
前面说debugger调试可以看到一条代码的执行路径,但是代码的执行路径往往比较曲折。
比如那个React会对每个fiber节点做处理,每个节点都会调用beginWork。处理完之后又会处理下一个节点,再次调用beginWork:
就像你走了一条小路,然后回到大路之后又走了另一条小路,使用debugger只能看到当前这条小路的执行路径,看不到其他小路的路径:
这时候就可以结合Performance工具了,使用Performance工具看到代码执行的全貌,然后用debugger来深入每一条代码执行路径的细节。
SourceMap
sourcemap非常重要,因为我们执行的都是编译打包后的代码,基本是不可读的,调试这种代码也没有什么意义,而sourcemap就可以让我们直接调试最初的源码。
比如vue,关联了sourcemap之后,我们能直接调试ts源码:
nest.js也是:
不使用sourcemap的话,想搞懂源码,但你调试的寻银行系统源码是编译后的代码,怎么读懂呢?
读懂一行
前面说的debugger、Performance、SourceMap只是调试代码的工具,那会了调试工具,依然读不懂代码怎么办呢?
我觉得这是不可能的。
为什么这么说呢?
就拿react源码来说:
switch case能读懂吧。三目运算符能读懂吧。函数调用能读懂吧。
每一行代码都能读懂,而全部的代码不就是由这一行行代码组成的么?
加上我们可以单步执行来知道代码执行路径。
为啥每行代码都能读懂,连起来就读不懂了呢?
那应该是代码太多了,而你花的时间不够而已。
先要读懂一行,一个函数,读懂一个小功能的实现流程,慢慢积累,之后了解的越来越多之后,你能读懂的代码就会越多。
总结
这篇文章讲了为什么要使用调试工具,如何读懂复杂代码。
console.log的弊端太多了,大对象打印不全,会超过terminal缓冲区,对象属性不能展开等等,不建议大家使用。即使要打印也可以使用LogPoint。
使用debugger可以看到调用栈,也就是代码的执行路径,每个栈帧的作用域,可以知道代码从开始运行到现在都经历了什么,而console.log只能知道某个变量的值。
此外,报错的时候也可以通过异常断点来梳理代码执行路径来排查报错原因。
但debugger只能看到一条执行路径,可以使用Performance录制代码执行的全流程,然后再结合debugger来深入其中一条路径的执行细节。
此外,只有调试最初的源码才有意义,不然调试编译后的代码会少很多信息。可以通过SourceMap来关联到源码,不管是Vue、React的源码还是Nest.js、Babel等的源码。
会了调试之后,就能调试各种代码了,不存在看不懂的源码,因为每一行代码都是基础的语法,都是能看懂的,如果看不懂,只可能是代码太多了,你需要更多的耐心去读一行行代码、一个个函数、理清一个个功能的实现,慢慢积累就好了。
掌握基于debugger、Performance、SourceMap等调试代码之后,各种网页和Node.js代码都能调试,各种源码都能读懂!
source-map原理及应用
源码映射(Source Map)是存放源代码与编译代码对应位置映射信息的文件,帮助开发者在生产环境中精确定位问题。当开启source-map编译后,构建工具生成的sourcemap文件可以在特定事件触发时,自动加载并重构代码回原始形态。
sourcemap文件由多个部分组成,V3版本的文件包括文件名、源码根目录、变量名、源码文件、源码内容以及位置映射。映射数据使用VLQ编码进行压缩,以减小文件体积。
当页面运行时加载编译构建产物,特定事件如打开Chrome Devtool面板时,系统会根据源码映射加载相应Map文件,重构代码至原始形态。
sourcemap文件内容包括文件名、源码根目录、变量名、源码文件、源码内容以及位置映射。位置映射由VLQ编码表示,用于还原编译产物到源码位置。
Webpack提供多种设置源码映射的方式,包括通过配置项设置规则短语或使用插件深度定制生成逻辑。这些设置符合特定正则表达式,如source-map、eval-source-map、cheap-source-map等,分别对应不同的生成策略。
cheap-source-map和module-cheap-source-map在cheap场景下生效,允许根据loader联调处理结果或原始代码作为source。nosources-source-map则不包含源码内容,而inline-source-map将sourcemap编码为Base DataURL,直接追加到产物文件中。
通常,产物中需要携带`# sourceMappingURL=`指令以正确找到sourcemap文件。当使用hidden-source-map时,编译产物中不包含此指令。需要时,可手动加载sourcemap文件。
通过sourcemap文件,开发者可以上传至远端,根据报错信息定位源码出错位置,实现高效问题定位与调试。
Source Map 原理及源码探索
前端开发中,代码经过转换后发布到线上时,通常会遇到压缩或混淆的问题,这虽然减小了代码体积,降低了网络开销,但同时也给开发者调试代码带来了不便。为解决这一难题,Source Map应运而生,旨在提供一种方式,使得开发者能够在压缩或混淆后的代码上进行源代码级别的调试。
从Source Map的诞生和演变可以看出,它经历了几个版本的更新,以适应不同场景的需求。最初由Joseph Schorr创建的v1版,旨在让闭包检查器在优化JS代码时进行源代码级别的调试。随着项目规模的扩大,v1版的映射结果变得异常冗长。v2版对此进行了优化,增加了映射文件的灵活性和简便性,减少了映射文件的总体大小,相较于v1版减少了约%至%。然而,v2版仍存在一些问题,因此v3版应运而生,进一步缩减了映射文件的大小,相较于v2版减少了大约%。
在v3规范格式中,mappings数据遵循一定的规则,其中VLQ(Variable-Length Quantized)编码起到了关键作用。VLQ编码的原理基于简化数字表示,通过使用特殊字符分隔数字,减少不必要的字符,实现数据的紧凑存储。VLQ背后的想法很简单,即根据数字位数调整分隔符,当数字位数减少时,可以省去分隔符,从而减少存储空间。
VLQ的进制表示和2进制表示展示了其灵活性。进制表示时,通过在数字之间插入分隔符来区分不同数字。二进制表示中,使用由6位组成的二进制组来表示数值,其中第一位作为连续标记位,确定后续字节组是否需要连续表示,最后一位作为符号标记位,指示数值的正负。这种编码方式允许更高效地表示数值,特别是当数值位数减少时,可以显著节省空间。
在实际应用中,通过Base编码,VLQ编码的数字可以进一步压缩,使得映射文件更加紧凑。在生成映射文件时,通常需要考虑输入文件的行号,但随着内容的增多,映射编码会快速增多,占用大量空间。为解决这一问题,可以采取以下改进措施:
1. 省略输出文件的行号,使用“;”换行来节省空间。
2. 名称和输入文件列表按索引引用,提取出两个索引表,减少重复记录。
3. 使用相对偏移,而不是绝对偏移,减少映射编码的长度,特别是在处理大型文件时。
4. 通过VLQ+Base编码进一步压缩映射数据。
5. 省略不必要的字段,优化映射长度,使其更紧凑。
在源码探索部分,以uglify-js为例,它利用source-map库生成SourceMap。生成过程涉及source-map库中的SourceMapGenerator类,通过调用generator.toJSON()方法输出SourceMap。在实际应用中,通过了解这些源码,开发者可以更深入地理解Source Map的生成机制,并在需要时进行定制或优化。
最后,以JS压缩为例,通过应用上述改进措施,可以生成紧凑的SourceMap文件。在实际环境中,使用命令行工具验证生成的SourceMap文件,可以确保其正确性和一致性。
在前端开发中,合理利用Source Map可以提高调试效率,同时优化代码发布流程。通过源码探索,开发者能够更好地理解Source Map的底层机制,为项目调试和维护提供强有力的支持。
sourcemap的认知与运用
在讨论sourcemap之前,我们首先需要了解编程语言的编译过程。
编译是将编程语言转换为可执行代码的过程。然而,可执行代码和源代码是完全不同的两种格式。如果在执行过程中出现错误,很难定位源代码编写过程中出现的问题。因此,现代编译器基本上都支持一种标准的可执行代码与源代码之间的映射,以特定格式将这种映射关系持久化存储到文件中。当程序执行出现问题时,根据堆栈信息使用映射文件逆向还原到源代码,可以清晰地、语义化地查找编写过程中的错误。
在常见的C Family languages中,这种可执行代码和源码之间映射的格式已经是一种标准化的东西了,称为DWARF。尽管在每个厂商应用的平台上产生的文件格式不尽相同,但它们内里遵循的映射格式都遵循了DWARF调试标准。
回到Web前端领域讨论这个问题,类似地,我们有sourcemap这个东西。或者我们应该称它为sourcemap调试格式标准。因为目前我们对sourcemap的格式都遵循了一定的标准,但在它的用途上,却出现了千差万别。
本文中讨论sourcemap的格式组成是没有意义的,sourcemap的格式应当是一种持续发展的标准,每当标准更新,自然会有健壮的工具帮助解读。理解sourcemap的格式对它的应用不会有太明显的帮助。
按照我的理解,最早sourcemap是为了协助JavaScript代码混淆(或者压缩)而产生的。框架的作者为了缩小代码体积、避免生产环境的JavaScript代码能被轻松地阅读,使用压缩、混淆工具对源码进行了处理。如果产生了问题,有sourcemap还能轻松地还原代码。但当时的sourcemap并不是web标准之一,这为今日的web前端工程化体系埋下了隐患。
在上一节中提到,sourcemap是一种“可执行代码”和源码之间的映射关系,那么只要是产生编译的地方,就应该会有sourcemap产生。
首先想到了Babel这个工具,它是应用最广泛的JavaScript编译器,接下来我们尝试一个示例:
接着,我们安装babel,指定sourcemap参数为inline,并对其进行转换,得到:
可以看到,转换后的代码和源码产生了很大的区别,并且sourcemap以注释的形式紧跟在可执行代码尾部,它的链接指向了一个data URL。
这么看就很容易了解到,sourcemap是一个基于之前的web标准拓展的功能,它只需要以固定的注释格式跟随在一个脚本文件的末尾即可。那么不同的浏览器厂商想要兼容这个标准,只需要加以识别这个格式即可。
但上文可以看出,data URL加在注释中,任何外部人员都可以轻易获得。因此在做代码转换时,可以将sourcemap生成到一个单独的文件中,一般是文件名加.map后缀,那么对应的sourcemap的地址也就变成:
sourcemap的这种设计方法并不是独一份的。例如在C Family Language的构建交付体系中,为了调试方便,对于测试环境交付的库、可执行代码,并不会将它们的"sourcemap"从可执行代码中分离出来。它们的执行环境和大部分JavaScript引擎一样,在调试断点或者Exception时,都能自动地载入"sourcemap"并且给出对应的源码栈信息。而在生产交付时,都会在构建时将"sourcemap"从库和可执行文件中提取出来单独保存。这样既减小了交付的产物体积,还能保证源码信息不被外部人员知晓。
既然sourcemap在逐渐地成为一个标准,那么自然就有配套的标准工具对这种格式进行解析。除了Chrome devtools自带的以外,还可以使用常见的库,接下来我们使用mozilla的source-map库,对刚才的代码进行一些还原的操作。
这是断点调试常见经常会碰到的场景,比如某个exception发生在了我们刚才代码的line 9; column 2; 即console.log的位置。那么我们可以这么做:
通过载入map文件,然后查找line 9 column 2对应的源代码位置可得:
然后我们去index.ts中的3行2列,正好是ts文件中的console.log
可以看到,sourcemap不仅记录了代码行列数,还记录了源文件的位置,那么不用翻sourcemap的manual也能说明,sourcemap也像其他编译系统一样,支持多个文件、库的代码映射的集成合并。这对于大型工程的构建十分有用,大型工程往往由数以千计万计的库和代码文件组成,在这种复杂的构建中,我们往往只想要获得有限几个产物。除了可执行代码,sourcemap的统一产出也有利于我们方便地追溯生产环境的问题。
mozilla的source-map标准库不止有查找映射位置的这个作用,更能直接还原代码。我们可以直接载入一个sourcemap文件,然后查看它对应了那些源代码文件,然后根据这些文件进行逐个还原内容:
由于我们只有一个,得到的输出是
在实际开发场景中,我们碰到的sourcemap运用比上面提到的情况复杂得多,但是问题又没有办法一一枚举,所以这里讨论一些常见的例子
前面就见过两种形式:dataURL放在JavaScript文件结尾的、和独立写入文件的。他们的形式是显而易见的,并且特点也能清楚地看出来。前者内联,增大了体积,但是降低了使用的门槛。后者独立,可以隐藏保存起来,非必要时不使用。
但在webpack这种打包构建工具中,对sourcemap做了更多更详细的定义,其基本分类的依据也遵循以下几个维度:
具体的规则可以翻阅webpack对应的文档,光靠脑子记是没办法的。
对库、预编译/转换的sourcemap处理
日常开发中,使用库或者预编译的组件是很常见的事,例如:
预编译文件的处理
像这些场景对工程的构建提出了比较多的要求。这里就针对常用的webpack做一个处理的展示,还是以前面的工程为例,我们的Typescript库导出了函数a,然后我们直接引用dist/index.js(而非src/index.ts)
然后我们通过webpack打包并且导出它的sourcemap
最后我们得到了./dist/demo.dist.js.map(名字有点诡异哈)
然后我们再通过刚才写的sourcemap工具进行还原看看:
可以看到,webpack默认情况下可以识别预编译的文件它的sourcemap,并且进行合理的目录转换,保证指向正确
那么我们如果删除预编译文件的sourcemap呢?我对a函数的库重新编译了一遍,并且不导出任何sourcemap,结果对引入的a函数库的文件还原就变成了
库的处理
如果a函数是从npm库引用的呢?我尝试了下把函数a放到了库中,然后将源码引用改成了
结果虽然node_modules中带了sourcemap,但是还原后并不是原本的ts代码
这时候需要对webpack进行配置,增加source-map-loader作为预处理,且不能排除node_modules目录:
然后再进行打包编译出来的map我们对其进行还原发现:
webpack成功帮我们提取了node_modules库中的sourcemap
本文主要阐述了sourcemap的基本知识,包含了几个方面:
弄懂 SourceMap,前端开发提效 %
深入理解 Source Map,提升前端开发效率
一、Source Map 的基本概念
Source Map 是一个 JSON 描述文件,用于记录代码打包转换后的映射关系,帮助开发者在遇到线上代码问题时快速定位到原始代码位置。
二、Source Map 的作用
在复杂代码环境下,转换后的代码与原始代码不一致,使得调试变得困难。Source Map 提供了从转换后的代码到原始代码的映射关系,帮助开发者轻松定位错误位置。
三、如何生成 Source Map
主流前端工具如 UglifyJS、Grunt、Gulp、SystemJS 和 Webpack 都支持生成 Source Map。通过配置工具或插件,即可实现代码打包时自动生成 Source Map 文件。
四、使用 Source Map
生成 Source Map 后,通过浏览器开发者工具开启相关功能,即可查看到真实源代码位置,辅助调试过程。
五、Source Map 的工作原理
Source Map 通过注释或响应头指示源代码位置,打包后的文件在浏览器中解析时,根据 Source Map 文件定位原始代码。Mappings 字段定义了代码位置的映射关系。
六、Webpack 中的 Source Map
Webpack 配置 devtool 属性即可使用 Source Map。不同类型如 source-map、inline-source-map 等在开发和生产环境中提供了不同的调试体验。
七、总结
Source Map 是前端开发中不可或缺的工具,通过它,开发者可以轻松定位错误,提高代码调试效率。掌握 Source Map 的应用,能够帮助开发者更好地解决实际开发过程中的问题。
生产上的问题你不会用 sourcemap 定位吗?
生产上的问题你不会用 sourcemap 定位吗?
sourcemap 是一个以.map 为后缀的文件,它以 json 形式存储了源代码打包转换后的位置信息。它的主要作用是实现运行时代码和开发时代码都能拥有相同准确的信息提示。常见的开发时代码提示如上图所示,而运行时代码提示如上图所示,运行时代码提示的信息不够详细准确。而 sourcemap 可以在不同的处理阶段中构建出运行时代码和开发时代码的映射关系,使得运行时代码也能够提供给我们详细而准确的信息,帮助我们在生产环境中快速定位到源代码中的位置。
要快速生成 sourcemap,前端构建工具有很多,这里列举两个常用的:vite 和 webpack。在 vite 中,只需要设置 build.sourcemap 的选项配置即可。在 webpack 中,则需要设置 devtool 的选项配置,值类型包括以下类型的组合。
要使得sourcemap 发挥作用,除了生成对应的映射规则外,还需要一个解析工具负责将源代码和 sourcemap 规则真正进行映射。通常,浏览器、异常监控系统(如:sentry)和手动映射都可以完成此任务。浏览器通常会默认启用sourcemap 映射功能。在 Sentry 监控系统中,接入、异常捕获和添加 sourcemap 的流程如下:
首先,在 Sentry 监控平台上注册/登录拥有自己的账号,然后可以构建一个对应的项目,项目创建好后会生成一个 dsn,在接入 Sentry 时需要传入。其次,在项目入口文件(main.js)中初始化接入 Sentry 即可。经过以上处理,Sentry 已经可以自动获取到错误信息,但没有接入 sourcemap 的错误信息在 Sentry 中也无法进行快速定位。因此,下一步就是需要给 Sentry 上传 sourcemap 相关的文件。
在 .map 文件中有 mappings 字段,它以 Base VLQ 编码形式存储了映射到源代码行、列等信息。使用 Base VLQ 编码可以减少文件体积,因为它是一种压缩数字内容的编码方式。每个分号中的第一串英文用来表示代码的第几行、第几列的绝对位置,后面的都是相对于之前的位置做加减法。
sourcemap 的生成、解析及应用在前端开发中是非常重要的,希望本文能帮助你更好地理解及应用 sourcemap。同时,编写文章的原则是首先保证自己有收获,其次,看看各位掘友对同一个问题都会有什么更好的方案。欢迎关注同名公众号《熊的猫》,文章会同步更新!