webpack是一个静态模块打包器,此处的模块可以是任意文件,包括Sass、TypeScript、模板和图像等。webpack可根据输入文件的依赖关系,打包输出浏览器可识别的JavaScript、CSS和HTML等文件,并且能对图像做优化处理,如图1所示。

图1  webpack打包

  目前,webpack的最新版本是4.33,其配置文件(webpack.config.js)的基本结构如下所示,包含了它的4个核心概念:入口(entry)、输出(output)、加载器(loader)和插件(plugin)。

module.exports = {
entry: {},   //入口
output: {},    //输出
module: { rules: [] }, //加载器
plugins: []    //插件
};

一、安装

  如果要安装最新版本的webpack,那么可以运行下面这条命令。

npm install --save-dev webpack

  当使用的是webpack 4+版本时,还需要再安装它的命令行工具,安装命令如下所示。

npm install --save-dev webpack-cli

  接下来就可以运行webpack的打包命令了,如下所示,其中“--config”参数后面会跟着配置文件的路径。

npx webpack --config webpack.config.js

  通过package.json的scripts字段可声明自定义的脚本任务(如下代码所示),从而就能快捷的执行打包命令,例如“npm run build”。

{
scripts: {
build: "npx webpack --config webpack.config.js"
}
}

二、入口

  在webpack.config.js中,entry字段是一个入口,记录着需要处理的模块。从这个入口开始,webpack会递归地构建出模块之间的依赖关系。

1)单个入口

  当entry字段是一个字符串类型时,其值就是模块的相对或绝对路径,如下所示。

module.exports = {
entry: "./index.js"
};

  这其实是一种简写,等价于下面的对象形式。对象的键就是chunk的名称,chunk是webpack的特定术语,通常一个chunk对应或多个chunk组成一个bundle(即打包生成的文件)。

module.exports = {
entry: {
main: "./index.js"
}
};

  entry字段的值既可以是对象和字符串,也可以是由模块路径组成的数组,如下代码所示,其中index.js和list.js两个文件会被合并成一个chunk。

module.exports = {
entry: ["./index.js", "./list.js"]
};

2)多个入口

  只要在entry的对象中添加多个属性就能设置多个入口,如下所示。

module.exports = {
entry: {
index: "./index.js",
list: __dirname + "/list.js"
}
};

  两个chunk会被分别命名为index和list,其中__dirname是Node.js内置的全局变量,保存着当前文件所处目录的绝对路径。

三、输出

  在webpack.config.js中,output字段是一个对象,用于配置输出的信息。它的filename属性可声明输出的文件名,而另一个path属性可配置输出目录的绝对路径。与入口不同,在配置文件中只能存在一个输出。

1)filename

  如果在入口中声明了多个chunk,那么在配置输出时得用占位符来表示对应的bundle名称,如下所示。

module.exports = {
entry: {
index: "./index.js",
list: "./list.js"
},
output: {
filename: "[name].bundle.js"
}
};

  [name]表示chunk的名称,例如index和list,还有另外三个常用的占位符,如表3所示。

表3  filename中的占位符

占位符 描述
[id] chunk的唯一标识符
[hash] chunk的唯一标识符的hash值
[chunkhash] chunk内容的hash值

  [hash]常与[name]配合使用(如下所示),在运行打包命令后,默认会在配置文件所处的位置创建dist目录,并且把生成的bundle文件放置其中。

module.exports = {
output: {
filename: "[name].[hash].bundle.js"
}
};

2)path

  如果要更改默认的输出路径(即不想在dist目录下生成bundle文件),那么可以通过设置path实现。但要注意,它的值必须是绝对路径,不能是相对路径,如下所示。

module.exports = {
output: {
path: __dirname + "/build"
}
};

四、加载器

  加载器(loader)能在webpack加载模块时对其进行预处理,即对模块的源码进行转换,下面列出加载器的几个比较典型的用途。

(1)将浏览器无法识别的JSX、Sass等语言转换成JavaScript、CSS等语言。

(2)把图像转换成Data URI格式嵌入到JavaScript文件中。

(3)用ES6的import关键字将CSS文件导入到JavaScript中。

1)配置

  加载器不仅需要单独安装,还得在webpack.config.js中配置module字段。下面是一个简单的配置示例,其作用是让file-loader加载器处理四种类型的图像。

module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [ "file-loader" ]
}]
}
};

  module的值是一个对象,包含一个rules属性,记录了模块匹配的规则,而这些规则又会以对象的形式组成一个数组,作为rules属性的值。每条规则必须包含test和use两个属性,前者是一个正则表达式,可设置匹配条件;后者是一个由字符串或对象组成的数组,可指定要使用的加载器。只有当test属性中的条件匹配成功后,才会让use属性中的加载器处理相应的模块。

  接下来将展示加载器的用法,以上面的file-loader为例,首先通过命令将其安装,如下所示。

