40 lines
126 KiB
HTML
40 lines
126 KiB
HTML
<!doctype html>
|
||
<html lang="en" dir="ltr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
<meta name="generator" content="Docusaurus v2.0.0-beta.5">
|
||
<link rel="search" type="application/opensearchdescription+xml" title="HZFE - 剑指前端 Offer" href="/awesome-interview/opensearch.xml">
|
||
<link rel="preconnect" href="https://hm.baidu.com">
|
||
<script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?c7cd0fd77ac518cc6ef46461cdc9524b";var c=document.getElementsByTagName("script")[0];c.parentNode.insertBefore(e,c)}()</script><title data-react-helmet="true">谈下 webpack loader 的机制 | HZFE - 剑指前端 Offer</title><meta data-react-helmet="true" name="twitter:card" content="summary_large_image"><meta data-react-helmet="true" property="og:url" content="https://hzfe.github.io/awesome-interview/book3/engineer-webpack-loader"><meta data-react-helmet="true" name="docsearch:language" content="en"><meta data-react-helmet="true" name="docsearch:version" content="current"><meta data-react-helmet="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-react-helmet="true" property="og:title" content="谈下 webpack loader 的机制 | HZFE - 剑指前端 Offer"><meta data-react-helmet="true" name="description" content="相关问题"><meta data-react-helmet="true" property="og:description" content="相关问题"><link data-react-helmet="true" rel="shortcut icon" href="/awesome-interview/img/favicon.ico"><link data-react-helmet="true" rel="canonical" href="https://hzfe.github.io/awesome-interview/book3/engineer-webpack-loader"><link data-react-helmet="true" rel="alternate" href="https://hzfe.github.io/awesome-interview/book3/engineer-webpack-loader" hreflang="en"><link data-react-helmet="true" rel="alternate" href="https://hzfe.github.io/awesome-interview/book3/engineer-webpack-loader" hreflang="x-default"><link data-react-helmet="true" rel="preconnect" href="https://61YWQXP6JY-dsn.algolia.net" crossorigin="anonymous"><link rel="stylesheet" href="/awesome-interview/assets/css/styles.304d13b7.css">
|
||
<link rel="preload" href="/awesome-interview/assets/js/runtime~main.50b7dd91.js" as="script">
|
||
<link rel="preload" href="/awesome-interview/assets/js/main.178fbfc5.js" as="script">
|
||
</head>
|
||
<body>
|
||
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}()</script><div id="__docusaurus">
|
||
<div><a href="#" class="skipToContent_1oUP">Skip to main content</a></div><nav class="navbar navbar--fixed-top navbarHideable_2qcr"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Navigation bar toggle" class="navbar__toggle clean-btn" type="button" tabindex="0"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/awesome-interview/"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_1VuW themedImage--light_3UqQ navbar__logo"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_1VuW themedImage--dark_hz6m navbar__logo"><b class="navbar__title">剑指前端 Offer</b></a></div><div class="navbar__items navbar__items--right"><a href="https://github.com/hzfe/awesome-interview" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link"><span>GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_3J9K"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a><div class="searchBox_1Doo"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"></span></button></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div class="main-wrapper docs-wrapper docs-doc-page"><div class="docPage_31aa"><button class="clean-btn backToTopButton_35hR" type="button"><svg viewBox="0 0 24 24" width="28"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" fill="currentColor"></path></svg></button><aside class="docSidebarContainer_3Kbt"><div class="sidebar_15mo sidebarWithHideableNavbar_267A"><a tabindex="-1" class="sidebarLogo_3h0W" href="/awesome-interview/"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_1VuW themedImage--light_3UqQ"><img src="/awesome-interview/img/badge.svg" alt="HZFE" class="themedImage_1VuW themedImage--dark_hz6m"><b>剑指前端 Offer</b></a><nav class="menu thin-scrollbar menu_Bmed menuWithAnnouncementBar_2WvA"><ul class="menu__list"><li class="menu__list-item"><a class="menu__link" href="/awesome-interview/about">关于我们</a></li><li class="menu__list-item"><a class="menu__link" href="/awesome-interview/">前言</a></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">模拟题一</a></li><li class="menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">模拟题二</a></li><li class="menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#">模拟题三</a><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/browser-event-loop">浏览器:浏览器事件循环</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/browser-memory-leaks">浏览器:如何定位内存泄露</a></li><li class="menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/awesome-interview/book3/engineer-webpack-loader">工程化:谈下 webpack loader 的机制</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/frame-react-hooks">框架:React Hooks 实现原理</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/frame-diff">框架:常见框架的 Diff 算法</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/js-async">基础:JavaScript 异步编程</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/js-ts-interface-type">基础:TypeScript 中的 Interface 和 Type Alias</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/css-mobile-adaptive">样式:移动端自适应的常见手段</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/network-http-1-2">网络:HTTP2 和 HTTP1.1 的对比</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/coding-arr-to-tree">编码:将列表还原为树状结构</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/algorithm-binary-tree-k">算法:二叉搜索树的第 k 个结点</a></li><li class="menu__list-item"><a class="menu__link" tabindex="0" href="/awesome-interview/book3/topic-white-screen-optimization">综合:如何减少白屏的时间</a></li></ul></li></ul></nav><button type="button" title="Collapse sidebar" aria-label="Collapse sidebar" class="button button--secondary button--outline collapseSidebarButton_1CGd"><svg width="20" height="20" aria-hidden="true" class="collapseSidebarButtonIcon_3E-R"><g fill="#7a7a7a"><path d="M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"></path><path d="M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"></path></g></svg></button></div></aside><main class="docMainContainer_3ufF"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_3FnS"><div class="docItemContainer_33ec"><article><div class="tocCollapsible_1PrD tocMobile_3Hoh"><button type="button" class="clean-btn tocCollapsibleButton_2O1e">On this page</button></div><div class="markdown"><header><h1>谈下 webpack loader 的机制</h1></header><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3R7-" id="相关问题"></a>相关问题<a class="hash-link" href="#相关问题" title="Direct link to heading">#</a></h2><ul><li>webpack loader 是如何工作的</li><li>如何编写 webpack loader</li></ul><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3R7-" id="回答关键点"></a>回答关键点<a class="hash-link" href="#回答关键点" title="Direct link to heading">#</a></h2><p><code>转换</code> <code>生命周期</code> <code>chunk</code></p><p>webpack 本身只能处理 JavaScript 和 JSON 文件,而 loader 为 webpack 添加了处理其他类型文件的能力。loader 将其他类型的文件转换成有效的 webpack modules(如 ESmodule、CommonJS、AMD),webpack 能消费这些模块,并将其添加到依赖关系图中。</p><p>loader 本质上是一个函数,该函数对接收到的内容进行转换,返回转换后的结果。</p><p>常见的 loader 有:</p><ul><li>raw-loader:加载文件原始内容。</li><li>file-loader:将引用文件输出到目标文件夹中,在代码中通过相对路径引用输出的文件。</li><li>url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式将文件内容注入到代码中。</li><li>babel-loader:将 ES 较新的语法转换为浏览器可以兼容的语法。</li><li>style-loader:将 CSS 代码注入到 JavaScript 中,通过 DOM 操作加载 CSS。</li><li>css-loader:加载 CSS,支持模块化、压缩、文件导入等特性。</li></ul><p>使用 loader 的方式主要有两种:</p><ol><li>在 webpack.config.js 文件中配置,通过在 module.rules 中使用 test 匹配要转换的文件类型,使用 use 指定要使用的 loader。</li></ol><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> module</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\.ts$</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> use</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ts-loader"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><ol start="2"><li>内联使用</li></ol><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">Styles</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"style-loader!css-loader?modules!./styles.css"</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3R7-" id="知识点深入"></a>知识点深入<a class="hash-link" href="#知识点深入" title="Direct link to heading">#</a></h2><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3R7-" id="1-编写-webpack-loader"></a>1. 编写 webpack loader<a class="hash-link" href="#1-编写-webpack-loader" title="Direct link to heading">#</a></h3><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="11-同步-loader"></a>1.1 同步 loader<a class="hash-link" href="#11-同步-loader" title="Direct link to heading">#</a></h4><p>同步转换内容后,可以通过 return 或调用 this.callback 返回结果。</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">loader</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">content</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> map</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> meta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">someSyncOperation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>通过 this.callback 可以返回除内容以外的其他信息(如 sourcemap)。</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">loader</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">content</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> map</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> meta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">someSyncOperation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> map</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> meta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 当调用 callback() 时,始终返回 undefined</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="12-异步-loader"></a>1.2 异步 loader<a class="hash-link" href="#12-异步-loader" title="Direct link to heading">#</a></h4><p>通过 this.async 可以获取异步操作的回调函数,并在回调函数中返回结果。</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">content</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> map</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> meta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> callback </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">async</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">someAsyncOperation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">content</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">err</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> result</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> sourceMaps</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> meta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=></span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> sourceMaps</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> meta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>除非计算很小,否则对于 Node.js 这种单线程环境,尽可能使用异步 loader。</p><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="13-loader-开发辅助工具及-loadercontext"></a>1.3 loader 开发辅助工具及 loaderContext<a class="hash-link" href="#13-loader-开发辅助工具及-loadercontext" title="Direct link to heading">#</a></h4><p><code>loader-utils</code> 与 <code>schema-utils</code>,可以使获取及验证传递给 loader 的参数的工作简单化。</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> getOptions </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"loader-utils"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> validate </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"schema-utils"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> schema </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"object"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> properties</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"string"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">source</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> options </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getOptions</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">validate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">schema</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Example Loader"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> baseDataPath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"options"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// Apply some transformations to the source...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">export default </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation constant" style="color:#36acaa">JSON</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation function" style="color:#d73a49">stringify</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation">source</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>loader-utils 主要有以下工具方法:</p><ul><li><code>parseQuery</code>:解析 loader 的 query 参数,返回一个对象。</li><li><code>stringifyRequest</code>:将请求的资源转换为可以在 loader 生成的代码中 require 或 import 使用的相对路径字符串,同时避免绝对路径导致重新计算 hash 值。<div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token plain">loaderUtils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringifyRequest</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./test.js"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// "\"./test.js\""</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div></li><li><code>urlToRequest</code>:将请求的资源路径转换成 webpack 可以处理的形式。<div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> url </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"~path/to/module.js"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> request </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> loaderUtils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">urlToRequest</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">url</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// "path/to/module.js"</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div></li><li><code>interpolateName</code>:对文件名模板进行插值。<div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// loaderContext.resourcePath = "/absolute/path/to/app/js/hzfe.js"</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain">loaderUtils</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">interpolateName</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">loaderContext</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"js/[hash].script.[ext]"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> content</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token spread operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// => js/9473fdd0d880a43c21b7778d34872157.script.js</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div></li><li><code>getHashDigest</code>:获取文件内容的 hash 值。</li></ul><p>在编写 loader 的过程中,还可以利用 loaderContext 对象来获取 loader 的相关信息和进行一些高级的操作,常见的属性和方法有:</p><ul><li><code>this.addDependency</code>:加入一个文件,作为 loader 产生的结果的依赖,使其在有任何变化时可以被监听到,从而触发重新编译。</li><li><code>this.async</code>:告诉 loader-runner 这个 loader 将会异步的执行回调。</li><li><code>this.cacheable</code>:默认情况下,将 loader 的处理结果标记为可缓存。传入 false 可以关闭 loader 处理结果的缓存能力。</li><li><code>this.fs</code>:用于访问 compilation 的 inputFileSystem 属性。</li><li><code>this.getOptions</code>:提取 loader 的配置选项。从 webpack 5 开始,可以获取到 loader 上下文对象,用于替代 <code>loader-utils</code> 中的 getOptions 方法。</li><li><code>this.mode</code>: webpack 的运行模式,可以是 "development" 或 "production"。</li><li><code>this.query</code>:如果 loader 配置了 options 对象,则指向这个对象。如果 loader 没有 options,而是以 query 字符串作为参数,query 则是一个以 <code>?</code> 开头的字符串。</li></ul><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3R7-" id="2-webpack-loader-工作机制"></a>2. webpack loader 工作机制<a class="hash-link" href="#2-webpack-loader-工作机制" title="Direct link to heading">#</a></h3><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="21-根据-modulerules-解析-loader-加载规则"></a>2.1 根据 module.rules 解析 loader 加载规则<a class="hash-link" href="#21-根据-modulerules-解析-loader-加载规则" title="Direct link to heading">#</a></h4><p>当 webpack 处理一个模块(module)时,会根据配置文件中 <code>module.rules</code> 的规则,使用 loader 处理对应资源,得到可供 webpack 使用的 JavaScript 模块。</p><p>根据具体的配置情况,loader 会有不同的类型,可以影响 loader 的执行顺序。具体类型如下所示:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token plain">rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// pre 前置 loader</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> enforce</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pre"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\.js$</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"eslint-loader"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// normal loader</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\.js$</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"babel-loader"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// post 后置 loader</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> enforce</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"post"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\.js$</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loader</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"eslint-loader"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>以及内联使用的 inline loader:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"style-loader!css-loader!sass-loader!./hzfe.scss"</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>在正常的执行流程中,这些不同类型的 loader 的执行顺序是:<code>pre -> normal -> inline -> post</code>。在下一节将会提到的 pitch 流程中,这些 loader 的执行顺序是反过来的:<code>post -> inline -> normal -> pre</code>。</p><p>对于内联 loader,可以通知修饰前缀改变 loader 的执行顺序:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// ! 前缀会禁用 normal loader</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">HZFE</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"!./hzfe.js"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// -! 前缀会禁用 pre loader 和 normal loader</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">HZFE</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"-!./hzfe.js"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// !! 前缀会禁用 pre、normal 和 post loader</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">HZFE</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"!!./hzfe.js"</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>一般情况下,<code>!</code> 前缀和 inline loader 一起使用仅出现在 loader(如 style-loader)生成的代码中,webpack 官方不建议用户同时使用 inline loader 和 <code>!</code> 前缀。</p><p>webpack rules 中配置的 loader 可以是多个链式串联的。在正常流程中,链式 loader 会按照从后往前的顺序执行。</p><ul><li>最后的 loader 最先执行,它接收的是资源文件(resource file)的内容。</li><li>第一个 loader 最后执行,它将返回 JavaScript 模块和可选的 source map。</li><li>位于中间的 loader,对接收和返回没有特定要求,只要能处理之前 loader 返回的内容,产出下一个 loader 能够理解的内容就可以。</li></ul><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="22-loader-runner-的执行流程"></a>2.2 loader-runner 的执行流程<a class="hash-link" href="#22-loader-runner-的执行流程" title="Direct link to heading">#</a></h4><p>webpack 调用 loader 的时机在触发 compilation 的 buildModule 钩子之后。webpack 会在 <code>NormalModule.js</code> 中,调用 runLoaders 运行 loader:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">runLoaders</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> resource</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">resource</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 资源文件的路径,可以有查询字符串。如:'./test.txt?query'</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> loaders</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">loaders</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// loader 的路径。</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> context</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> loaderContext</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 传递给 loader 的上下文</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">processResource</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">loaderContext</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> resourcePath</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=></span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 获取资源的方式,有 scheme 的文件通过 readResourceForScheme 读取,否则通过 fs.readFile 读取。</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> resource </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> loaderContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">resource</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> scheme </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getScheme</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">resource</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">scheme</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> hooks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">readResourceForScheme</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token keyword control-flow" style="color:#00009f">for</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">scheme</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">callAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">resource</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">err</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=></span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">else</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> loaderContext</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addDependency</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">resourcePath</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> fs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">readFile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">resourcePath</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> callback</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">err</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=></span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 当 loader 转换完成后,会将结果返回到 webpack 中继续处理。</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">processResult</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">result</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>runLoaders 函数来自 <code>loader-runner</code> 包。在介绍 runLoaders 的具体流程之前,先介绍一下 pitch 阶段,上一节中所讲的这种从后往前执行 loader 的流程,一般叫做 normal 阶段。与之相对的,还有一种叫做 pitch 阶段的流程。</p><p>一个 loader 如果在导出的函数的 pitch 属性上挂在了方法,那这个方法将在 pitch 阶段执行。pitch 阶段不同于 normal 阶段,pitch 阶段的执行顺序是从前往后的,整个流程类似浏览器事件模型或洋葱模型,pitch 阶段先从前往后执行 loader,然后再进入 normal 阶段从后往前执行 loader。注意,pitch 阶段一般不返回值,一旦 pitch 阶段有 loader 返回值,则从这里开始进入从后往前执行的 normal 阶段。</p><p>loader-runner 的具体流程如下:</p><ol><li><p>处理从 webpack 接收的 context,继续添加必要的属性和辅助方法。</p></li><li><p>iteratePitchingLoaders 处理 pitch loader。</p><p>如果我们给一个 module 配置了三个 loader,每个 loader 都配置了 pitch 函数:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">//...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> module</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">//...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> use</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"a-loader"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"b-loader"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"c-loader"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>那么处理这个 module 的流程如下:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly shell"><pre tabindex="0" class="prism-code language-shell codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">|</span><span class="token plain">- a-loader </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">pitch</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- b-loader </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">pitch</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- c-loader </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">pitch</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- requested module is picked up as a dependency</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- c-loader normal execution</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- b-loader normal execution</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- a-loader normal execution</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>如果 b-loader 在 pitch 中提前返回了值,那么流程如下:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly shell"><pre tabindex="0" class="prism-code language-shell codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">|</span><span class="token plain">- a-loader </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">pitch</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- b-loader </span><span class="token variable" style="color:#36acaa">`</span><span class="token variable" style="color:#36acaa">pitch</span><span class="token variable" style="color:#36acaa">`</span><span class="token plain"> returns a module</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">|</span><span class="token plain">- a-loader normal execution</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div></li><li><p>iterateNormalLoaders 处理 normal loader。</p><p>当 pitch loader 的流程处理完后,就来到了处理 normal loader 的流程。处理 normal loader 的流程和 pitch loader 相似,只是从后往前迭代。</p><p>iterateNormalLoaders 和 iteratePitchingLoaders 都会调用 runSyncOrAsync 来执行 loader。runSyncOrAsync 会提供 context.async,这是一个返回 callback 的 async 函数,用于异步处理。</p></li></ol><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3R7-" id="3-常见-webpack-loader-原理解析"></a>3. 常见 webpack loader 原理解析<a class="hash-link" href="#3-常见-webpack-loader-原理解析" title="Direct link to heading">#</a></h3><p>loader 本身的操作并不复杂,就是一个负责转换其他资源到 JavaScript 模块的函数。</p><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="31-raw-loader-分析"></a>3.1 raw-loader 分析<a class="hash-link" href="#31-raw-loader-分析" title="Direct link to heading">#</a></h4><p>该 loader 是功能非常简单的同步 loader,它的核心步骤是从文件原始内容中取得序列化的字符串,修复 JSON 序列化特殊字符时的 bug,添加导出语句,使其成为 JavaScript 模块。</p><p>该 loader 在 webpack 5 中已废弃,直接使用 asset modules 的功能代替即可。该 loader 源码如下:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> getOptions </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"loader-utils"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> validate </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"schema-utils"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">schema</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./options.json"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">rawLoader</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">source</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> options </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getOptions</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token function" style="color:#d73a49">validate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">schema</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Raw Loader"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> baseDataPath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"options"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> json </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token known-class-name class-name">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">source</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\u2028</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-flags" style="color:#36acaa">g</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\\u2028"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">\u2029</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-flags" style="color:#36acaa">g</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\\u2029"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> esModule </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">typeof</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">esModule</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">!==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"undefined"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">esModule</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">esModule </span><span class="token template-string interpolation operator" style="color:#393A34">?</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation string" style="color:#e3116c">"export default"</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation operator" style="color:#393A34">:</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation string" style="color:#e3116c">"module.exports ="</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c"> </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">json</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">;</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="32-babel-loader-分析"></a>3.2 babel-loader 分析<a class="hash-link" href="#32-babel-loader-分析" title="Direct link to heading">#</a></h4><p>babel loader 是一个综合了同步和异步的 loader,在使用缓存配置时以异步模式运行,否则以同步方式运行。该 loader 的主要源码如下:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// imports ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">transpile</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">source</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> babel</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">transform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">source</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">error</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> code</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> map</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> map</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> metadata</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> metadata</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">source</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> inputSourceMap</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// ...</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cacheDirectory</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> callback </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">async</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cache</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> directory</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> cacheDirectory</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> identifier</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> cacheIdentifier</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> source</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> source</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> options</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> transform</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> transpile</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">err</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter"> code</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> map</span><span class="token parameter punctuation" style="color:#393A34">,</span><span class="token parameter"> metadata </span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token parameter"> </span><span class="token parameter operator" style="color:#393A34">=</span><span class="token parameter"> </span><span class="token parameter punctuation" style="color:#393A34">{</span><span class="token parameter punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=></span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">err</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> metadataSubscribers</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">s</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=></span><span class="token plain"> </span><span class="token function" style="color:#d73a49">passMetadata</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> metadata</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> map</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> map</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> metadata </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">transpile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">source</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block">
|
||
</span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword null nil" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> code</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> map</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>babel-loader 通过 callback 传递了经过 babel.transform 转换后的代码及 source map。</p><h4><a aria-hidden="true" tabindex="-1" class="anchor anchor__h4 anchorWithHideOnScrollNavbar_3R7-" id="33-style-loader-与-css-loader-分析"></a>3.3 style-loader 与 css-loader 分析<a class="hash-link" href="#33-style-loader-与-css-loader-分析" title="Direct link to heading">#</a></h4><p>style-loader 负责将样式插入到 DOM 中,使样式对页面生效。css-loader 主要负责处理 import、url 路径等外部引用。</p><p>style-loader 只有 pitch 函数。css-loader 是 normal module。整个执行流程是先执行 style-loader 阶段,style-loader 会创建形如 <code>require(!!./hzfe.css)</code> 的代码返回给 webpack。webpack 会再次调用 css-loader 处理样式,css-loader 会返回包含 runtime 的 js 模块给 webpack 去解析。style-loader 在上一步注入 <code>require(!!./hzfe.css)</code> 的同时,也注入了添加 style 标签的代码。这样,在运行时(浏览器中),style-loader 就可以把 css-loader 的样式插入到页面中。</p><p>常见的疑问就是为什么不按照 normal 模式组织 style-loader 和 css-loader。</p><p>首先 css-loader 返回的是形如这样的代码:</p><div class="codeBlockContainer_K1bP"><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports">___CSS_LOADER_API_IMPORT___</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"../node_modules/_css-loader@5.1.3@css-loader/dist/runtime/api.js"</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> ___CSS_LOADER_EXPORT___ </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">___CSS_LOADER_API_IMPORT___</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">i</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> i</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Module</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain">___CSS_LOADER_EXPORT___</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">push</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token string" style="color:#e3116c">".hzfe{\r\n height: 100px;\r\n}"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Exports</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword module" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">default</span><span class="token plain"> ___CSS_LOADER_EXPORT___</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>style-loader 无法在编译时获取 CSS 相关的内容,因为 style-loader 无法处理 css-loader 生成结果的 runtime 依赖。style-loader 也无法在运行时获取 CSS 相关的内容,因为无论怎样拼接运行时代码,都无法获取到 CSS 的内容。</p><p>作为替代,style-loader 采用了 pitch 方案,style-loader 的核心功能如下所示:</p><div class="codeBlockContainer_K1bP"><div style="color:#393A34;background-color:#f6f8fa" class="codeBlockTitle_eoMF">style-loader</div><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">pitch</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 生成 require CSS 文件的语句,交给 css-loader 解析 得到包含 CSS 内容的 JS 模块</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 其中 !! 是为了避免 webpack 解析时递归调用 style-loader</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">var content=require("</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">loaderUtils</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation function" style="color:#d73a49">stringifyRequest</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation keyword" style="color:#00009f">this</span><span class="token template-string interpolation punctuation" style="color:#393A34">,</span><span class="token template-string interpolation"> `</span><span class="token template-string interpolation operator" style="color:#393A34">!</span><span class="token template-string interpolation operator" style="color:#393A34">!</span><span class="token template-string interpolation">$</span><span class="token template-string interpolation punctuation" style="color:#393A34">{</span><span class="token template-string interpolation">request</span><span class="token template-string interpolation punctuation" style="color:#393A34">}</span><span class="token template-string interpolation">`</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">")</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 在运行时调用 addStyle 把 CSS 内容插入到 DOM 中</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">require("</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token template-string interpolation">loaderUtils</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation function" style="color:#d73a49">stringifyRequest</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation keyword" style="color:#00009f">this</span><span class="token template-string interpolation punctuation" style="color:#393A34">,</span><span class="token template-string interpolation"> `</span><span class="token template-string interpolation operator" style="color:#393A34">!</span><span class="token template-string interpolation">$</span><span class="token template-string interpolation punctuation" style="color:#393A34">{</span><span class="token template-string interpolation">path</span><span class="token template-string interpolation punctuation" style="color:#393A34">.</span><span class="token template-string interpolation function" style="color:#d73a49">join</span><span class="token template-string interpolation punctuation" style="color:#393A34">(</span><span class="token template-string interpolation">__dirname</span><span class="token template-string interpolation punctuation" style="color:#393A34">,</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation string" style="color:#e3116c">"add-style.js"</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation punctuation" style="color:#393A34">}</span><span class="token template-string interpolation">`</span><span class="token template-string interpolation punctuation" style="color:#393A34">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token template-string string" style="color:#e3116c">")(content)</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 如果发现启用了 CSS modules,则默认导出它</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token string" style="color:#e3116c">"if(content.locals) module.exports = content.locals"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">";"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><div class="codeBlockContainer_K1bP"><div style="color:#393A34;background-color:#f6f8fa" class="codeBlockTitle_eoMF">add-style.js</div><div class="codeBlockContent_hGly js"><pre tabindex="0" class="prism-code language-js codeBlock_23N8 thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_39YC"><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">content</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> style </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">createElement</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"style"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> style</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">innerHTML</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> content</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token dom variable" style="color:#36acaa">document</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">head</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">appendChild</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">style</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_Ue-o clean-btn">Copy</button></div></div><p>在 pitch 阶段,style-loader 生成 require CSS 以及注入 runtime 的代码。该结果会返回给 webpack 进一步解析,css-loader 返回的结果会作为模块在运行时导入,在运行时能够获得 CSS 的内容,然后调用 add-style.js 把 CSS 内容插入到 DOM 中。</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3R7-" id="参考资料"></a>参考资料<a class="hash-link" href="#参考资料" title="Direct link to heading">#</a></h2><ol><li><a href="https://webpack.js.org/contribute/writing-a-loader/" target="_blank" rel="noopener noreferrer">writting a loader</a></li><li><a href="https://webpack.js.org/api/loaders/" target="_blank" rel="noopener noreferrer">Loader Interface</a></li><li><a href="https://github.com/webpack/loader-runner" target="_blank" rel="noopener noreferrer">loader runner</a></li></ol></div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages navigation"><div class="pagination-nav__item"><a class="pagination-nav__link" href="/awesome-interview/book3/browser-memory-leaks"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">« 浏览器:如何定位内存泄露</div></a></div><div class="pagination-nav__item pagination-nav__item--next"><a class="pagination-nav__link" href="/awesome-interview/book3/frame-react-hooks"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">框架:React Hooks 实现原理 »</div></a></div></nav></div></div><div class="col col--3"><div class="tableOfContents_35-E thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#相关问题" class="table-of-contents__link">相关问题</a></li><li><a href="#回答关键点" class="table-of-contents__link">回答关键点</a></li><li><a href="#知识点深入" class="table-of-contents__link">知识点深入</a><ul><li><a href="#1-编写-webpack-loader" class="table-of-contents__link">1. 编写 webpack loader</a></li><li><a href="#2-webpack-loader-工作机制" class="table-of-contents__link">2. webpack loader 工作机制</a></li><li><a href="#3-常见-webpack-loader-原理解析" class="table-of-contents__link">3. 常见 webpack loader 原理解析</a></li></ul></li><li><a href="#参考资料" class="table-of-contents__link">参考资料</a></li></ul></div></div></div><div class="row"><div class="col"><div class="react-utterences"><div>Loading script...</div></div></div><div class="col col--3"></div></div></div></main></div></div></div>
|
||
<script src="/awesome-interview/assets/js/runtime~main.50b7dd91.js"></script>
|
||
<script src="/awesome-interview/assets/js/main.178fbfc5.js"></script>
|
||
</body>
|
||
</html> |