webpack 是什么?用来做什么?有哪些优点?webpack 的核心概念有哪些?如何配置?
一 Webpack 基础知识
1. Webpack 是什么?
Webpack 是前端的一种建构工具,它的兴起,源于前端模块化和工程化的普及。它是用 Node.js 写的。
Webpack 适用于多种语言,本文示例及后续相关文章,都以 JavaScript 语法为例。
1) 模块化
模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程,有多种属性,分别反映其内部特性。
前端模块化一般指的是 JavaScript 的模块,比如最常见的 NPM 包,可以被抽象封装的最小/最优代码集合,模块化解决的是功能耦合问题。目前,css 也在一定程度上实现了模块化,如 Less / Sass 可以使用 @import 引入自己的依赖。
[注:] 大家可能也经常听到组件化这个名词,组件化则更像是模块化进一步封装,根据业务特点或者不同的场景封装出具有一定功能特性的独立整体;另外,前端提到组件化更多的是具有模板、样式和 JS 交互的 UI 组件。
主流的模块化的规范:
CommonJS
CommonJS 是 Nodejs 广泛使用的一套模块化规范,是一种同步加载模块依赖的方式。
- require: 引入一个模块。
- exports: 导出模块内容。
- module: 模块本身。
AMD
模块加载库 RequireJS 提出并且完善的一套模块化规范,是一种异步加载模块依赖的方式。
- id: 模块 id。
- dependencies:模块依赖。
- factory:模块的工厂函数,即模块的初始化操作函数
- require:引入模块。
ES6 Module
ES6 推出的一套模块化规范。
- import: 引入模块。
- export:导出模块。
2) 工程化
一切以提高效率、降低成本、质量保证为目的的手段,都属于工程化。
前端工程化的必要性,如下列问题:
- 模块多了,依赖管理怎么做?
- 页面复杂度提升之后,多页面、多系统、多状态怎么办?
- 团队扩大之后,团队合作怎么做?
- 怎么解决多人研发中的性能、代码风格等问题?
- 如何权衡研发效率和产品迭代的问题?
3) Webpack 的作用
Webpack 是实现工程化的一个工具。
本质上,Webpack 是一个现代 JavaScript 应用程序的静态模块打包器,在 Webpack 处理应用程序时,它会在内部创建一个依赖图,用于映射到项目需要的每个模块,然后将所有这些依赖生成到一个或多个 bundle。
Webpack 跟其他构建工具(Grunt、Gulp)本质上不同之处在于:Webpack 是从入口文件开始,经过模块依赖加载、分析和打包三个流程完成项目的构建。在加载、分析和打包的三个过程中,可以针对性的做一些解决方案,达到按需加载的目的。
Webpack 还可以轻松的解决传统构建工具解决的问题:
- 模块化打包,一切皆模块,JS 是模块,CSS 等也是模块;
- 语法糖转换:比如 ES6 转 ES5、TypeScript;
- 预处理器编译:比如 Less、Sass 等;
- 项目优化:比如压缩、CDN;
- 解决方案封装:通过强大的 Loader 和插件机制,可以完成解决方案的封装,比如 PWA;
- 流程对接:比如测试流程、语法检测等。
2. Webpack 无配置文件打包操作示例
安装 webpack 及 webpack-cli
npm i webpack --save-dev
npm i webpack-cli --save-dev
命令脚本配置
在 package.json 文件,添加scripts字段:
"scripts": {
"build": "webpack"
}
执行npm run build,即可完成项目打包。
Webpack 默认的入口文件是src/index.js,默认的输出目录是dist/main.js。
"scripts": {
"dev": "webpack --mode development", // 开发模式
"build": "webpack --mode production" // 生产模式
}
// 指定输入文件
"scripts": {
"dev": "webpack --mode development ./src/ex/index.js",
"build": "webpack --mode production ./src/ex/index.js"
}
// 指定输出文件
"scripts": {
"dev": "webpack --mode development --output ./output/main.js",
"build": "webpack --mode production --output ./output/main.js"
}
// 显示进度和颜色
"scripts": {
"build": "webpack --progress --colors"
}
// 打印错误详情
"scripts": {
"build": "webpack --display-error-details"
}
// 指定 Webpack 配置文件的路径
"scripts": {
"build": "webpack –-config config.js"
}
3. Webpack 配置文件及配置类型
Webpack 默认的配置文件是 webpack.config.js,该 js 文件是一个 Node.js 的模块,遵循 CommonJS 模块规范,可以通过 require() 语法导入其他文件或者使用 Node.js 内置的模块,也可以使用普通的 JavaScript 编写语法,包括变量、函数、表达式等。
如果需要指定某个配置文件,可以使用下面的命令:
webpack --config xx.js
如果 Webpack 不是全局安装,则可以在项目目录下实行:
node ./node_modules/webpack/bin/webpack --config xx.js
或者使用npx:
npx webpack --config xx.js
npx 是一个方便开发者访问 node_modules 内的 bin 命令行的小工具,npx webpack -v 相当于执行了 node ./node_modules/webpack/bin/webpack -v ,npx 在开发中非常方便,推荐安装:npm install -g npx 。
简单的 webpack.config.js 示例
const path = require('path');
module.exports = {
mode: 'development',
entry: './foo.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'foo.bundle.js'
}
};
函数类型的 Webpack 配置
// 函数类型的配置必须返回一个配置对象
// 函数接受两个参数env和argv:分别对应着环境对象和 Webpack-CLI 的命令行选项
module.exports = (env, argv) => {
return {
mode: env.production ? 'production' : 'development',
devtool: env.production ? 'source-maps' : 'eval',
plugins: [
new TerserPlugin({
terserOptions: {
compress: argv['optimize-minimize'] // 只有传入 -p 或 --optimize-minimize
}
})
]
};
};
Promise 类型的 Webpack 配置
module.exports = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
entry: './app.js'
/* ... */
});
}, 5000);
});
};
支持多个配置、数组类型的 Webpack 配置
module.exports = [
{
mode: 'production'
// 配置1
},
{
// 配置2
}
];
二 Webpack 核心概念与常用配置
webpack 是一个模块打包工具,能够从一个需要处理的 js 文件开始,构建一个关系图,该图映射到了项目中的每个模块,然后将这个依赖关系图输出到一个或者多个 bundle 中。
webpack 的核心模块包括:入口 entry,输出 output,loader 及 插件 plugin 四大部分。除了这四个核心概念外,还有resolve、module等一些重要的配置项。
常见配置解释
| 参数 | 说明 |
|---|---|
| entry | 项目入口 |
| output | 项目出口 |
| loader | 模块转化器/处理器,对模块进行转换处理 |
| plugin | 扩展插件,打包优化,资源管理,注入环境变量等 |
| mode | 模式,开发环境development,生成环境production |
| module | 开发中每一个文件都可以看作 module,包括 css/图片 等 |
| resolve | 帮助 Webpack 快速查找依赖 |
| chunk | 代码块,一个 chunk 可以由多个模块组成 |
| bundle | 最终打包完成的文件,一般和chunk一一对应,bundle就是对chunk进行压缩打包等处理后的产出 |
| context | 项目打包的相对路径上下文,如 entry 的相对路径是基于此的,默认为process.cwd()即工作目录 |
1. 入口 entry
webpack 打包的入口起点,默认值是./src/index.js。
entry 支持多种数据类型,包括字符串、对象、数组。从形式上来说,包括了单文件入口和多文件入口两种方式。
1) 单文件入口
// 字符串形式
module.exports = {
entry: './path/to/my/entry/file.js',
};
// 对象形式
module.exports = {
entry: {
main: './path/to/my/entry/file.js',
}
};
// 数组形式
// webpack 会自动生成一个新的入口模块,将数组中每个文件加载进来,绘制在一个"chunk"中,
// 并将最后一个模块的 module.exports 作为入口模块的 module.exports 导出
module.exports = {
entry: ['./src/file_1.js', './src/file_2.js'],
output: {
filename: 'bundle.js',
}
};
2) 多文件入口
多文件入口是使用对象语法来通过支持多个entry,多文件入口的对象语法相对于单文件入口,具有较高的灵活性。
module.exports = {
entry: {
app: './src/app.js',
adminApp: './src/adminApp.js',
},
};
2. 输出 output
output指定了 entry 对应文件编译打包后的输出 bundle,默认输出到 dist/main.js。output 的常用属性有:
| 属性 | 说明 |
|---|---|
| path | 指定输出的 bundle 存放的路径 |
| filename | bundle 的名称 |
| publicPath | 指定一个在浏览器中被引用的 URL 地址 |
| library | 指定库名(打包生成一个供别人使用的库) |
| libraryTarget | 指定库打包出来的规范 |
| externals | 去除输出的打包文件中依赖的某些第三方 js 模块(例如 jquery等) |
| target | 针对多种平台应用,指定构建的目标(web/node/electron等) |
| devtool | 用来控制怎么显示sourcemap, 通过sourcemap可以快速还原代码的错误位置 |
filename
output 只能配置一个 filename,如果要使不同的 entry 输出到不同的 chunk,需要使用占位符。
| 占位符 | 说明 |
|---|---|
| [name] | 模块名称 |
| [id] | 模块标识符 |
| [hash] | 模块标识符的 hash |
| [chunkhash] | chunk 内容的hash |
| [query] | 模块的 query,例如,文件名 ? 后面的字符串 |
| [function] | 一个 return 出一个 string 作为 filename 的函数 |
// 写入到硬盘:./dist/app.js, ./dist/search.js
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js',
},
output: {
filename: '[name].js',
path: __dirname + '/dist',
},
};
[hash] 和 [chunkhash] 的长度可以使用 [hash:16](默认为 20)来指定。或者,通过指定 output.hashDigestLength 在全局配置长度。
占位符是可以组合使用的,例如[name]-[hash:8]。
[hash]:整个项目的 hash 值,其根据每次编译内容计算得到,每次编译之后都会生成新的 hash,即修改任何文件都会导致所有文件的 hash 发生改变;在一个项目中虽然入口不同,但是 hash 是相同的;hash 无法实现前端静态资源在浏览器上长缓存,这时候应该使用 chunkhash;
[chunkhash]:根据不同的入口文件(entry)进行依赖文件解析,构建对应的 chunk,生成相应的 hash;只要组成 entry 的模块文件没有变化,则对应的 hash 也是不变的,所以一般项目优化时,会将公共库代码拆分到一起,因为公共库代码变动较少的,使用 chunkhash 可以发挥最长缓存的作用。
publicPath
对于使用<script> 和 <link>标签时,当文件路径不同于他们的本地磁盘路径(由output.path指定)时,output.publicPath 被用来作为 src 或者 link 指向该文件。这种做法在需要将静态文件放在不同的域名或者 CDN 上面的时候是很有用的。
module.exports = {
output: {
path: '/home/git/public/assets',
publicPath: '/assets/'
}
};
// 输出:
<head>
<link href="/assets/logo.png" />
</head>
library / libraryTarget
如果我们打包的目的是生成一个供别人使用的库,那么可以使用library来指定库的名称,可以使用libraryTarget指定库打包出来的规范。
库的名称支持占位符和普通字符串。
libraryTarget 的值可以为:var、assign、this、window、global、commonjs、commonjs2、commonjs-module、amd、umd、umd2、jsonp,默认是var。
// var
// 这种导出方式,只能以 <script> 标签的形式引入我们的库,以全局变量的形式提供这些被依赖的模块
{
output: {
library: 'myLib',
filename: 'var.js',
libraryTarget: 'var'
}
}
// output
var myLib = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// assign
{
output: {
library: 'myLib',
filename: 'assign.js',
libraryTarget: 'assign'
}
}
// output: 变量没有 var 声明
myLib = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// this
{
output: {
library: 'myLib',
filename: 'this.js',
libraryTarget: 'this'
}
}
// output
this["myLib"] = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// window
{
output: {
library: 'myLib',
filename: 'window.js',
libraryTarget: 'window'
}
}
// output
window["myLib"] = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// global
{
output: {
library: 'myLib',
filename: 'global.js',
libraryTarget: 'global'
}
}
// output:注意 target=node 的时候才是 global,默认 target=web下global 为 window
window["myLib"] = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// commonjs
{
output: {
library: 'myLib',
filename: 'commonjs.js',
libraryTarget: 'commonjs'
}
}
// output
exports["myLib"] = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// amd
{
output: {
library: 'myLib',
filename: 'amd.js',
libraryTarget: 'amd'
}
}
// output
define('myLib', [], function() {
return (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
});
// umd
{
output: {
library: 'myLib',
filename: 'umd.js',
libraryTarget: 'umd'
}
}
// output
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object') module.exports = factory();
else if (typeof define === 'function' && define.amd) define([], factory);
else if (typeof exports === 'object') exports['myLib'] = factory();
else root['myLib'] = factory();
})(window, function() {
return (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
});
// commonjs2
{
output: {
library: 'myLib',
filename: 'commonjs2.js',
libraryTarget: 'commonjs2'
}
}
// output
module.exports = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// umd2
{
output: {
library: 'myLib',
filename: 'umd2.js',
libraryTarget: 'umd2'
}
}
// output
(function webpackUniversalModuleDefinition(root, factory) {
if (typeof exports === 'object' && typeof module === 'object') module.exports = factory();
else if (typeof define === 'function' && define.amd) define([], factory);
else if (typeof exports === 'object') exports['myLib'] = factory();
else root['myLib'] = factory();
})(window, function() {
return (function(modules) {})({
'./src/index.js': function(module, exports) {
}
});
});
// commonjs-module
{
output: {
library: 'myLib',
filename: 'commonjs-module.js',
libraryTarget: 'commonjs-module'
}
}
// output
module.exports = (function(modules) {})({
'./src/index.js': function(module, exports) {}
});
// jsonp
{
output: {
library: 'myLib',
filename: 'jsonp.js',
libraryTarget: 'jsonp'
}
}
// output
myLib((function(modules) {})({
'./src/index.js': function(module, exports) {}
}));
devtool
devtool是来控制怎么显示 sourcemap,通过 sourcemap 我们可以快速还原代码的错误位置。
由于 sourcemap 包含的数据量较大,而且生成算法需要计算量支持,所以 sourcemap 的生成会消耗打包的时间,下面的表格整理了不同的devtool值对应不同的 sourcemap 类型对应打包速度和特点。
| devtool | 构建速度 | 重新构建速度 | 生产环境 | 品质 |
|---|---|---|---|---|
| 留空,none | +++ | +++ | yes | 打包后的代码 |
| eval | +++ | +++ | no | 生成后的代码 |
| cheap-eval-source-map | + | ++ | no | 转换过的代码(仅限行) |
| cheap-module-eval-source-map | 0 | ++ | no | 原始源代码(仅限行) |
| eval-source-map | - | + | no | 原始源代码 |
| cheap-source-map | + | 0 | no | 转换过的代码(仅限行) |
| cheap-module-source-map | 0 | - | no | 原始源代码(仅限行) |
| inline-cheap-source-map | + | 0 | no | 转换过的代码(仅限行) |
| inline-cheap-module-source-map | 0 | - | no | 原始源代码(仅限行) |
| source-map | -- | -- | yes | 原始源代码 |
| inline-source-map | -- | -- | no | 原始源代码 |
| hidden-source-map | -- | -- | yes | 原始源代码 |
| nosources-source-map | -- | -- | yes | 原始源代码 |
[注:]+++ 非常快速, ++ 快速, + 比较快, 0 中等, - 比较慢, -- 慢。
3. resolve
通过 resolve 的配置,可以帮助 Webpack 快速查找依赖,也可以替换对应的依赖(比如开发环境用 dev 版本的 lib 等)。resolve 的基本配置语法如下:
module.exports = {
resolve: {
// resolve的配置
}
};
extensions
resolve.extensions 是帮助 Webpack 解析扩展名的配置,默认值:['.wasm', '.mjs', '.js', '.json'],所以我们引入 js 和 json 文件,可以不写它们的扩展名,通常我们可以加上 .css、.less等,但是要确保同一个目录下面没有重名的 css 或者 js 文件,如果存在的话,还是写全路径吧。
module.exports = {
resolve: {
extensions: ['.js', '.json', '.css']
}
};
alias
通过设置 alias 可以帮助 webpack 更快查找模块依赖,而且也能使我们编写代码更加简便。
alias 的名字可以使用@ ! ~等这些特殊字符,实际使用中 alias 都使用一种,或者不同类型使用一种,这样可以跟正常的模块引入区分开,增加辨识度。
在 src/pages/demo/index.js 中如果要引用 src/lib/utils.js 那么可以通过:import utils from '../../lib/utils'; ,如果目录更深一些,会越来越难看,这时可以通过设置 alias 来缩短这种写法,例如:
module.exports = {
resolve: {
alias: {
src: path.resolve(__dirname, 'src'),
'@lib': path.resolve(__dirname, 'src/lib')
}
}
};
那么可以通过 import utils from '@lib/utils' 直接引入 src/lib/utils.js。
alias 还常被用于给生产环境和开发环境配置不同的 lib 库:
module.exports = {
resolve: {
alias: {
san: process.env.NODE_ENV === 'production' ? 'san/dist/san.min.js' : 'san/dist/san.dev.js'
}
}
};
alias 还支持在名称末尾添加$符号来缩小范围只命中以关键字结尾的导入语句,这样可以做精准匹配:
module.exports = {
resolve: {
alias: {
react$: '/path/to/react.min.js'
}
}
};
import react from 'react'; // 精确匹配,所以 react.min.js 被解析和导入
import file from 'react/file.js'; // 非精确匹配,触发普通解析
mainFields
有一些我们用到的模块会针对不同宿主环境提供几份代码,例如提供 ES5 和 ES6 的两份代码,或者提供浏览器环境和 nodejs 环境两份代码,这时候在package.json文件里会做如下配置:
{
"jsnext:main": "es/index.js", //采用ES6语法的代码入口文件
"main": "lib/index.js", //采用ES5语法的代码入口文件,node
"browser": "lib/web.js" //这个是专门给浏览器用的版本
}
在 Webpack 中,会根据resolve.mainFields的设置去决定使用哪个版本的模块代码,在不同的target下对应的resolve.mainFields默认值不同,默认target=web对应的默认值为:
module.exports = {
resolve: {
mainFields: ['browser', 'module', 'main']
}
};
resolve 的其他配置:
| 配置 | 说明 |
|---|---|
| mainFiles | 解析目录时候的文件名,默认是 index(后缀为resolve.extensions值) |
| modules | 要查找的模块依赖,默认是node_modules |
| symlinks | 是否解析符合链接 |
| plugins | 添加解析插件,数组格式 |
| cachePredicate | 是否缓存,支持 boolean 和 function,function 传入一个带有 path 和 require 的对象,必须返回 boolean 值 |
4. module
在 webpack 解析模块的同时,不同的模块需要使用不同类型的模块处理器来处理,这部分的设置就在module配置中。module 有两个配置:module.noParse和module.rules。
noParse
防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件中不应该含有 import, require, define 的调用,或任何其他导入机制。忽略大型的 library 可以提高构建性能。
module.exports = {
module: {
// 使用正则表达式
noParse: /jquery|lodash/
// 使用函数,从 Webpack 3.0.0 开始支持
noParse: (content) => {
// content 代表一个模块的文件路径
// 返回 true or false
return /jquery|lodash/.test(content);
}
}
}
[注:]parser 来控制模块化语法
webpack 是以模块化的 JavaScript 文件为入口,所以内置了对模块化 JavaScript 的解析功能,支持AMD、Commonjs、SystemJs、ES6。
parser 属性可以更细粒度的配置哪些模块语法要解析,哪些不解析。简单来说,如果设置 parser.commonjs = false,那么代码里面使用 commonjs 的 require 语法引入模块,对应的模块就不会被解析到依赖中,也不会被处理,支持的选项包括:
module: {
rules: [{
test: /\.js$/,
use: ['babel-loader'],
parser: {
amd: false, // 禁用 AMD
commonjs: false, // 禁用 CommonJS
system: false, // 禁用 SystemJS
harmony: false, // 禁用 ES6 import/export
requireInclude: false, // 禁用 require.include
requireEnsure: false, // 禁用 require.ensure
requireContext: false, // 禁用 require.context
browserify: false, // 禁用 browserify
requireJs: false, // 禁用 requirejs
}
}]
}
rules
在处理模块时,将符合规则条件的模块,提交给对应的处理器来处理,通常用来配置 loader,其类型是一个数组,数组里每一项都描述了如何去处理部分文件。
每一项 rule 大致可以由以下三部分组成:
- 条件匹配:通过 test、include、exclude 等配置来命中可以应用规则的模块文件
- 应用规则:对匹配条件通过后的模块,使用use配置项来应用loader
- 重置顺序:一组 loader 的执行顺序默认是从后到前(或者从右到左)执行,通过 enforce 选项可以让其中一个 loader 的执行顺序放到最前(pre)或者是最后(post)
条件匹配相关的配置有test、include、exclude、resource、resourceQuery和issuer。条件匹配的对象包括三类:resource,resourceQuery和issuer。
- resource:请求文件的绝对路径。它已经根据 resolve 规则解析;
- issuer: 被请求资源(requested the resource)的模块文件的绝对路径,即导入时的位置。
举例来说明:从 app.js 导入 './style.css?inline':resource 是/path/to/style.css;resourceQuery 是?之后的inline;issuer 是/path/to/app.js。
// 匹配的条件为:来自src和test文件夹,
// 不包含node_modules和bower_modules子目录
// 模块的文件路径为.tsx和.jsx结尾的文件
{
test: [/\.jsx?$/, /\.tsx?$/],
include: [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, 'test')
],
exclude: [
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, 'bower_modules')
]
}
5. Loader
在使用对应的loader之前,需要先安装它。
配置方式:
// webpack.config.js
module.exports = {
module:{
rules:[
// test: /\.html$/, use: ['html-loader']
test: /\.less$/, use:'less-loader'
]
}
}
// config内写法,通过 options 传入
module: {
rules: [{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: true,
removeComments: false,
collapseWhitespace: false
}
}]
}]
}
// config内写法,通过 query 传入
module: {
rules: [{
test: /\.html$/,
use: [ {
loader: 'html-loader?minimize=true&removeComments=false&collapseWhitespace=false',
}]
}]
}
// 内联配置
const html = require('html-loader!./loader.html');
console.log(html);
loader 解析顺序是从右到左、从后到前的,如果需要调整执行顺序,可以使用 enforce,enforce 取值是 pre|post,pre 表示把放到最前,post 是放到最后。
// query 写法从右到左,使用!隔开
const styles = require('css-loader!less-loader!./src/index.less');
// 数组写法,从后到前
module.exports = {
module: {
rules: [
{
test: /\.less$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'less-loader',
enforce: 'post'
}
]
}
]
}
};
oneOf:只应用第一个匹配的规则
oneOf表示对该资源只应用第一个匹配的规则,一般结合resourceQuery。
module.exports = {
//...
module: {
rules: [
{
test: /\.css$/,
oneOf: [
{
resourceQuery: /inline/, // foo.css?inline
use: 'url-loader'
},
{
resourceQuery: /external/, // foo.css?external
use: 'file-loader'
}
]
}
]
}
};
6. plugin 插件
loader 面向的是解决某个或者某类模块的问题,而 plugin 面向的是项目整体。plugin可以解决loader解决不了的问题。
// 内置插件
module.exports = {
//....
plugins: [
// 压缩js
new webpack.optimize.UglifyJsPlugin();
]
}
// 非内置插件
const ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
//....
plugins: [
// 导出css文件到单独的内容
new ExtractTextPlugin({
filename: 'style.css'
})
]
};
记忆题: Webpack 的配置有几种写法,分别可以应用到什么场景?
我们要开发一个 jQuery 插件、Vue 组件等,需要怎么配置 Webpack?
Webpack 的占位符 [hash] 、[chunkhash] 和 [contenthash] 有什么区别和联系?最佳实践是什么?
Webpack 的 SourceMap 有几种形式?分别有什么特点?SourceMap 配置的最佳实践是什么?
什么是 bundle ,什么是 chunk,什么是 module?
能不能手写一个 Webpack 配置?记住重点配置项:entry、output、module.rules(loader)和plugin。
在 JS 文件中怎么调用 Loader 来处理一个模块?
Loader 的解析顺序是怎样的?