webpack(2.0)入门详解 | 掘金技术征文

3,055 阅读5分钟


声明:以此文写给摸索和学习使用webpack的初级使用者,因为官方文档只给出了关键点的解读,对于入门的初学者看的可谓是一头雾水,现在好多的文章也是只是介绍了自己的想法和案例,没有讲明白基础点

我所接触的前端模块化变更

两年前实习所在的一家创业公司在前端工程化方面还很传统,几乎称不上是前端工程化,因为我们通过使用Ant这个构建工具,在一个build.xml中配置将所有的js都打包成一个js文件,css也是,可想而知项目小的时候,前期很顺利,后来项目越来越大,新需求新逻辑越来越多,这种方法即将被淘汰掉。

后半年我们开始接触grunt+sea.js,将以前的项目全部重构,性能上有了很大的改善,但是慢慢地就会发现需要配置的task越来越多等问题,那时候jquery+bootstrap还是我们的主要开发框架。

一年后我毕业了离开创业公司,来到了现在的公司,见识到了真正的前端工程化项目,目前我们还处在使用webpack+gulp的阶段,今年即将全面使用webpack 去掉gulp

来一段废话

如今的Webpack已经是一个出色的前端自动化构建工具、模块化工具、资源管理工具。至于Webpack是什么?为什么使用Webpack?gulp,grant,webpack 什么关系和区别这些在此不做赘述,太多的文档和文章了大家去了解一下

走进webpack 基本实例(翻译2.0官方例子)

先不要考虑把每一个点和知识都了解和懂了再动手,我一般都是直接看官方的英文文档的Getting Started,相信我这绝对是最快最正确的学习方式

一步步开始吧

  • 基础需求:先装好node,因为webpack是一个基于node的项目
  • 初始化一个webpack项目根据以下命令
//cd 到你的一个目录下可以是documents or desktop一步步输入命令
mkdir webpack-demo && cd webpack-demo 
npm init -y
npm install --save-dev webpack

这时候打开你的项目文件你会发现webpack-demo文件夹下多了两个文件

  • package.json
  • node_modules

然后我们在编辑器中打开webpack-demo文件夹,手动创建下列目录文件

  1. app文件夹/index.js(用来写js逻辑)
  2. index.html(实例的入口页面)
  3. webpack.config.js(webpack的配置文件)

上代码示例(粘贴到你的文件中用吧)app/index.js

function component () {
  var element = document.createElement('div');

  /* lodash is required for the next line to work */
  element.innerHTML = _.join(['Hello','webpack'], ' ');

  return element;
}

document.body.appendChild(component());

由于在这个js中依赖lodash(这是一个具有一致接口、模块化、高性能等特性的 JavaScript 工具库),所以在我们的项目中需要加入这个依赖到我们的node_modules中,执行以下命令即可

npm install --save lodash

以上命令只是将lodash这个依赖库添加进去,然而我们要使用还得通过import 将它引入到具体的js文件中,所以修改我们的app/index.js,添加一行,然后将它以 _ 的别名绑定(不会造成全局范围变量名污染)

import _ from 'lodash';
function component () {
  var element = document.createElement('div');
  element.innerHTML = _.join(['Hello','webpack'], ' ');
  return element;
}
document.body.appendChild(component());

下面来写index.html

<html>
  <head>
    <title>webpack 2 demo</title>
  </head>
  <body>
   <script src="dist/bundle.js"></script>
  </body>
</html>

你肯定会问?dist目录没有啊?更别说bundle.js了,别着急继续往下看webpack.config.js

var path = require('path');

module.exports = {
  entry: './app/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

然后在运行webpack 命令(如果存在 webpack.config.js,webpack 命令将默认选择使用它)

webpack --config webpack.config.js

命令行的结果

Hash: ff6c1d39b26f89b3b7bb
Version: webpack 2.2.0
Time: 390ms
    Asset    Size  Chunks                    Chunk Names
bundle.js  544 kB       0  [emitted]  [big]  main
   [0] ./~/lodash/lodash.js 540 kB {0} [built]
   [1] (webpack)/buildin/global.js 509 bytes {0} [built]
   [2] (webpack)/buildin/module.js 517 bytes {0} [built]
   [3] ./app/index.js 278 bytes {0} [built]

此时我们的项目目录中已经成功创建 dist/bundle.js 文件,打开浏览器运行你的index.html页面会显示'Hello webpack'

让我门带着疑问我们开始讲下面的内容

webpack配置文件详解

webpack的配置文件是一个node.js的module,它没有固定的命名,我们看到的例子都是webpack.config.js,一开始我以为这是约定成俗的固定名字,就例如gulpfile.js,它也没有固定的路径要求,如果你直接用webpack来执行编译,那么webpack默认读取的将是当前目录下的webpack.config.js

webpack.config.js用CommonJS风格来书写,形如:

module.exports = {
    entry: "./entry",
    output: {
        path: __dirname + "/dist",
        filename: "bundle.js"
    }
}

如果你有其它命名的需要或是你有多份配置文件,可以使用--config参数传入路径来执行:

$ webpack --config ./webpackConfig/dev.config.js

一些常用基本概念

  • 入口文件配置:entry参数

entry可以是字符串形式的单个文件入口,也可以是对象形式的多入口,如果是多个单页应用程序,那么则使用多入口的方式来独立每个应用

entry: './app/index.js',
单入口
entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
}
多入口
  • 输出文件:output参数