npm install --save-dev file-loader

  然后创建一个index.js文件,其内容就是引入一张avatar.jpg图像,并将实例化的Image对象添加到页面的<body>元素内,如下所示。

import src from "./avatar.jpg";
var img = new Image();
img.src = src;
document.body.appendChild(img);

  再创建一张index.html页面,引用打包生成的index.bundle.js文件,如下所示。

<body>
<script src="dist/index.bundle.js"></script>
</body>

  最后执行webpack的打包命令,就会在index.html的<body>元素中添加下面的<img>元素。

<img src="68fd51ab711118f323bdddf6de7a0175.jpg" />

  注意,默认情况下,图像会随着bundle文件一起被放置到dist目录下。这样的话,上述<img>元素将无法读取到图像,得为其src属性加上路径。为了解决该问题,可以利用file-loader提供的配置参数。下面将use属性中的规则修改成对象形式,用options参数记录publicPath选项,即定义图像的发布目录。

module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [{
loader: "file-loader",
options: { publicPath: "./dist" }
}]
}]
}
};

  当一个条件对应多个加载器时,其执行顺序是从右到左。以下面的加载器为例,先执行url-loader,后执行file-loader。

module.exports = {
module: {
rules: [{
test: /\.(png|svg|jpg|gif)$/,
use: [ "file-loader", "url-loader" ]
}]
}
};

  由于目前市面上的加载器有可能无法满足实际需求,因此官方提供了自定义加载器的方法,具体可参考相关文档

2)Babel

  在之前的第2篇中,对Babel做了专门的讲解,现在利用加载器可以将Babel的配置写到webpack.config.js中,接下来将演示在webpack中使用Babel。

  首先安装babel-loader、Babel的核心包以及集合了ES语法的预设,命令如下所示。

npm install --save-dev babel-loader @babel/core @babel/preset-env

  然后在webpack.config.js的module字段中配置规则,如下所示。

module.exports = {
module: {
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: [{
loader: "babel-loader",
options: {
presets: ['@babel/preset-env']
}
}]
}]
}
};

  rules中的exclude属性定义了忽略的条件,即不会对node_modules目录中的脚本文件执行babel-loader。在babel-loader的presets选项中声明了所使用的预设。当执行打包命令后,就会对下面这样的ES6语法进行编译,转换成低版本的ES语法。

let fn = () => true;

五、插件

  插件能够借助webpack引擎的能力,将自定义的行为注入到webpack的构建流程中,解决加载器无法实现的功能,例如分离打包、压缩文件等。插件不仅能处理模块和编译过的资源,还能监控文件的变化。与加载器一样,插件也可根据特定需求实现自定义,具体可参考官方文档

  插件的配置被放在了plugins字段中,它的值是一个由插件实例组成的数组。接下来将通过html-webpack-plugin来演示插件的用法。

1)配置

  html-webpack-plugin不仅能根据模板生成一个HTML文件,还能自动引入所需的bundle文件,对于那些随着编译而发生名称变化的bundle文件特别有用。

  要使用它,首先需要将其安装,相关的命令如下所示。

npm install --save-dev html-webpack-plugin

  然后在webpack.config.js中声明插件的实例,如下配置所示,在初始化插件时,没有为其传递任何参数。

var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
index: "./index.js"
},
output: {
filename: "[name].[hash].bundle.js"
},
plugins: [
new HtmlWebpackPlugin()
]
};

  最后执行打包命令,生成的HTML文件如下所示,其中脚本文件的名称包含了一个hash值。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script src="index.4ee657c406f9babd171a.bundle.js"></script>
</body>
</html>

2)自定义模板

  除了上面所使用的默认模板之外,html-webpack-plugin还提供了自定义模板的功能。默认情况下,html-webpack-plugin采用的是EJS模板引擎,声明的加载器是ejs-loader。如果要使用其它模板引擎,那么必须得把相应的加载器添加到配置文件中。

  下面利用EJS模板的语法插入页面标题,首先创建一个template.html模板文件,如下所示。

<!DOCTYPE html>
<html>
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
<meta charset="utf-8" />
</head>
<body>
<div>模板内容</div>
</body>
</html>

  然后向插件传递两个参数:title和template,前者就是页面标题,后者是模板路径。如此设置之后,就能渲染出所需要的页面了。

module.exports = {
plugins: [
new HtmlWebpackPlugin({
title: "模板",
template: "./template.html"
})
]
};

六、模块化

  webpack实现了一套兼容所有模块化方案的机制,这让任意文件皆有可能成为模块,而构建依赖图和按需打包等功能则都由webpack内部完成。

  在webpack中,能够表达模块之间依赖关系的方式有多种,例如下面所列的。

(1)CommonJS规范的require()函数。

(2)AMD规范的define()函数。

(3)ES6标准的import语句。

(4)Sass和Less中的@import语句。

(5)CSS中引用图像的url()函数。

(6)<img>元素用于加载图像的src属性。

