<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Webpack - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Webpack - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 19:38:02 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/webpack/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 实现一个 webpack loader 和 webpack plugin ]]>
                </title>
                <description>
                    <![CDATA[ loader > 官网上的定义：loader 是一个转换器，用于对源代码进行转换。 例如 babel-loader 可以将 ES6 代码转换为 ES5 代码；sass-loader 将 sass 代码转换为 css 代码。 一般 loader 的配置代码如下： module: {         rules: [             {       ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/code-a-webpack-loader-and-a-webpack-plugin/</link>
                <guid isPermaLink="false">612c5ca60cd9ee0623e15aa3</guid>
                
                    <category>
                        <![CDATA[ Webpack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ woai3c ]]>
                </dc:creator>
                <pubDate>Tue, 24 Aug 2021 02:00:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/08/1_5d_eDFKTmlTdYafG9dahdw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="loader">loader</h2><blockquote>官网上的定义：loader 是一个转换器，用于对源代码进行转换。</blockquote><p>例如 <code>babel-loader</code> 可以将 ES6 代码转换为 ES5 代码；<code>sass-loader</code> 将 <code>sass</code> 代码转换为 <code>css</code> 代码。</p><p>一般 loader 的配置代码如下：</p><pre><code class="language-js">module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    // loader 的执行顺序从下到上
                    {
                        loader: path.resolve('./src/loader2.js'),
                    },
                    {
                        loader: path.resolve('./src/loader1.js'),
                    },
                ]
            }
        ]
    },</code></pre><p>rules 数组包含了一个个匹配规则和具体的 loader 文件。</p><p>上述代码中的 <code>test: /\.js$/</code> 就是匹配规则，表示对 js 文件使用下面的两个 loader。</p><p>而 loader 的处理顺序是自下向上的，即先用 loader1 处理源码，然后将处理后的代码再传给 loader2。</p><p>loader2 处理后的代码就是最终的打包代码。</p><h3 id="loader-">loader 的实现</h3><p>loader 其实是一个函数，它的参数是匹配文件的源码，返回结果是处理后的源码。下面是一个最简单的 loader，它什么都没做：</p><pre><code class="language-js">module.exports = function (source) {
    return source
}</code></pre><p>这么简单的 loader 没有挑战性，我们可以写一个稍微复杂一点的 loader，它的作用是将 <code>var</code> 关键词替换为 <code>const</code>：</p><pre><code class="language-js">module.exports = function (source) {
    return source.replace(/var/g, 'const')
}</code></pre><p>写完之后，我们来测试一下，测试文件为：</p><pre><code class="language-js">function test() {
    var a = 1;
    var b = 2;
    var c = 3;
    console.log(a, b, c);
}

test()</code></pre><p><code>wepback.config.js</code> 配置文件为：</p><pre><code class="language-js">const path = require('path')

module.exports = {
    mode: 'development',
    entry: {
        main: './src/index.js'
    },
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    {
                        loader: path.resolve('./src/loader1.js'),
                    },
                ]
            }
        ]
    },
}</code></pre><p>运行 <code>npm run build</code>，得到打包文件 <code>bundle.js</code>，我们来看一看打包后的代码：</p><pre><code class="language-js">eval("function test() {\r\n    const a = 1;\r\n    const b = 2;\r\n    const c = 3;\r\n    console.log(a, b, c);\r\n}\r\n\r\ntest()\n\n//# sourceURL=webpack:///./src/index.js?");</code></pre><p>可以看到，代码中的 <code>var</code> 已经变成了 <code>const</code>。</p><h3 id="-loader">异步 loader</h3><p>刚才实现的 loader 是一个同步 loader，在处理完源码后用 <code>return</code> 返回。</p><p>下面我们来实现一个异步 loader：</p><pre><code class="language-js">module.exports = function (source) {
    const callback = this.async()

    // 由于有 3 秒延迟，所以打包时需要 3+ 秒的时间
    setTimeout(() =&gt; {
        callback(null, `${source.replace(/;/g, '')}`)
    }, 3000)
}</code></pre><p>异步 loader 需要调用 webpack 的 <code>async()</code> 生成一个 callback，它的第一个参数是 <code>error</code>，这里可设为 <code>null</code>，第二个参数就是处理后的源码。当你异步处理完源码后，调用 <code>callback</code> 即可。</p><p>下面来试一下异步 loader 到底有没生效，这里设置了一个 3 秒延迟。我们来对比一下打包时间：</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1426f2b0393d47b084d4f7c596896c0b~tplv-k3u1fbpfcp-watermark.image" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d43faffb61a144beb5f620d68a597f16~tplv-k3u1fbpfcp-watermark.image" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>上图是调用同步 loader 的打包时间，为 141 ms；下图是调用异步 loader 的打包时间，为 3105 ms，说明异步 loader 生效了。</p><p>如果想看完整 demo 源码，请点击我的 <a href="https://github.com/woai3c/webpack-demo">GitHub</a>。</p><h2 id="plugin">plugin</h2><p>webpack 在整个编译周期中会触发很多不同的事件，plugin 可以监听这些事件，并且可以调用 webpack 的 API 对输出资源进行处理。</p><p>这是它和 loader 的不同之处，loader 一般只能对源文件代码进行转换，而 plugin 可以做得更多。plugin 在整个编译周期中都可以被调用，只要监听事件。</p><p>对于 webpack 编译，有两个重要的对象需要了解一下：</p><blockquote>Compiler 和 Compilation</blockquote><p>在插件开发中最重要的两个资源就是 compiler 和 compilation 对象。理解它们的角色是扩展 webpack 引擎重要的第一步。</p><blockquote>compiler 对象代表了完整的 webpack 环境配置。这个对象在启动 webpack 时被一次性建立，并配置好所有可操作的设置，包括 options，loader 和 plugin。当在 webpack 环境中应用一个插件时，插件将收到此 compiler 对象的引用。可以使用它来访问 webpack 的主环境。</blockquote><blockquote>compilation 对象代表了一次资源版本构建。当运行 webpack 开发环境中间件时，每当检测到一个文件变化，就会创建一个新的 compilation，从而生成一组新的编译资源。一个 compilation 对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态信息。compilation 对象也提供了很多关键时机的回调，以供插件做自定义处理时选择使用。</blockquote><blockquote>这两个组件是任何 webpack 插件不可或缺的部分（特别是 compilation），因此，开发者在阅读源码，并熟悉它们之后，会感到获益匪浅。</blockquote><h3 id="plugin-">plugin 的实现</h3><p>我们看一下官网的定义，webpack 插件由以下部分组成：</p><ol><li>一个 JavaScript 命名函数。</li><li>在插件函数的 prototype 上定义一个 apply 方法。</li><li>指定一个绑定到 webpack 自身的事件钩子。</li><li>处理 webpack 内部实例的特定数据。</li><li>功能完成后调用 webpack 提供的回调。</li></ol><p>简单的说，一个具有 apply 方法的函数就是一个插件，并且它要监听 webpack 的某个事件。下面来看一个简单的示例：</p><pre><code class="language-js">function Plugin(options) { }