output参数告诉webpack以什么方式来生成/输出文件,值得注意的是,与entry不同,output相当于一套规则,所有的入口都必须使用这一套规则,不能针对某一个特定的入口来制定output规则,于此同时output有很多options,这里介绍比较常用的:

path、publicPath、filename、chunkFilename

所有的opitons

webpack中output配置项必须要包含的两个参数是path和filename

const config = {
  output: {
    filename: 'bundle.js',
    path: '/home/proj/public/assets'
  }
};

module.exports = config;

path

path参数表示生成文件的根目录,需要传入一个绝对路径。path参数和后面的filename参数共同组成入口文件的完整路径

filename

filename指定每个输出文件在最终生成目录上的名称

1.单个入口文件的output配置

{
  entry: './src/app.js',
  output: {
    filename: 'bundle.js',
    path: __dirname + '/build'
  }
}

// writes to disk: ./build/bundle.js

2.多入口文件的output配置,有以下三种规则

{
  entry: {
    app: './src/app.js',
    search: './src/search.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/build'
  }
}

// writes to disk: ./build/app.js, ./build/search.js
  • [name],指代入口文件的name,也就是上面提到的entry参数的key,因此,我们可以在name里利用/,即可达到控制文件目录结构的效果。
  • [hash],指代本次编译的一个hash版本,值得注意的是,只要是在同一次编译过程中生成的文件,这个[hash]的值就是一样的;在缓存的层面来说,相当于一次全量的替换。
  • [chunkhash],指代的是当前chunk的一个hash版本,也就是说,在同一次编译中,每一个chunk的hash都是不一样的;而在两次编译中,如果某个chunk根本没有发生变化,那么该chunk的hash也就不会发生变化。这在缓存的层面上来说,就是把缓存的粒度精细到具体某个chunk,只要chunk不变,该chunk的浏览器缓存就可以继续使用。

publicPath

publicPath参数表示的是一个URL路径(指向生成文件的根目录),用于生成css/js/图片/字体文件等资源的路径,以确保网页能正确地加载到这些资源。 publicPath参数跟path参数的区别是:path参数其实是针对本地文件系统的,而publicPath则针对的是浏览器;因此,publicPath既可以是一个相对路径,如示例中的'../../../../build/',也可以是一个绝对路径如http形式的网页链接。

chunkFilename

chunkFilename参数与filename参数类似,都是用来定义生成文件的命名方式的,只不过,chunkFilename参数指定的是除入口文件外的chunk

现在来说说我们最开始的案例中用到的path变量是什么,在最开始讲到webpack的配置文件是一个node.js的module,所以这里的path其实也是node.js的一个模块,使用来处理路径的模块,它提供了很多方法比如获取绝对路径的path.resolve()等,可以去搜索资料学习

var path = require('path');

module.exports = {
  entry: './app/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};
  • 各种Loader配置:module参数
    webpack一直号称能够打包任何资源,采用的就是Loader,loader是对应用程序中资源文件进行转换。它们是(运行在Node.js中的)函数,可以将资源文件作为参数的来源,然后返回新的资源文件

这里距举一个打包css的例子,要通过webpack打包CSS,像任何其他模块一样将CSS导入JavaScript代码,并使用css-loader(它输出CSS作为JS模块)

  1. 首先,安装相对应的 loader
  2. 像JavaScript模块一样导入CSS文件,例如在index.js中
  3. 其次,配置 webpack.config.js,对每个 .css 文件使用 css-loader

$ npm install --save-dev css-loader

创建一个index.css在index.js中

import './index.css';

配置CSS和JavaScript打包在一起

module.exports = {
    module: {
        rules: [{
            test: /\.css$/,
            use: 'css-loader'
        }]
    }
}
  • 更多的内容例如插件 模块 热替换等内容请自行学习

写在最后的总结

看完这篇文章,如果按照我的步骤一步步实现,肯定对webpack(2.0)已经入门了,下面需要的就是你花费时间学习官方文档并且结合自己的项目来实际操作了,附上官网的文档,2.0的文档写的很清楚和详细webpack2.0

Cayley 一个不断努力学习的女程序员