关于 webpack 你可能忽略的细节(附源码分析)

Woodrow3385 8年前
   <p>本篇的主要目标是通过实际问题来介绍 webpack 中容易被人忽略的细节, 以及源码分析( <strong>以最新发布的 release 版本1.14.0的源码为例</strong> ), 并且提供几种解决方案。</p>    <p><img src="https://simg.open-open.com/show/eb003d14a21809c61e9e2125aab79644.png"></p>    <p style="text-align: center;">webpack from the official website</p>    <p>随着前端技术的火热发展,工程化,模块化和组件化的思想已逐步成为主流,与之相应的,就需要有一整套工具流可以支撑起它。</p>    <p>现在比较热门的前端资源模块化管理和打包工具应该非 Webpack 莫属了。</p>    <h2>Webpack 是什么</h2>    <p>它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。</p>    <p>--引自 Webpack 中文指南</p>    <h2>使用举例</h2>    <p>我们来看一下官方文档中的最小用例,新建并写入以下内容到这两个文件:</p>    <p>cats.js</p>    <pre>  var cats = ['dave', 'henry', 'martha'];  module.exports = cats;</pre>    <p>app.js (Entry Point)</p>    <pre>  cats = require('./cats.js');  console.log(cats);</pre>    <p>这个时候,就可以使用 webpack 进行打包了:</p>    <pre>  webpack ./app.js app.bundle.js</pre>    <p>我们来看一下发生了什么, 目录下生成了一个打包后的文件 app.bundle.js ,这就是最基础的打包过程。</p>    <h2>提出问题</h2>    <p>如何判断打包是否成功?</p>    <h2>通用方案</h2>    <p>下面是我们常用的两种判断任务是否执行成功的方案</p>    <p>通过 return code</p>    <p>通过命令执行后的 return code 来判断(在 shell 中使用 $? 获得)。 并且通常情况下 0 是执行成功, 非 0 是未成功。 我们以上面的例子来测试一下:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/17ad881147bd3f3b753bd4da00102d24.png"></p>    <p style="text-align: center;">webpack-demo.png</p>    <p>可以看到 $? 的值为 0 , 且打包后的文件运行正常。</p>    <p>那么我们来修改一下 app.js 文件的内容, 将 require 引入的模块路径故意写错,来测试一下:</p>    <p><img src="https://simg.open-open.com/show/839e911d942153d37d8d43cb830c0ded.png"></p>    <p style="text-align: center;">webpack-error.png</p>    <p>注意:箭头处 $? 的值仍然为 0 , 且生成的打包后的文件运行出错。</p>    <p>这就说明,根据 return code 的值判断任务是否执行成功, 不可行!</p>    <p>通过标准错误输出</p>    <p>我们也会通过 <strong>标准错误输出</strong> ( stderr )来判断一个任务执行过程中是否有错误输出。还是使用上面的例子做示范:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/6e0ed8d2e5e8cee95d8b69c6d4640ff6.png"></p>    <p style="text-align: center;">webpack-stderr.png</p>    <p>根据这个例子,可以看到 webpack 并没有标准错误输出!所以这个方法也不可行。</p>    <h2>探究原因及源码分析</h2>    <p>这里以最新发布的 release 版本 1.14.0 的源码作为分析。 在 lib/Compilation.js 中我们可以看到这样一段代码:</p>    <pre>  var errorAndCallback = function errorAndCallback(err) {    err.dependencies = dependencies;    err.origin = module;    module.dependenciesErrors.push(err);    _this.errors.push(err);    if(bail) {      callback(err);    } else {      callback();    }  };</pre>    <p>在源码中可以看到这个函数其实被调用的还比较多, 例如:在模块为可选的时候, 会判断只是抛出警告还是处理错误, 而上面这段代码自然也不必多数, 关键点在于 bail 的值, 而我们继续找, 可以看到在 bin/config-optimist.js 中有对 bail 参数的解析, 这是一个布尔值。而因为没有太多描述, 所以这个参数就经常容易被忽略。</p>    <h2>解决方案</h2>    <h3>1. 加bail参数</h3>    <p>基于上面简要的分析, 我们来尝试下 bail 参数的作用。 仍然使用上面的例子:</p>    <p>我们使用 webpack --bail true app.js app.bundle.js 进行测试</p>    <p><img src="https://simg.open-open.com/show/8652a5a930c885d33e5c3731a1b1fc52.png"></p>    <p style="text-align: center;">webpack-bail.png</p>    <p>可以看到, 使用 bail 参数并传递 true 进去, 在遇到错误的时候,打包过程将会退出, return code 为 1 且把错误信息打印到 stderr .</p>    <h3>2. 使用 webpack-fail-plugin</h3>    <p>webpack-fail-plugin 是专为解决这个问题而生的,它会在错误发生的时候 return 1 . 使用方法也很简单:</p>    <p>安装:</p>    <pre>  npm install webpack-fail-plugin</pre>    <p>使用:</p>    <pre>  var failPlugin = require('webpack-fail-plugin');    module.exports = {      //config       plugins: [          failPlugin      ]  }</pre>    <h3>3.使用 done plugin</h3>    <p>具体用法如下:</p>    <pre>  // ...  plugins: [     // ...     function() {       this.plugin("done", function(stats) {        if (stats.compilation.errors && stats.compilation.errors.length) {          console.log(stats.compilation.errors);           process.exit(1);         }        // ...       });     }  // ...  ],  // ...</pre>    <h3>4. 使用 webpack 2</h3>    <p>不过 webpack 2 现在还在 beta 阶段,可以期待下。 (webpack 2 也仍然是使用 bail 参数)</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/21eaaacba1f2</p>    <p> </p>