Plugin.prototype.apply = function (compiler) {
    // 所有文件资源都被 loader 处理后触发这个事件
    compiler.plugin('emit', function (compilation, callback) {
        // 功能完成后调用 webpack 提供的回调
        console.log('Hello World')
        callback()
    })
}

module.exports = Plugin</code></pre><p>写完插件后要怎么调用呢？</p><p>先在 webpack 配置文件中引入插件，然后在 plugins 选项中配置：</p><pre><code class="language-js">const Plugin = require('./src/plugin')

module.exports = {
	...
    plugins: [
        new Plugin()
    ]
}</code></pre><p>这就是一个简单的插件了。</p><p>下面我们再来写一个复杂点的插件，它的作用是将经过 loader 处理后的打包文件 <code>bundle.js</code> 引入到 <code>index.html</code> 中：</p><pre><code class="language-js">function Plugin(options) { }

Plugin.prototype.apply = function (compiler) {
    // 所有文件资源经过不同的 loader 处理后触发这个事件
    compiler.plugin('emit', function (compilation, callback) {
        // 获取打包后的 js 文件名
        const filename = compiler.options.output.filename
        // 生成一个 index.html 并引入打包后的 js 文件
        const html = `&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Document&lt;/title&gt;
    &lt;script src="${filename}"&gt;&lt;/script&gt;
&lt;/head&gt;
&lt;body&gt;
    
&lt;/body&gt;
&lt;/html&gt;`
        // 所有处理后的资源都放在 compilation.assets 中
        // 添加一个 index.html 文件
        compilation.assets['index.html'] = {
            source: function () {
                return html
            },
            size: function () {
                return html.length
            }
        }

        // 功能完成后调用 webpack 提供的回调
        callback()
    })
}

