前端利器躬行记(3)——webpack基础
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基础的更多相关文章
- 前端利器躬行记(4)——webpack进阶
webpack是一个非常强大的工具,除了前文所介绍的基础概念之外,还有各种进阶应用,例如Source Map.模块热替换.集成等,本文会对这些内容做依次讲解. 一. runtime和manifest ...
- 前端利器躬行记(1)——npm
npm(Node Package Manager)是Node.js的包管理工具,相当于一个在线仓库.它提供了一个公共的平台,将分散在世界各地的包集中起来,能轻松的安装.分享和管理相关的包,不用再为搜索 ...
- 前端利器躬行记(2)——Babel
Babel是一个JavaScript编译器,不仅能将当前运行环境不支持的JavaScript语法(例如ES6.ES7等)编译成向下兼容的可用语法(例如ES3或ES5),这其中会涉及新语法的转换和缺失特 ...
- 前端利器躬行记(5)——Git
Git是一款开源的分布式版本控制系统,它的出现和Linux紧密相关.Linux内核项目组为了能更好地管理和维护Linux内核开发,于2002年开始启用商业的分布式版本控制系统BitKeeper.虽然软 ...
- 前端利器躬行记(6)——Fiddler
Fiddler是一款免费的.基于Windows系统的代理服务器软件(即Web调试抓包工具),由Eric Lawrence用C#语言在2003年10月发布了第一个版本.注意,由于Fiddler依赖Mic ...
- 前端利器躬行记(8)——VSCode插件研发
VSCode提供了丰富的 API,可以借助编辑器扩展许多定制功能. 本次研发了一款名为 Search Method 的插件,在此记录整个研发过程. 一.准备工作 1)安装环境 首先是全局安装 yo 和 ...
- ES6躬行记(1)——let和const
古语云:“纸上得来终觉浅,绝知此事要躬行”.的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓.自己研读过许多ES6相关的书籍和资料,平时工作中也会用到,但在用到时经常需要上搜索引擎中查 ...
- ES6躬行记 笔记
ES6躬行记(18)--迭代器 要实现以下接口## next() ,return,throw 可以用for-of保证迭代对象的正确性 例如 var str = "向
- Node.js躬行记(4)——自建前端监控系统
这套前端监控系统用到的技术栈是:React+MongoDB+Node.js+Koa2.将性能和错误量化.因为自己平时喜欢吃菠萝,所以就取名叫菠萝系统.其实在很早以前就有这个想法,当时已经实现了前端的参 ...
随机推荐
- 7kyu (难度系数kyu阶段数值越大难度越低) 数组分组及求和
几个人排成一排,分成两队.第一个人进入一队,第二个人进入第二队,第三个人进入第一队,以此类推. 给定一个正整数的数组(人的权重),返回两个整数的新数组/元组,其中第一个是第1组的总重量,第二个是第2组 ...
- .net持续集成sonarqube篇之 sonarqube与jenkins集成(命令模式)
系列目录 Sonarqube结合Jenkins与常见问题 我们引入sonarqube组件的最终目的是要为整个Ci环境服务的,如果不能集成于当前的Jenkins CI,那么我们做的很多关于sonarqu ...
- JS面向对象编程(一):封装
js是一门基于面向对象编程的语言. 如果我们要把(属性)和(方法)封装成一个对象,甚至要从原型对象生成一个实例,我们应该怎么做呢? 一.生成对象的原始模式 假定把猫看 ...
- java中map,set的简单使用
package test2; import java.util.*; import static java.lang.System.out; public class test2 extends St ...
- 【Android】Field requires API level 4 (current min is 1): android.os.Build.VERSION#SDK_INT
刚遇到了这个问题: Field requires API level 4 (current min is 1): android.os.Build.VERSION#SDK_INT 解决方法: 修改 A ...
- 请使用switch语句和if...else语句,计算2008年8月8日这一天,是该年中的第几天。
请使用switch语句和if...else语句,计算2008年8月8日这一天,是该年中的第几天. #include <stdio.h> int main() { /* 定义需要计算的日期 ...
- 调测Onvif事件总结解决办法
主要在调测事件用例的过程中,发现了大量的信息,和未曾碰到的场景和非法错误等信息,先总结解决办法如下: (1)测试过程中发现以前的一个难题解决了,原先在生成soap空间命名的文件中有部分需要下载,离线生 ...
- hadoop学习(二)----HDFS简介及原理
前面简单介绍了hadoop生态圈,大致了解hadoop是什么.能做什么.带着这些目的我们深入的去学习他.今天一起看一下hadoop的基石--文件存储.因为hadoop是运行与集群之上,处于分布式环境之 ...
- 转载 | SVG向下兼容优雅降级方法
本文引自:http://www.zhangxinxu.com/wordpress/2013/09/svg-fallbacks/ 1.svg image标签降级技术 <svg width=&quo ...
- 如何获取app中的toast
前言 Toast是什么呢?在这个手机飞速发展的时代,app的种类也越来越多,那们在日常生活使用中,经常会发现,当你在某个app的输入框输入非法字符或者非法执行某个流程时,经常看到系统会给你弹出一个黑色 ...