Webpack 3,从入门到放弃

原文首发于:Webpack 3,从入门到放弃

Update (2017.8.27) : 关于 output.publicPathdevServer.contentBasedevServer.publicPath的区别。如下:

  • output.publicPath: 对于这个选项,我们无需关注什么绝对相对路径,因为两种路径都可以。我们只需要知道一点:这个选项是指定 HTML 文件中资源文件 (字体、图片、JS文件等) 的文件名的公共 URL 部分的。在实际情况中,我们首先会通过output.filename或有些 loader 如file-loadername属性设置文件名的原始部分,webpack 将文件名的原始部分和公共部分结合之后,HTML 文件就能获取到资源文件了。
  • devServer.contentBase: 设置静态资源的根目录,html-webpack-plugin生成的 html 不是静态资源。当用 html 文件里的地址无法找到静态资源文件时就会去这个目录下去找。
  • devServer.publicPath: 指定浏览器上访问所有 打包(bundled)文件 (在dist里生成的所有文件) 的根目录,这个根目录是相对服务器地址及端口的,比devServer.contentBaseoutput.publicPath优先。

前言

Tips
如果你用过 webpack 且一直用的是 webpack 1,请参考 从v1迁移到v2 (v2 和 v3 差异不大) 对版本变更的内容进行适当的了解,然后再选择性地阅读本文。

首先,这篇文章是根据当前最新的 webpack 版本 (即 v3.4.1) 撰写,较长一段时间内无需担心过时的问题。其次,这应该会是一篇极长的文章,涵盖了基本的使用方法,有更高级功能的需求可以参考官方文档继续学习。再次,即使是基本的功能,也内容繁多,我尽可能地解释通俗易懂,将我学习过程中的疑惑和坑一一解释,如有纰漏,敬请雅正。再次,为了清晰有效地讲解,我会演示从零编写 demo,只要一步步跟着做,就会清晰许多。最后,官方文档也是个坑爹货!

Webpack,何许人也?

借用官方的说法:

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

简言之,webpack 是一个模块打包器 (module bundler),能够将任何资源如 JavaScript 文件、CSS 文件、图片等打包成一个或少数文件。

为什么要用介个 Webpack?

首先,定义已经说明了 webpack 能将多个资源模块打包成一个或少数文件,这意味着与以往的发起多个 HTTP 请求来获得资源相比,现在只需要发起少量的 HTTP 请求。

Tips
想了解合并 HTTP 请求的意义,请见 这里

其次,webpack 能将你的资源转换为最适合浏览器的“格式”,提升应用性能。比如只引用被应用使用的资源 (剔除未被使用的代码),懒加载资源 (只在需要的时候才加载相应的资源)。再次,对于开发阶段,webpack 也提供了实时加载和热加载的功能,大大地节省了开发时间。除此之外,还有许多优秀之处之处值得去挖掘。不过,webpack 最核心的还是打包的功能。

webpack,gulp/grunt,npm,它们有什么区别?

webpack 是模块打包器(module bundler),把所有的模块打包成一个或少量文件,使你只需加载少量文件即可运行整个应用,而无需像之前那样加载大量的图片,css文件,js文件,字体文件等等。而gulp/grunt 是自动化构建工具,或者叫任务运行器(task runner),是把你所有重复的手动操作让代码来做,例如压缩JS代码、CSS代码,代码检查、代码编译等等,自动化构建工具并不能把所有模块打包到一起,也不能构建不同模块之间的依赖图。两者来比较的话,gulp/grunt 无法做模块打包的事,webpack 虽然有 loader 和 plugin可以做一部分 gulp/grunt 能做的事,但是终究 webpack 的插件还是不如 gulp/grunt 的插件丰富,能做的事比较有限。于是有人两者结合着用,将 webpack 放到 gulp/grunt 中用。然而,更好的方法是用 npm scripts 取代 gulp/grunt,npm 是 node 的包管理器 (node package manager),用于管理 node 的第三方软件包,npm 对于任务命令的良好支持让你最终省却了编写任务代码的必要,取而代之的,是老祖宗的几个命令行,仅靠几句命令行就足以完成你的模块打包和自动化构建的所有需求。