module.exports = Plugin</code></pre><p>OK，执行一下，看看效果。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d6e9c91ae0ab4a30aefd9ebdff581c41~tplv-k3u1fbpfcp-watermark.image" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>完美，和预测的结果一模一样。</p><p>完整 demo 源码，请看我的 <a href="https://github.com/woai3c/webpack-demo">GitHub</a>。</p><p>想了解更多的事件，请看官网介绍 <a href="https://webpack.docschina.org/api/compiler-hooks/">compiler 钩子</a>。</p><h2 id="-">参考资料</h2><ul><li><a href="https://www.webpackjs.com/contribute/writing-a-loader/">编写一个 loader</a></li><li><a href="https://www.webpackjs.com/contribute/writing-a-plugin/">编写一个插件</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ webpack 模块加载原理 ]]>
                </title>
                <description>
                    <![CDATA[ webpack 是一个模块打包器，在它看来，每一个文件都是一个模块。 无论你开发使用的是 CommonJS 规范还是 ES6 模块规范，打包后的文件都统一使用 webpack 自定义的模块规范来管理、加载模块。本文将从一个简单的示例开始，来讲解 webpack 模块加载原理。 CommonJS 规范 假设现在有如下两个文件： // index.js const test2 = require('./test2') function test() {} test() test2() // test2.js function test2() {} module.exports = test2 以上两个文件使用 CommonJS 规范来导入导出文件，打包后的代码如下（已经删除了不必要的注释）： (function(modules) { // webpackBootstrap     // The module cache   ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/webpack-module-loading/</link>
                <guid isPermaLink="false">611dd027020863066536ae25</guid>
                
                    <category>
                        <![CDATA[ Webpack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ woai3c ]]>
                </dc:creator>
                <pubDate>Thu, 24 Jun 2021 03:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/08/learn-to-code.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>webpack 是一个模块打包器，在它看来，每一个文件都是一个模块。</p><p>无论你开发使用的是 CommonJS 规范还是 ES6 模块规范，打包后的文件都统一使用 webpack 自定义的模块规范来管理、加载模块。本文将从一个简单的示例开始，来讲解 webpack 模块加载原理。</p><h2 id="commonjs-">CommonJS 规范</h2><p>假设现在有如下两个文件：</p><pre><code class="language-js">// index.js
const test2 = require('./test2')

function test() {}

test()
test2()</code></pre><pre><code class="language-js">// test2.js
function test2() {}

module.exports = test2</code></pre><p>以上两个文件使用 CommonJS 规范来导入导出文件，打包后的代码如下（已经删除了不必要的注释）：</p><pre><code class="language-js">(function(modules) { // webpackBootstrap
    // The module cache
    // 模块缓存对象
	var installedModules = {};

    // The require function
    // webpack 实现的 require() 函数
	function __webpack_require__(moduleId) {
        // Check if module is in cache
        // 如果模块已经加载过，直接返回缓存
		if(installedModules[moduleId]) {
			return installedModules[moduleId].exports;
		}
        // Create a new module (and put it into the cache)
        // 创建一个新模块，并放入缓存
		var module = installedModules[moduleId] = {
			i: moduleId,
			l: false,
			exports: {}
		};

        // Execute the module function
        // 执行模块函数
		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

        // Flag the module as loaded
        // 将模块标识为已加载
		module.l = true;

		// Return the exports of the module
		return module.exports;
	}


    // expose the modules object (__webpack_modules__)
    // 将所有的模块挂载到 require() 函数上
	__webpack_require__.m = modules;

    // expose the module cache
    // 将缓存对象挂载到 require() 函数上
	__webpack_require__.c = installedModules;

	// define getter function for harmony exports
	__webpack_require__.d = function(exports, name, getter) {
		if(!__webpack_require__.o(exports, name)) {
			Object.defineProperty(exports, name, { enumerable: true, get: getter });
		}
	};

	// define __esModule on exports
	__webpack_require__.r = function(exports) {
		if(typeof Symbol !== 'undefined' &amp;&amp; Symbol.toStringTag) {
			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
		}
		Object.defineProperty(exports, '__esModule', { value: true });
	};

	// create a fake namespace object
	// mode &amp; 1: value is a module id, require it
	// mode &amp; 2: merge all properties of value into the ns
	// mode &amp; 4: return value when already ns object
	// mode &amp; 8|1: behave like require
	__webpack_require__.t = function(value, mode) {
		if(mode &amp; 1) value = __webpack_require__(value);
		if(mode &amp; 8) return value;
		if((mode &amp; 4) &amp;&amp; typeof value === 'object' &amp;&amp; value &amp;&amp; value.__esModule) return value;
		var ns = Object.create(null);
		__webpack_require__.r(ns);
		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
		if(mode &amp; 2 &amp;&amp; typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
		return ns;
	};

	// getDefaultExport function for compatibility with non-harmony modules
	__webpack_require__.n = function(module) {
		var getter = module &amp;&amp; module.__esModule ?
			function getDefault() { return module['default']; } :
			function getModuleExports() { return module; };
		__webpack_require__.d(getter, 'a', getter);
		return getter;
	};

	// Object.prototype.hasOwnProperty.call
	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

	// __webpack_public_path__
	__webpack_require__.p = "";


    // Load entry module and return exports
    // 加载入口模块，并返回模块对象
	return __webpack_require__(__webpack_require__.s = "./src/index.js");
})({
  "./src/index.js": (function(module, exports, __webpack_require__) {
    eval("const test2 = __webpack_require__(/*! ./test2 */ \"./src/test2.js\")\r\n\r\nfunction test() {}\r\n\r\ntest()\r\ntest2()\n\n//# sourceURL=webpack:///./src/index.js?");
  }),
  
  "./src/test2.js": (function(module, exports) {
    eval("function test2() {}\r\n\r\nmodule.exports = test2\n\n//# sourceURL=webpack:///./src/test2.js?");
  })
});</code></pre><p>可以看到 webpack 实现的模块加载系统非常简单，仅仅只有一百行代码。</p><p>打包后的代码其实是一个立即执行函数，传入的参数是一个对象。这个对象以文件路径为 key，以文件内容为 value，它包含了所有打包后的模块。</p><pre><code class="language-js">{
  "./src/index.js": (function(module, exports, __webpack_require__) {
    eval("const test2 = __webpack_require__(/*! ./test2 */ \"./src/test2.js\")\r\n\r\nfunction test() {}\r\n\r\ntest()\r\ntest2()\n\n//# sourceURL=webpack:///./src/index.js?");
  }),
  
  "./src/test2.js": (function(module, exports) {
    eval("function test2() {}\r\n\r\nmodule.exports = test2\n\n//# sourceURL=webpack:///./src/test2.js?");
  })
}</code></pre><p>将这个立即函数化简一下，相当于：</p><pre><code class="language-js">(function(modules){
	// ...
})({
	path1: function1,
	path2: function2
})</code></pre><p>再看一下这个立即函数做了什么：</p><ol><li>定义了一个模块缓存对象 <code>installedModules</code>，作用是缓存已经加载过的模块。</li><li>定义了一个模块加载函数 <code>__webpack_require__()</code>。</li><li>... 省略一些其他代码。</li><li>使用 <code>__webpack_require__()</code> 加载入口模块。</li></ol><p>其中的核心就是 <code>__webpack_require__()</code> 函数，它接收的参数是 <code>moduleId</code>，其实就是文件路径。</p><p>它的执行过程如下：</p><ol><li>判断模块是否有缓存，如果有则返回缓存模块的 <code>export</code> 对象，即 <code>module.exports</code>。</li><li>新建一个模块 <code>module</code>，并放入缓存。</li><li>执行文件路径对应的模块函数。</li><li>将这个新建的模块标识为已加载。</li><li>执行完模块后，返回该模块的 <code>exports</code> 对象。</li></ol><pre><code class="language-js">   // The require function
   // webpack 实现的 require() 函数
function __webpack_require__(moduleId) {
       // Check if module is in cache
       // 如果模块已经加载过，直接返回缓存
	if(installedModules[moduleId]) {
		return installedModules[moduleId].exports;
	}
       // Create a new module (and put it into the cache)
       // 创建一个新模块，并放入缓存
	var module = installedModules[moduleId] = {
		i: moduleId,
		l: false,
		exports: {}
	};

       // Execute the module function
       // 执行模块函数
	modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

       // Flag the module as loaded
       // 将模块标识为已加载
	module.l = true;

	// Return the exports of the module
	return module.exports;
}</code></pre><p>从上述代码可以看到，在执行模块函数时传入了三个参数，分别为 <code>module</code>、<code>module.exports</code>、<code>__webpack_require__</code>。</p><p>其中 <code>module</code>、<code>module.exports</code> 的作用和 CommonJS 中的 <code>module</code>、<code>module.exports</code> 的作用是一样的，而 <code>__webpack_require__</code> 相当于 CommonJS 中的 <code>require</code>。</p><p>在立即函数的最后，使用了 <code>__webpack_require__()</code> 加载入口模块。并传入了入口模块的路径 <code>./src/index.js</code>。</p><pre><code class="language-js">__webpack_require__(__webpack_require__.s = "./src/index.js");</code></pre><p>我们再来分析一下入口模块的内容。</p><pre><code class="language-js">(function(module, exports, __webpack_require__) {
    eval("const test2 = __webpack_require__(/*! ./test2 */ \"./src/test2.js\")\r\n\r\nfunction test() {}\r\n\r\ntest()\r\ntest2()\n\n//# sourceURL=webpack:///./src/index.js?");
  })</code></pre><p>入口模块函数的参数正好是刚才所说的那三个参数，而 eval 函数的内容美化一下后和下面内容一样：</p><pre><code class="language-js">const test2 = __webpack_require__("./src/test2.js")
function test() {}
test()
test2()
//# sourceURL=webpack:///./src/index.js?</code></pre><p>将打包后的模块代码和原模块的代码进行对比，可以发现仅有一个地方发生了变化，那就是 <code>require</code> 变成了 <code>__webpack_require__</code>。</p><p>再看一下 <code>test2.js</code> 的代码：</p><pre><code class="language-js">function test2() {}
module.exports = test2
//# sourceURL=webpack:///./src/test2.js?</code></pre><p>从刚才的分析可知，<code>__webpack_require__()</code> 加载模块后，会先执行模块对应的函数，然后返回该模块的 <code>exports</code> 对象。而 <code>test2.js</code> 的导出对象 <code>module.exports</code> 就是 <code>test2()</code> 函数。所以入口模块能通过 <code>__webpack_require__()</code> 引入 <code>test2()</code> 函数并执行。</p><p>到目前为止可以发现 webpack 自定义的模块规范完美适配 CommonJS 规范。</p><h2 id="es6-module">ES6 module</h2><p>将刚才用 CommonJS 规范编写的两个文件换成用 ES6 module 规范来写，再执行打包。</p><pre><code class="language-js">// index.js
import test2 from './test2'

function test() {}

test()
test2()</code></pre><pre><code class="language-js">// test2.js
export default function test2() {}</code></pre><p>使用 ES6 module 规范打包后的代码和使用 CommonJS 规范打包后的代码绝大部分都是一样的。</p><p>一样的地方是指 webpack 自定义模块规范的代码一样，唯一不同的是上面两个文件打包后的代码不同。</p><pre><code class="language-js">{
 	"./src/index.js":(function(module, __webpack_exports__, __webpack_require__) {
		"use strict";
		eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _test2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./test2 */ \"./src/test2.js\");\n\r\n\r\nfunction test() {}\r\n\r\ntest()\r\nObject(_test2__WEBPACK_IMPORTED_MODULE_0__[\"default\"])()\n\n//# sourceURL=webpack:///./src/index.js?");
	}),
	
	"./src/test2.js": (function(module, __webpack_exports__, __webpack_require__) {
		"use strict";
		eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return test2; });\nfunction test2() {}\n\n//# sourceURL=webpack:///./src/test2.js?");
	})
}</code></pre><p>可以看到传入的第二个参数是 <code>__webpack_exports__</code>，而 CommonJS 规范对应的第二个参数是 <code>exports</code>。将这两个模块代码的内容美化一下：</p><pre><code class="language-js">// index.js
__webpack_require__.r(__webpack_exports__);
 var _test2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/test2.js");
 function test() {}
 test()
 Object(_test2__WEBPACK_IMPORTED_MODULE_0__["default"])()
 //# sourceURL=webpack:///./src/index.js?</code></pre><pre><code class="language-js">// test2.js
 __webpack_require__.r(__webpack_exports__);
 __webpack_require__.d(__webpack_exports__, "default", function() { return test2; });
 function test2() {}
 //# sourceURL=webpack:///./src/test2.js?</code></pre><p>可以发现，在每个模块的开头都执行了一个 <code>__webpack_require__.r(__webpack_exports__)</code> 语句。并且 <code>test2.js</code> 还多了一个 <code>__webpack_require__.d()</code> 函数。</p><p>我们先来看看 <code>__webpack_require__.r()</code> 和 <code>__webpack_require__.d()</code> 是什么。</p><h3 id="webpack_require-d-"><strong>webpack_require</strong>.d()</h3><pre><code class="language-js">// define getter function for harmony exports