前端利器躬行记(3)——webpack基础的更多相关文章

  1. 前端利器躬行记(4)——webpack进阶

    webpack是一个非常强大的工具,除了前文所介绍的基础概念之外,还有各种进阶应用,例如Source Map.模块热替换.集成等,本文会对这些内容做依次讲解. 一. runtime和manifest ...

  2. 前端利器躬行记(1)——npm

    npm(Node Package Manager)是Node.js的包管理工具,相当于一个在线仓库.它提供了一个公共的平台,将分散在世界各地的包集中起来,能轻松的安装.分享和管理相关的包,不用再为搜索 ...

  3. 前端利器躬行记(2)——Babel

    Babel是一个JavaScript编译器,不仅能将当前运行环境不支持的JavaScript语法(例如ES6.ES7等)编译成向下兼容的可用语法(例如ES3或ES5),这其中会涉及新语法的转换和缺失特 ...

  4. 前端利器躬行记(5)——Git

    Git是一款开源的分布式版本控制系统,它的出现和Linux紧密相关.Linux内核项目组为了能更好地管理和维护Linux内核开发,于2002年开始启用商业的分布式版本控制系统BitKeeper.虽然软 ...

  5. 前端利器躬行记(6)——Fiddler

    Fiddler是一款免费的.基于Windows系统的代理服务器软件(即Web调试抓包工具),由Eric Lawrence用C#语言在2003年10月发布了第一个版本.注意,由于Fiddler依赖Mic ...

  6. 前端利器躬行记(8)——VSCode插件研发

    VSCode提供了丰富的 API,可以借助编辑器扩展许多定制功能. 本次研发了一款名为 Search Method 的插件,在此记录整个研发过程. 一.准备工作 1)安装环境 首先是全局安装 yo 和 ...

  7. ES6躬行记(1)——let和const

    古语云:“纸上得来终觉浅,绝知此事要躬行”.的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓.自己研读过许多ES6相关的书籍和资料,平时工作中也会用到,但在用到时经常需要上搜索引擎中查 ...

  8. ES6躬行记 笔记

    ES6躬行记(18)--迭代器 要实现以下接口## next() ,return,throw 可以用for-of保证迭代对象的正确性 例如 var str = "向

  9. Node.js躬行记(4)——自建前端监控系统

    这套前端监控系统用到的技术栈是:React+MongoDB+Node.js+Koa2.将性能和错误量化.因为自己平时喜欢吃菠萝,所以就取名叫菠萝系统.其实在很早以前就有这个想法,当时已经实现了前端的参 ...

随机推荐

  1. java - 解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法

    通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间: 而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收 ...

  2. halcon视频教程如何学习?怎么样才能踏入机器视觉这个行业?

    本人是工作八年的视觉工程师,主要从事Halcon和Visionpro视觉开发,谈谈个人对视觉学习看法: 1.HALCON是德国MVtec公司开发的一套完善的标准的机器视觉算法包,它节约了产品成本,缩短 ...

  3. 初始Spring 文档 完整版

    初始Spring 1.Spring官网 spring.io 01. IOC(Inverse of Control)控制反转 02. AOP(Aspect Oritend Programming)面向切 ...

  4. python基础学习(起步)

    目录 python基础学习(起步) 变量 常量 变量的内存管理 python垃圾回收机制 变量的其他赋值方式 今日编程小题 本人能力有限,若有偏颇之处请读者大大不吝赐教! 祝大家每天都在成长! pyt ...

  5. Nginx搭建详细

    Linux 安装Nginx搭建详细内容 进入:/usr/java/nginx位置下载nginx: wget et http://nginx.org/download/nginx-1.8.0.tar.g ...

  6. 【iOS】UILabel 常用属性设置

    UILabel 的一些常用属性,示例代码如下: // 字体大小 label.font = [UIFont systemFontOfSize:14.0]; label.font = [UIFont fo ...

  7. 面试必问之ArrayList

    ArrayList概述 (1)ArrayList 是一种变长的集合类,基于定长数组实现. (2)ArrayList 允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容量时, ...

  8. hdoj 4715 Difference Between Primes 素数筛选+二分查找

    #include <string.h> #include <stdio.h> const int maxn = 1000006; bool vis[1000006]; int ...

  9. 20190803 NOIP模拟测试12「斐波那契(fibonacci)· 数颜色 · 分组 」

    164分 rank11/64 这次考的不算太差,但是并没有多大的可能性反超(只比一小部分人高十几分而已),时间分配还是不均,T2两个半小时,T1半个小时,T3-额十几分钟吧 然额付出总是与回报成反比的 ...

  10. Hadoop 系列(一)—— 分布式文件系统 HDFS

    一.介绍 HDFS (Hadoop Distributed File System)是 Hadoop 下的分布式文件系统,具有高容错.高吞吐量等特性,可以部署在低成本的硬件上. 二.HDFS 设计原理 ...