<p>今天的主线任务是,稍微了解下vue-loader的sourcemap</p> <p>这里简单记录下打怪经历</p> <h3 id="articleHeader0">故事背景</h3> <p>大祭司布鲁梅尔,跟玩家说在杰罗瓦镇的西北方有一个迷宫,里面有个被关闭了300年的魔物,我们需要把这个魔物干掉,正好以此来测试下玩家是否具备"开启者"的资格,也就是战斗系转职资格。</p> <p>于是作为勇士的我来到了杰村,获取钥匙<br /><span class="img-wrap"><img data-src="/img/bVHry1?w=640&amp;h=480" src="/img/bVHry1?w=640&amp;h=480" alt="图片描述" title="图片描述" style="cursor: pointer; display: inline;"></span></p> <p><span class="img-wrap"><img data-src="/img/bVHry6?w=640&amp;h=480" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="图片描述" title="图片描述" style="cursor: pointer;"></span></p> <p>任务之前,先说一下,有不少法兰城的勇士,并不喜欢它,有人说它违反了webpack自定义loader的精神,不支持inline<a href="http://www.js-code.com/tag/template" title="Template" target="_blank">Template</a>(<code>具体这个inline<a href="http://www.js-code.com/tag/template" title="Template" target="_blank">Template</a>是什么鬼?</code>)...</p> <p>引自<a href="https://webpack.github.io/docs/how-to-write-a-loader.html" rel="nofollow noreferrer" target="_blank">https://webpack.github.io/doc...</a></p> <blockquote> <h4>do only a single task</h4> <p>Loaders can be chained. Create loaders for every step, instead of a loader that does everything at once.</p> <p>This also means they should not convert to JavaScript if not necessary.</p> <p>Example: Render <a href="http://www.js-code.com/tag/html" title="HTML" target="_blank">HTML</a> from a template file by applying the query parameters<br />I could write a loader that compiles the template from source, execute it and return a module that <a href="http://www.js-code.com/tag/export" title="export" target="_blank">export</a>s a string containing the <a href="http://www.js-code.com/tag/html" title="HTML" target="_blank">HTML</a> code. This is bad.</p> </blockquote> <p>作为萌新的我,并不知道vue-loader发生了什么了,只能说一脸蒙逼</p> <p>但是我内心其实觉得vue-loader作为一个vue配套的工具,还是挺不错的</p> <p>怀揣这样的心情,就来到了门口<br /><span class="img-wrap"><img data-src="/img/bVHrzb?w=640&amp;h=480" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="图片描述" title="图片描述" style="cursor: pointer;"></span></p> <h3 id="articleHeader1">二话不说,正式开干</h3> <p>怎么说呢,还是先了解下vue-loader的作用,它可以transform以vue结尾的文件,从而达到一些目的,比如单文件component, hot reload, scope css...</p> <h4>项目结构</h4> <p><span class="img-wrap"><img data-src="/img/bVHpAd?w=184&amp;h=433" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="clipboard.png" title="clipboard.png" style="cursor: pointer;"></span></p> <p>这里要了解的sourcemap,都在parser.js文件中</p> <h4>source code分析</h4> <p>引入的外部模块</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="var compiler = require('vue-template-compiler') var cache = require('lru-cache')(100) var hash = require('hash-sum') var SourceMapGenerator = require('source-map').SourceMapGenerator" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-keyword">var</span> compiler = <span class="hljs-built_in">require</span>(<span class="hljs-string">'vue-template-compiler'</span>) <span class="hljs-keyword">var</span> cache = <span class="hljs-built_in">require</span>(<span class="hljs-string">'lru-cache'</span>)(<span class="hljs-number">100</span>) <span class="hljs-keyword">var</span> hash = <span class="hljs-built_in">require</span>(<span class="hljs-string">'hash-sum'</span>) <span class="hljs-keyword">var</span> Source<a href="http://www.js-code.com/tag/map" title="浏览关于“Map”的文章" target="_blank" class="tag_link">Map</a><a href="http://www.js-code.com/tag/generator" title="浏览关于“Generator”的文章" target="_blank" class="tag_link">Generator</a> = <span class="hljs-built_in">require</span>(<span class="hljs-string">'source-map'</span>).SourceMapGenerator</code></pre> <ul> <li> <p>lru-cache,lru缓存,vue1.0内部也用的</p> </li> <li> <p>hash-sum,其简介为Blazing fast unique hash generator,用于生成hash</p> </li> <li> <p>soruce-map,一个我们生成sourcemap主要的依赖库,这里用的其属性为Source<a href="http://www.js-code.com/tag/map" title="Map" target="_blank">Map</a><a href="http://www.js-code.com/tag/generator" title="Generator" target="_blank">Generator</a>的类</p> </li> <li> <p>vue-template-compiler,模块发现是vue内部的代码,为src/entries/web-compiler.js文件,这个模块是由vue的build/build.js,使用rollup生成的packager,哦? 少年好像又学到了什么。具体vue-template-compiler搞了什么,<code>暂时不管</code>,先标个红</p> </li> </ul> <p><code>generateSource<a href="http://www.js-code.com/tag/map" title="Map" target="_blank">Map</a>函数</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="function generateSourceMap (filename, source, generated) { var map = new SourceMapGenerator() map.setSourceContent(filename, source) generated.split(splitRE).forEach((line, index) => {<br /> if (!emptyRE.test(line)) {<br /> map.addMapping({<br /> source: filename,<br /> original: {<br /> line: index + 1,<br /> column: 0<br /> },<br /> generated: {<br /> line: index + 1,<br /> column: 0<br /> }<br /> })<br /> }<br /> })<br /> return map.toJSON()<br /> }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateSourceMap</span> (<span class="hljs-params">filename, source, generated</span>) </span>{ <span class="hljs-keyword">var</span> map = <span class="hljs-keyword">new</span> SourceMapGenerator() map.setSourceContent(filename, source) generated.split(splitRE).forEach(<span class="hljs-function">(<span class="hljs-params">line, index</span>) =&gt;</span> { <span class="hljs-keyword">if</span> (!emptyRE.test(line)) { map.addMapping({ <span class="hljs-attr">source</span>: filename, <span class="hljs-attr">original</span>: { <span class="hljs-attr">line</span>: index + <span class="hljs-number">1</span>, <span class="hljs-attr">column</span>: <span class="hljs-number">0</span> }, <span class="hljs-attr">generated</span>: { <span class="hljs-attr">line</span>: index + <span class="hljs-number">1</span>, <span class="hljs-attr">column</span>: <span class="hljs-number">0</span> } }) } }) <span class="hljs-keyword">return</span> map.toJSON() }</code></pre> <p>判断空行的目的是,这样的话,那些空白行就不会生成map,减少map体积</p> <p>chrome调试的时候,那些空白行也是灰的,没必要打断点嘛</p> <p>然后,那个<code>换行正则</code>也是很有意思,外服的玩家讨论了一番,<a href="https://github.com/webpack/webpack/issues/318" rel="nofollow noreferrer" target="_blank">https://github.com/webpack/we...</a> <br />结论就是g这样的flag,js容易出问题的,比如你可以在控制台里感受下:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="var r = /n/g; console.log(r.test(&quot;n&quot;)); // => true<br /> console.log(r.test(&quot;n&quot;)); // => false" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-keyword">var</span> r = <span class="hljs-regexp">/n/g</span>; <span class="hljs-built_in">console</span>.log(r.test(<span class="hljs-string">"n"</span>)); <span class="hljs-comment">// =&gt; true</span> <span class="hljs-built_in">console</span>.log(r.test(<span class="hljs-string">"n"</span>)); <span class="hljs-comment">// =&gt; false</span></code></pre> <p>解决是,要吗将<code>正则的属性lastIndex = 0</code>,要吗将g去掉</p> <p><code>导出函数</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="module.exports = function (content, filename, needMap) { var cacheKey = hash(filename + content) // source-map cache busting for hot-reloadded modules var filenameWithHash = filename + '?' + cacheKey var output = cache.get(cacheKey) if (output) return output output = compiler.parseComponent(content, { pad: true }) if (needMap) { if (output.script &amp;&amp; !output.script.src) { output.script.map = generateSourceMap( filenameWithHash, content, output.script.content ) } if (output.styles) { output.styles.forEach(style => {<br /> if (!style.src) {<br /> style.map = generateSourceMap(<br /> filenameWithHash,<br /> content,<br /> style.content<br /> )<br /> }<br /> })<br /> }<br /> }<br /> cache.set(cacheKey, output)<br /> return output<br /> }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-built_in">module</span>.<a href="http://www.js-code.com/tag/export" title="浏览关于“export”的文章" target="_blank" class="tag_link">export</a>s = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">content, filename, needMap</span>) </span>{ <span class="hljs-keyword">var</span> cacheKey = hash(filename + content) <span class="hljs-comment">// source-map cache busting for hot-reloadded modules</span> <span class="hljs-keyword">var</span> filenameWithHash = filename + <span class="hljs-string">'?'</span> + cacheKey <span class="hljs-keyword">var</span> output = cache.get(cacheKey) <span class="hljs-keyword">if</span> (output) <span class="hljs-keyword">return</span> output output = compiler.parseComponent(content, { <span class="hljs-attr">pad</span>: <span class="hljs-literal">true</span> }) <span class="hljs-keyword">if</span> (needMap) { <span class="hljs-keyword">if</span> (output.script &amp;&amp; !output.script.src) { output.script.map = generateSourceMap( filenameWithHash, content, output.script.content ) } <span class="hljs-keyword">if</span> (output.styles) { output.styles.forEach(<span class="hljs-function"><span class="hljs-params">style</span> =&gt;</span> { <span class="hljs-keyword">if</span> (!style.src) { style.map = generateSourceMap( filenameWithHash, content, style.content ) } }) } } cache.set(cacheKey, output) <span class="hljs-keyword">return</span> output }</code></pre> <p>任务途中,突遇掉线,果然很魔力</p> <p>掉线回城,东门医院补血,继续任务</p> <p>这里举个例子,然后理解下<br /><code>index.html</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text=" <div id=&quot;app&quot;></div> <p>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="xml hljs"><code class="html" style="word-break: break-word; white-space: initial;"><span class="hljs-tag">&lt;<span class="hljs-name"><a href="http://www.js-code.com/tag/div" title="浏览关于“div”的文章" target="_blank" class="tag_link">div</a></span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></code></pre> <p><code>src/index.js</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="const Vue = require('vue') const Hello = require('./Hello.vue') new Vue({ el: '#app', components: { Hello: Hello }, render: function(h) { return h( 'Hello' ) } })" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="javascript"><span class="hljs-keyword"><a href="http://www.js-code.com/tag/const" title="浏览关于“const”的文章" target="_blank" class="tag_link">const</a></span> <a href="http://www.js-code.com/tag/vue" title="浏览关于“Vue”的文章" target="_blank" class="tag_link">Vue</a> = <span class="hljs-built_in">require</span>(<span class="hljs-string">'vue'</span>) <span class="hljs-keyword">const</span> Hello = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./Hello.vue'</span>) <span class="hljs-keyword">new</span> Vue({ <span class="hljs-attr">el</span>: <span class="hljs-string">'#app'</span>, <span class="hljs-attr">components</span>: { <span class="hljs-attr">Hello</span>: Hello }, <span class="hljs-attr">render</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">h</span>) </span>{ <span class="hljs-keyword">return</span> h( <span class="hljs-string">'Hello'</span> ) } })</code></pre> <p><code>src/Hello.vue</code></p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="<template></p> <div class=&quot;hello&quot;> <span>hello</span> </div> <p></template></p> <p><script> module.<a href="http://www.js-code.com/tag/export" title="export" target="_blank">export</a>s = { created() { console.log('hello .vue file') } } </script></p> <style scoped> .hello { font-size: 12px; } </style> <p>" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs xml"><code><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"hello"</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>hello<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span> <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript"> <span class="hljs-built_in">module</span>.exports = { created() { <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'hello .vue file'</span>) } } </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span> <span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">scoped</span>&gt;</span><span class="css"> <span class="hljs-selector-class">.hello</span> { <span class="hljs-attribute">font-size</span>: <span class="hljs-number">12px</span>; } </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></code></pre> <p>然后以此例来分析下,</p> <p>3个参数</p> <ul> <li> <p>content: Hello.vue中的文件内容</p> </li> <li> <p>filename: Hello.vue</p> </li> <li> <p>needMap: webpack配置中如果dev-tools使用了'source-map'... 则为true</p> </li> </ul> <p>调用compiler.parseComponent(content, { pad: true }),会解析成下面的格式,<br />稍微注意style是一个<a href="http://www.js-code.com/tag/%e6%95%b0%e7%bb%84" title="数组" target="_blank">数组</a>的形式,即可以有多个style标签</p> <p>然后这些标签其实可以有src属性,表示引入外部对应的文件,比如这里判断了!output.script.src</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="{ template: { type: 'template', content: 'n <div class=&quot;hello&quot;>n <span>hello</span>n</div> <p>n',<br /> start: 10,<br /> end: 65<br /> },<br /> script: {<br /> type: 'script',<br /> content: '//n//n//n//n//n//nnmodule.exports = {n created() {n console.log('hello .vue file')n }n}n',<br /> start: 86,<br /> end: 161<br /> },<br /> styles: [<br /> {<br /> type: 'style',<br /> content: 'nnnnnnnnnnnnnnn.hello {n font-size: 12px;n}n',<br /> start: 186,<br /> scoped: true,<br /> end: 217<br /> }<br /> ],<br /> customBlocks: []<br /> }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="hljs properties"><code><span class="hljs-meta">{</span> <span class="hljs-string"></span> <span class="hljs-attr">template</span>: <span class="hljs-string">{ </span> <span class="hljs-attr">type</span>: <span class="hljs-string">'template',</span> <span class="hljs-attr">content</span>: <span class="hljs-string">'n&lt;div class="hello"&gt;n &lt;span&gt;hello&lt;/span&gt;n&lt;/div&gt;n',</span> <span class="hljs-attr">start</span>: <span class="hljs-string">10,</span> <span class="hljs-attr">end</span>: <span class="hljs-string">65 </span> <span class="hljs-attr">},</span> <span class="hljs-attr">script</span>: <span class="hljs-string">{</span> <span class="hljs-attr">type</span>: <span class="hljs-string">'script',</span> <span class="hljs-attr">content</span>: <span class="hljs-string">'//n//n//n//n//n//nnmodule.exports = {n created() {n console.log('hello .vue file')n }n}n',</span> <span class="hljs-attr">start</span>: <span class="hljs-string">86,</span> <span class="hljs-attr">end</span>: <span class="hljs-string">161 </span> <span class="hljs-attr">},</span> <span class="hljs-attr">styles</span>: <span class="hljs-string">[</span> <span class="hljs-meta">{</span> <span class="hljs-string"></span> <span class="hljs-attr">type</span>: <span class="hljs-string">'style',</span> <span class="hljs-attr">content</span>: <span class="hljs-string">'nnnnnnnnnnnnnnn.hello {n font-size: 12px;n}n',</span> <span class="hljs-attr">start</span>: <span class="hljs-string">186,</span> <span class="hljs-attr">scoped</span>: <span class="hljs-string">true,</span> <span class="hljs-attr">end</span>: <span class="hljs-string">217 </span> <span class="hljs-attr">}</span> <span class="hljs-attr">],</span> <span class="hljs-attr">customBlocks</span>: <span class="hljs-string">[] </span> <span class="hljs-attr">}</span></code></pre> <h3 id="articleHeader2">战斗结束</h3> <p><span class="img-wrap"><img data-src="/img/bVHrzs?w=640&amp;h=480" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="图片描述" title="图片描述" style="cursor: pointer;"></span><br /><span class="img-wrap"><img data-src="/img/bVHrzv?w=640&amp;h=480" src="https://static.segmentfault.com/v-5cc2cd8e/global/img/squares.svg" alt="图片描述" title="图片描述" style="cursor: pointer;"></span></p> <p>ok,没怎么费血瓶,顺利获取重要道具:大地翼龙的鳞片1个,下一层继续出发</p>

本文固定链接: http://www.js-code.com/vue-js/vue-js_16842.html