__webpack_require__.d = function(exports, name, getter) {
	if(!__webpack_require__.o(exports, name)) {
		Object.defineProperty(exports, name, { enumerable: true, get: getter });
	}
};</code></pre><p>原来 <code>__webpack_require__.d()</code> 是给 <code>__webpack_exports__</code> 定义导出变量用的。例如下面这行代码：</p><pre><code class="language-js">__webpack_require__.d(__webpack_exports__, "default", function() { return test2; });</code></pre><p>它的作用相当于 <code>__webpack_exports__["default"] = test2</code>。这个 <code>"default"</code> 是因为你使用 <code>export default</code> 来导出函数，如果这样导出函数：</p><pre><code class="language-js">export function test2() {}</code></pre><p>它就会变成 <code>__webpack_require__.d(__webpack_exports__, "test2", function() { return test2; });</code></p><h3 id="webpack_require-r-"><strong>webpack_require</strong>.r()</h3><pre><code class="language-js">// define __esModule on exports
__webpack_require__.r = function(exports) {
	if(typeof Symbol !== 'undefined' &amp;&amp; Symbol.toStringTag) {
		Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
	}
	Object.defineProperty(exports, '__esModule', { value: true });
};</code></pre><p><code>__webpack_require__.r()</code> 函数的作用是给 <code>__webpack_exports__</code> 添加一个 <code>__esModule</code> 为 <code>true</code> 的属性，表示这是一个 ES6 module。</p><p><strong>添加这个属性有什么用呢?</strong></p><p>主要是为了处理混合使用 ES6 module 和 CommonJS 的情况。</p><p>例如导出使用 CommonJS <code>module.export = test2</code> 导出函数，导入使用 ES6 module <code>import test2 from './test2</code>。</p><p>打包后的代码如下：</p><pre><code class="language-js">// index.js
__webpack_require__.r(__webpack_exports__);
var _test2__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./src/test2.js");
var _test2__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(_test2__WEBPACK_IMPORTED_MODULE_0__);
function test() {}
test()
_test2__WEBPACK_IMPORTED_MODULE_0___default()()
//# sourceURL=webpack:///./src/index.js?</code></pre><pre><code class="language-js">// test2.js
 function test2() {}
 module.exports = test2
 //# sourceURL=webpack:///./src/test2.js?</code></pre><p>从上述代码可以发现，又多了一个 <code>__webpack_require__.n()</code> 函数：</p><pre><code class="language-js">__webpack_require__.n = function(module) {
   var getter = module &amp;&amp; module.__esModule ?
   	function getDefault() { return module['default']; } :
   	function getModuleExports() { return module; };
   __webpack_require__.d(getter, 'a', getter);
   return getter;
};</code></pre><p>先来分析一下入口模块的处理逻辑：</p><ol><li>将 <code>__webpack_exports__</code> 导出对象标识为 ES6 module。</li><li>加载 <code>test2.js</code> 模块，并将该模块的导出对象作为参数传入 <code>__webpack_require__.n()</code> 函数。</li><li><code>__webpack_require__.n</code> 分析该 <code>export</code> 对象是否是 ES6 module，如果是则返回 <code>module['default']</code> 即 <code>export default</code> 对应的变量。如果不是 ES6 module 则直接返回 <code>export</code>。</li></ol><h2 id="-">按需加载</h2><p>按需加载，也叫异步加载、动态导入，即只在有需要的时候才去下载相应的资源文件。</p><p>在 webpack 中可以使用 <code>import</code> 和 <code>require.ensure</code> 来引入需要动态导入的代码，例如下面这个示例：</p><pre><code class="language-js">// index.js
function test() {}