准备开始

先来看看一个 webpack 的一个完备的配置文件,是 介样 的,当然啦,这里面有很多配置项是即使到这个软件被废弃你也用不上的:),所以无需担心。

基本配置

开始之前,请确定你已经安装了当前 Node 的较新版本。

然后执行以下命令以新建我们的 demo 目录:

$ mkdir webpack-demo && cd webpack-demo && npm init -y
$ npm i --save-dev webpack
$ mkdir src && cd src && touch index.js

我们使用工具函数库 lodash 来演示我们的 demo。先安装之:

$ npm i --save lodash

src/index.js

import _ from 'lodash';

function component() {
  const element = document.createElement('div');
    
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');
    
  return element;
}

document.body.appendChild(component());

Tips
importexport 已经是 ES6 的标准,但是仍未得到大多数浏览器的支持 (可喜的是, Chrome 61 已经开始默认支持了,见 ES6 modules),不过 webpack 提供了对这个特性的支持,但是除了这个特性,其他的 ES6 特性并不会得到 webpack 的特别支持,如有需要,须借助 Babel 进行转译 (transpile)。

然后新建发布版本目录:

$ cd .. && mkdir dist && cd dist && touch index.html 

dist/index.html




    webpack demo


    

现在,我们运行 webpack 来打包 index.jsbundle.js,本地安装了 webpack 后可以通过 node_modules/.bin/webpack 来访问 webpack 的二进制版本。

$ cd ..
$ ./node_modules/.bin/webpack src/index.js dist/bundle.js # 第一个参数是打包的入口文件,第二个参数是打包的出口文件

咻咻咻,大致如下输出一波:

Hash: de8ed072e2c7b3892179
Version: webpack 3.4.1
Time: 390ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  544 kB       0  [emitted]  [big]  main
   [0] ./src/index.js 225 bytes {0} [built]
   [2] (webpack)/buildin/global.js 509 bytes {0} [built]
   [3] (webpack)/buildin/module.js 517 bytes {0} [built]
    + 1 hidden module

现在,你已经得到了你的第一个打包文件 (bundle.js) 了。

使用配置文件

像上面这样使用 webpack 应该是最挫的姿势了,所以我们要使用 webpack 的配置文件来提高我们的姿势水平。

$ touch webpack.config.js

webpack.config.js

const path = require('path');

module.exports = {
  entry: './src/index.js', // 入口起点,可以指定多个入口起点
  output: { // 输出,只可指定一个输出配置
    filename: 'bundle.js', // 输出文件名
    path: path.resolve(__dirname, 'dist') // 输出文件所在的目录
  }
};

执行:

$ ./node_modules/.bin/webpack --config webpack.config.js # `--config` 制定 webpack 的配置文件,默认是 `webpack.config.js`

所以这里可以省却 --config webpack.config.js。但是每次都要写 ./node_modules/.bin/webpack 实在让人不爽,所以我们要动用 NPM Scripts

package.json

{
  ...
  "scripts": {
    "build": "webpack"
  },
  ...
}

Tips
npm scripts 中我们可以通过包名直接引用本地安装的 npm 包的二进制版本,而无需编写包的整个路径。

执行:

$ npm run build

一波输出后便得到了打包文件。

Tips
bulid 并不是 npm scripts 的内置属性,需要使用 npm run 来执行脚本,详情见 npm run

打包其他类型的文件

因为其他文件和 JS 文件类型不同,要把他们加载到 JS 文件中就需要经过加载器 (loader) 的处理。

加载 CSS

我们需要安装两个 loader 来处理 CSS 文件:

$ npm i --save-dev style-loader css-loader

style-loader 通过插入