test()
import('./test2')</code></pre><pre><code class="language-js">// test2.js
export default function test2() {}</code></pre><p>其中使用 <code>import</code> 导入的 <code>test2.js</code> 文件在打包时会被单独打包成一个文件，而不是和 <code>index.js</code> 一起打包到 <code>bundle.js</code>。</p><figure class="kg-card kg-image-card"><img src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5e51f947e8c0447190af843b3c62be70~tplv-k3u1fbpfcp-watermark.awebp" class="kg-image" alt="在这里插入图片描述" width="600" height="400" loading="lazy"></figure><p>这个 <code>0.bundle.js</code> 对应的代码就是动态导入的 <code>test2.js</code> 的代码。</p><p>接下来看看这两个打包文件的内容：</p><pre><code class="language-js">// bundle.js
(function(modules) { // webpackBootstrap
	// install a JSONP callback for chunk loading
	function webpackJsonpCallback(data) {
		var chunkIds = data[0];
		var moreModules = data[1];

		// add "moreModules" to the modules object,
		// then flag all "chunkIds" as loaded and fire callback
		var moduleId, chunkId, i = 0, resolves = [];
		for(;i &lt; chunkIds.length; i++) {
			chunkId = chunkIds[i];
			if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) &amp;&amp; installedChunks[chunkId]) {
				resolves.push(installedChunks[chunkId][0]);
			}
			installedChunks[chunkId] = 0;
		}
		for(moduleId in moreModules) {
			if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
				modules[moduleId] = moreModules[moduleId];
			}
		}
		if(parentJsonpFunction) parentJsonpFunction(data);

		while(resolves.length) {
			resolves.shift()();
		}

	};


	// The module cache
	var installedModules = {};

	// object to store loaded and loading chunks
	// undefined = chunk not loaded, null = chunk preloaded/prefetched
	// Promise = chunk loading, 0 = chunk loaded
	var installedChunks = {
		"main": 0
	};

	// script path function
	function jsonpScriptSrc(chunkId) {
		return __webpack_require__.p + "" + chunkId + ".bundle.js"
	}

	// The require function
	function __webpack_require__(moduleId) {

		// Check if module is in cache
		if(installedModules[moduleId]) {
			return installedModules[moduleId].exports;
		}
		// Create a new module (and put it into the cache)
		var module = installedModules[moduleId] = {
			i: moduleId,
			l: false,
			exports: {}
		};

		// Execute the module function
		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

		// Flag the module as loaded
		module.l = true;

		// Return the exports of the module
		return module.exports;
	}

	// This file contains only the entry chunk.
	// The chunk loading function for additional chunks
	__webpack_require__.e = function requireEnsure(chunkId) {
		var promises = [];

		// JSONP chunk loading for javascript

		var installedChunkData = installedChunks[chunkId];
		if(installedChunkData !== 0) { // 0 means "already installed".

			// a Promise means "currently loading".
			if(installedChunkData) {
				promises.push(installedChunkData[2]);
			} else {
				// setup Promise in chunk cache
				var promise = new Promise(function(resolve, reject) {
					installedChunkData = installedChunks[chunkId] = [resolve, reject];
				});
				promises.push(installedChunkData[2] = promise);

				// start chunk loading
				var script = document.createElement('script');
				var onScriptComplete;

				script.charset = 'utf-8';
				script.timeout = 120;
				if (__webpack_require__.nc) {
					script.setAttribute("nonce", __webpack_require__.nc);
				}
				script.src = jsonpScriptSrc(chunkId);

				// create error before stack unwound to get useful stacktrace later
				var error = new Error();
				onScriptComplete = function (event) {
					// avoid mem leaks in IE.
					script.onerror = script.onload = null;
					clearTimeout(timeout);
					var chunk = installedChunks[chunkId];
					if(chunk !== 0) {
						if(chunk) {
							var errorType = event &amp;&amp; (event.type === 'load' ? 'missing' : event.type);
							var realSrc = event &amp;&amp; event.target &amp;&amp; event.target.src;
							error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
							error.name = 'ChunkLoadError';
							error.type = errorType;
							error.request = realSrc;
							chunk[1](error);
						}
						installedChunks[chunkId] = undefined;
					}
				};
				var timeout = setTimeout(function(){
					onScriptComplete({ type: 'timeout', target: script });
				}, 120000);
				script.onerror = script.onload = onScriptComplete;
				document.head.appendChild(script);
			}
		}
		return Promise.all(promises);
	};

	// expose the modules object (__webpack_modules__)
	__webpack_require__.m = modules;

	// expose the module cache
	__webpack_require__.c = installedModules;

	// define getter function for harmony exports
	__webpack_require__.d = function(exports, name, getter) {
		if(!__webpack_require__.o(exports, name)) {
			Object.defineProperty(exports, name, { enumerable: true, get: getter });
		}
	};

	// define __esModule on exports
	__webpack_require__.r = function(exports) {
		if(typeof Symbol !== 'undefined' &amp;&amp; Symbol.toStringTag) {
			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
		}
		Object.defineProperty(exports, '__esModule', { value: true });
	};

	// create a fake namespace object
	// mode &amp; 1: value is a module id, require it
	// mode &amp; 2: merge all properties of value into the ns
	// mode &amp; 4: return value when already ns object
	// mode &amp; 8|1: behave like require
	__webpack_require__.t = function(value, mode) {
		if(mode &amp; 1) value = __webpack_require__(value);
		if(mode &amp; 8) return value;
		if((mode &amp; 4) &amp;&amp; typeof value === 'object' &amp;&amp; value &amp;&amp; value.__esModule) return value;
		var ns = Object.create(null);
		__webpack_require__.r(ns);
		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
		if(mode &amp; 2 &amp;&amp; typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
		return ns;
	};

	// getDefaultExport function for compatibility with non-harmony modules
	__webpack_require__.n = function(module) {
		var getter = module &amp;&amp; module.__esModule ?
			function getDefault() { return module['default']; } :
			function getModuleExports() { return module; };
		__webpack_require__.d(getter, 'a', getter);
		return getter;
	};

	// Object.prototype.hasOwnProperty.call
	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

	// __webpack_public_path__
	__webpack_require__.p = "";

	// on error function for async loading
	__webpack_require__.oe = function(err) { console.error(err); throw err; };

	var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
	var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
	jsonpArray.push = webpackJsonpCallback;
	jsonpArray = jsonpArray.slice();
	for(var i = 0; i &lt; jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
	var parentJsonpFunction = oldJsonpFunction;

	// Load entry module and return exports
	return __webpack_require__(__webpack_require__.s = "./src/index.js");
})({
  "./src/index.js":(function(module, exports, __webpack_require__) {
    eval("function test() {}\r\n\r\ntest()\r\n__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./test2 */ \"./src/test2.js\"))\n\n//# sourceURL=webpack:///./src/index.js?");
  })
});</code></pre><pre><code class="language-js">// 0.bundle.js
(window["webpackJsonp"] = window["webpackJsonp"] || []).push(
  [
    [0],
    {
      "./src/test2.js":(function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"default\", function() { return test2; });\nfunction test2() {}\n\n//# sourceURL=webpack:///./src/test2.js?");
      })
    }
  ]
);</code></pre><p>这次打包的代码量有点膨胀，<code>bundle.js</code> 代码居然有 200 行。我们来看看相比于同步加载的 webpack 模块规范，它有哪些不同：</p><ol><li>定义了一个对象 <code>installedChunks</code>，作用是缓存动态模块。</li><li>定义了一个辅助函数 <code>jsonpScriptSrc()</code>，作用是根据模块 ID 生成 URL。</li><li>定义了两个新的核心函数 <code>__webpack_require__.e()</code> 和 <code>webpackJsonpCallback()</code>。</li><li>定义了一个全局变量 <code>window["webpackJsonp"] = []</code>，它的作用是存储需要动态导入的模块。</li><li>重写 <code>window["webpackJsonp"]</code> 数组的 <code>push()</code> 方法为 <code>webpackJsonpCallback()</code>。也就是说 <code>window["webpackJsonp"].push()</code> 其实执行的是 <code>webpackJsonpCallback()</code>。</li></ol><p>而从 <code>0.bundle.js</code> 文件可以发现，它正是使用 <code>window["webpackJsonp"].push()</code> 来放入动态模块的。动态模块数据项有两个值，第一个是 <code>[0]</code>，它是模块的 ID；第二个值是模块的路径名和模块内容。</p><p>然后我们再看一下打包后的入口模块的代码，经过美化后：</p><pre><code class="language-js">function test() {}
test()
__webpack_require__.e(0).then(__webpack_require__.bind(null, "./src/test2.js"))
//# sourceURL=webpack:///./src/index.js?</code></pre><p>原来模块代码中的 <code>import('./test2')</code> 被翻译成了 <code>__webpack_require__.e(0).then(__webpack_require__.bind(null, "./src/test2.js"))</code>。</p><p>那 <code>__webpack_require__.e()</code> 的作用是什么呢？</p><h3 id="webpack_require-e-"><strong>webpack_require</strong>.e()</h3><pre><code class="language-js">__webpack_require__.e = function requireEnsure(chunkId) {
	var promises = [];
	// JSONP chunk loading for javascript
	var installedChunkData = installedChunks[chunkId];
	if(installedChunkData !== 0) { // 0 means "already installed".
		// a Promise means "currently loading".
		if(installedChunkData) {
			promises.push(installedChunkData[2]);
		} else {
			// setup Promise in chunk cache
			var promise = new Promise(function(resolve, reject) {
				installedChunkData = installedChunks[chunkId] = [resolve, reject];
			});
			promises.push(installedChunkData[2] = promise);

			// start chunk loading
			var script = document.createElement('script');
			var onScriptComplete;

			script.charset = 'utf-8';
			script.timeout = 120;
			if (__webpack_require__.nc) {
				script.setAttribute("nonce", __webpack_require__.nc);
			}
			script.src = jsonpScriptSrc(chunkId);

			// create error before stack unwound to get useful stacktrace later
			var error = new Error();
			onScriptComplete = function (event) {
				// avoid mem leaks in IE.
				script.onerror = script.onload = null;
				clearTimeout(timeout);
				var chunk = installedChunks[chunkId];
				if(chunk !== 0) {
					if(chunk) {
						var errorType = event &amp;&amp; (event.type === 'load' ? 'missing' : event.type);
						var realSrc = event &amp;&amp; event.target &amp;&amp; event.target.src;
						error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
						error.name = 'ChunkLoadError';
						error.type = errorType;
						error.request = realSrc;
						chunk[1](error);
					}
					installedChunks[chunkId] = undefined;
				}
			};
			var timeout = setTimeout(function(){
				onScriptComplete({ type: 'timeout', target: script });
			}, 120000);
			script.onerror = script.onload = onScriptComplete;
			document.head.appendChild(script);
		}
	}
	return Promise.all(promises);
};</code></pre><p>它的处理逻辑如下：</p><ol><li>先查看该模块 ID 对应缓存的值是否为 0，0 代表已经加载成功了，第一次取值为 <code>undefined</code>。</li><li>如果不为 0 并且不是 <code>undefined</code> 代表已经是加载中的状态。然后将这个加载中的 Promise 推入 <code>promises</code> 数组。</li><li>如果不为 0 并且是 <code>undefined</code> 就新建一个 Promise，用于加载需要动态导入的模块。</li><li>生成一个 <code>script</code> 标签，URL 使用 <code>jsonpScriptSrc(chunkId)</code> 生成，即需要动态导入模块的 URL。</li><li>为这个 <code>script</code> 标签设置一个 2 分钟的超时时间，并设置一个 <code>onScriptComplete()</code> 函数，用于处理超时错误。</li><li>然后添加到页面中 <code>document.head.appendChild(script)</code>，开始加载模块。</li><li>返回 <code>promises</code> 数组。</li></ol><p>当 JS 文件下载完成后，会自动执行文件内容。也就是说下载完 <code>0.bundle.js</code> 后，会执行 <code>window["webpackJsonp"].push()</code>。</p><p>由于 <code>window["webpackJsonp"].push()</code> 已被重置为 <code>webpackJsonpCallback()</code> 函数。所以这一操作就是执行 <code>webpackJsonpCallback()</code> ，接下来我们看看 <code>webpackJsonpCallback()</code> &nbsp;做了哪些事情。</p><h3 id="webpackjsonpcallback-">webpackJsonpCallback()</h3><p>对这个模块 ID 对应的 Promise 执行 <code>resolve()</code>，同时将缓存对象中的值置为 0，表示已经加载完成了。相比于 <code>__webpack_require__.e()</code>，这个函数还是挺好理解的。</p><h3 id="--1">小结</h3><p>总的来说，动态导入的逻辑如下：</p><ol><li>重写 <code>window["webpackJsonp"].push()</code> 方法。</li><li>入口模块使用 <code>__webpack_require__.e()</code> 下载动态资源。</li><li>资源下载完成后执行 <code>window["webpackJsonp"].push()</code>，即 <code>webpackJsonpCallback()</code>。</li><li>将资源标识为 0，代表已经加载完成。由于加载模块使用的是 Promise，所以要执行 <code>resolve()</code>。</li><li>再看一下入口模块的加载代码 <code>__webpack_require__.e(0).then(__webpack_require__.bind(null, "./src/test2.js"))</code>，下载完成后执行 <code>then()</code> 方法，调用 <code>__webpack_require__()</code> 真正开始加载代码，<code>__webpack_require__()</code> 在上文已经讲解过，如果不了解，建议再阅读一遍。</li></ol> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Webpack 打包太慢? 试试 Dllplugin ]]>
                </title>
                <description>
                    <![CDATA[ Webpack 在 build 包的时候，有时候会遇到打包时间很长的问题，这里提供了一个解决方案，让打包如丝般顺滑。 1. 介绍 在用 Webpack 打包的时候，对于一些不经常更新的第三方库，比如 react，lodash，vue 我们希望能和自己的代码分离开，Webpack 社区有两种方案：  * CommonsChunkPlugin  * DLLPlugin 对于 CommonsChunkPlugin，Webpack 每次打包实际还是需要去处理这些第三方库，只是打包完之后，能把第三方库和我们自己的代码分开。而  DLLPlugin 则是能把第三方代码完全分离开，即每次只打包项目自身的代码。Dll 这个概念是借鉴了 Windows 系统的 dll。一个 dll 包，就是一个纯纯的依赖库，它本身不能运行，是用来给你的app引用的。 2. 模板 webpack-simple 用法 要使用 DLLPlugin，需要额外新建一个配置文件。所以对于用这种方式打包的项目，一般会有下面两个配置文件：  * webpack.config.js  * webpack.dll.config. ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/webpack-packaging-skills/</link>
                <guid isPermaLink="false">5d8ae9c9fbfdee429dc5fe72</guid>
                
                    <category>
                        <![CDATA[ Webpack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ SHERlocked93 ]]>
                </dc:creator>
                <pubDate>Wed, 25 Sep 2019 04:29:47 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/04/photo-1569271532860-dd35503aaf1f.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Webpack 在 build 包的时候，有时候会遇到打包时间很长的问题，这里提供了一个解决方案，让打包如丝般顺滑。</p><h2 id="1-">1. 介绍</h2><p>在用 Webpack 打包的时候，对于一些不经常更新的第三方库，比如 <code>react</code>，<code>lodash</code>，<code>vue</code> 我们希望能和自己的代码分离开，Webpack 社区有两种方案：</p><ul><li>CommonsChunkPlugin</li><li>DLLPlugin</li></ul><p>对于 <code>CommonsChunkPlugin</code>，Webpack 每次打包实际还是需要去处理这些第三方库，只是打包完之后，能把第三方库和我们自己的代码分开。而 <code>DLLPlugin</code> 则是能把第三方代码完全分离开，即每次只打包项目自身的代码。Dll 这个概念是借鉴了 Windows 系统的 dll。一个 dll 包，就是一个纯纯的依赖库，它本身不能运行，是用来给你的app引用的。</p><h2 id="2-webpack-simple-">2. 模板 webpack-simple 用法</h2><p>要使用 <code>DLLPlugin</code>，需要额外新建一个配置文件。所以对于用这种方式打包的项目，一般会有下面两个配置文件：</p><ul><li>webpack.config.js</li><li>webpack.dll.config.js</li></ul><p><strong><strong>在项目根目录新建一个文件 webpack.dll.config.js</strong>。</strong></p><pre><code>const path    = require('path');
const webpack = require('webpack');
module.exports = {
  entry: {
      vendor: ['vue-router','vuex','vue/dist/vue.common.js','vue/dist/vue.js','vue-loader/lib/component-normalizer.js','vue']
  },
  output: {
    path: path.resolve('./dist'),
    filename: '[name].dll.js',
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve('./dist', '[name]-manifest.json'),
      name: '[name]_library'
    })
  ]
};</code></pre><p>这是把用到的第三方插件添加到 vendor 中。<strong><strong>然后在</strong> <strong>webpack.config.js</strong> <strong>中添加代码</strong>。</strong></p><pre><code>plugins: [
    new webpack.DllReferencePlugin({
      manifest: require('./dist/vendor-manifest.json')
    })
  ]</code></pre><p><strong><strong>再在入口</strong> <strong>html</strong> <strong>文件中引入 vendor.dll.js</strong>。</strong><br><code>&lt;script type="text/javascript" src="./../vendor.dll.js"&gt;&lt;/script&gt;</code></p><p><strong><strong>然后在</strong> <strong>package.json</strong> <strong>文件中添加快捷命令</strong></strong><strong>（build:dll）。</strong></p><pre><code>"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
    "build:dll": "webpack --config webpack.dll.config.js"
  },</code></pre><p>最后打包的时候首先执行 npm run build:dll 命令会在打包目录下生成 <code>vendor-manifest.json</code> 文件与 <code>vendor.dll.js</code> 文件。打包 dll 的时候，Webpack 会将所有包含的库做一个索引，写在一个 manifest 文件中，而引用 dll 的代码（dll user）在打包的时候，只需要读取这个 manifest 文件，就可以了。</p><p><strong><strong>再执行</strong></strong><code><strong><strong>npm run build</strong></strong></code><br>发现现在的 Webpack 打包速度为 2，3 秒左右，与之前的 20 秒左右快了很多。</p><h2 id="3-webpack-">3. 模板webpack 用法</h2><p><strong><strong>在</strong> <strong>build</strong> <strong>下创建 webpack.dll.config.js</strong>。</strong></p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/09/image-58.png" class="kg-image" alt="image-58" width="293" height="303" loading="lazy"></figure><p>内容：</p><pre><code>const path = require('path')
const webpack = require('webpack')
module.exports = {
  entry: {
    vendor: [
      'vue-router',
      'vuex',
      'vue/dist/vue.common.js',
      'vue/dist/vue.js',
      'vue-loader/lib/component-normalizer.js',
      'vue',
      'axios',
      'echarts'
    ]
  },
  output: {
    path: path.resolve('./dist'),
    filename: '[name].dll.js',
    library: '[name]_library'
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve('./dist', '[name]-manifest.json'),
      name: '[name]_library'
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
}</code></pre><p>建议加上代码压缩插件，否则 dll 包会比较大。</p><p><strong><strong>在 webpack.prod.conf.js 的 plugin 后面加入配置</strong>。</strong></p><pre><code>new webpack.DllReferencePlugin({
    manifest: require('../dist/vendor-manifest.json')
})</code></pre><p><strong><strong>根目录下的入口 index.html 加入引用</strong>。</strong><br><code>&lt;script type="text/javascript" src="./vendor.dll.js"&gt;&lt;/script&gt;</code></p><p><strong><strong>package.json</strong> <strong>的</strong> <strong>script</strong> <strong>里加入快捷命令</strong>。</strong><br><code>"build:dll": "webpack --config build/webpack.dll.config.js"</code></p><p>要生成 dll 时运行<code>npm run build:dll</code>，即生成 dist 目录下两个文件 <code>vender-manifest.json</code> 与 <code>vender.dll.js</code>。<br>然后正式生成 prod <code>npm run build:prod</code>，即生成除<code>webpack.dll.config.js</code>中指定包之外的其他打包文件。</p><p>在尝试在 <a href="http://panjiachen.github.io/vue-element-admin" rel="nofollow noreferrer"><code>vue-element-admin</code></a> 中引入 DllPlugin 时，加入 20 个打包项，测试结果：<br>原来的打包时间——</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/09/image-59.png" class="kg-image" alt="image-59" width="600" height="257" loading="lazy"></figure><p>引入 DllPlugin 后的打包时间——</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/09/image-60.png" class="kg-image" alt="image-60" width="600" height="261" loading="lazy"></figure><p>可以看到大幅缩短了打包时间。</p><h2 id="4-externals-">4. 另一种方法 externals 选项</h2><p>也可以使用 externals 让 Webpack 不打包某部分，然后在其他地方引入 cdn 上的 js 文件，利用缓存下载 cdn 文件达到减少打包时间的目的。<br><strong><strong>配置</strong> <strong>externals</strong> <strong>选项</strong></strong>：</p><pre><code>// webpack.prod.config.js
// 多余代码省略
module.exports = {
    externals: {
        'vue': 'window.Vue',
        'vuex': 'window.Vuex',
        'vue-router': 'window.VueRouter'
        ...
    }
}

// 配置externals之后，webpack不会把配置项中的代码打包进去，别忘了需要在外部引入cdn上的js文件
// html
&lt;body&gt;
    &lt;script src="XXX/cdn/vue.min.js"&gt;&lt;/script&gt;
    ......
&lt;/body&gt;</code></pre> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
