插件(plugins
)
插件是
webpack
的支柱功能。插件目的在于解决loader
无法实现的其他事。
webpack
插件是一个具有 apply
属性的 JavaScript
对象。apply
属性会被 webpack compiler
调用,并且 compiler
对象可在整个编译生命周期访问。如ConsoleLogOnBuildWebpackPlugin.js
:
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
//compiler hook 的 tap 方法的第一个参数,
//应该是驼峰式命名的插件名称
compiler.hooks.run.tap(pluginName, compilation => {
console.log("webpack 构建过程开始!");
});
}
}
一些插件简介:
Name | Description |
---|---|
AggressiveSplittingPlugin | 将原来的 chunk 分成更小的 chunk |
BabelMinifyWebpackPlugin | 使用 babel-minify进行压缩 |
BannerPlugin | 在每个生成的 chunk 顶部添加 banner |
CommonsChunkPlugin | 提取 chunks 之间共享的通用模块 |
CompressionWebpackPlugin | 预先准备的资源压缩版本,使用 Content-Encoding 提供访问服务 |
ContextReplacementPlugin | 重写 require 表达式的推断上下文 |
CopyWebpackPlugin | 将单个文件或整个目录复制到构建目录 |
DefinePlugin | 允许在编译时(compile time)配置的全局常量 |
DllPlugin | 为了极大减少构建时间,进行分离打包 |
EnvironmentPlugin | DefinePlugin 中 process.env 键的简写方式。 |
ExtractTextWebpackPlugin | 从 bundle 中提取文本(CSS)到单独的文件 |
HotModuleReplacementPlugin | 启用模块热替换(Enable Hot Module Replacement - HMR) |
HtmlWebpackPlugin | 简单创建 HTML 文件,用于服务器访问 |
I18nWebpackPlugin | 为 bundle 增加国际化支持 |
IgnorePlugin | 从 bundle 中排除某些模块 |
LimitChunkCountPlugin | 设置 chunk 的最小/最大限制,以微调和控制 chunk |
LoaderOptionsPlugin | 用于从 webpack 1 迁移到 webpack 2 |
MinChunkSizePlugin | 确保 chunk 大小超过指定限制 |
NoEmitOnErrorsPlugin | 在输出阶段时,遇到编译错误跳过 |
NormalModuleReplacementPlugin | 替换与正则表达式匹配的资源 |
NpmInstallWebpackPlugin | 在开发时自动安装缺少的依赖 |
ProvidePlugin | 不必通过 import/require 使用模块 |
SourceMapDevToolPlugin | 对 source map进行更细粒度的控制 |
EvalSourceMapDevToolPlugin | 对 eval source map 进行更细粒度的控制 |
UglifyjsWebpackPlugin | 可以控制项目中 UglifyJS 的版本 |
ZopfliWebpackPlugin | 通过 node-zopfli 将资源预先压缩的版本 |
更多第三方插件,请查看 awesome-webpack 列表。
用法案例:CommonsChunkPlugin
image
The CommonsChunkPlugin
已经从webpack v4 legato
中移除。想要了解在最新版本中如何处理chunk
,请查看SplitChunksPlugin
(该插件配置过程更为便捷高效)。
以
element-ui
初始化工程为例看下vendor.js
:
import Vue from 'vue'
import ElementUI from 'element-ui'
由于插件可以携带参数/选项,你必须在 webpack
配置中,向 plugins
属性传入 new
实例。根据你的 webpack
用法,这里有多种方式使用插件。
//package.json中引入依赖
"devDependencies": {
...
"html-webpack-plugin": "^2.24.1",
"webpack": "^2.4.1",
...
}
//webpack.config.js中引入
//通过 npm 直接安装插件
const HtmlWebpackPlugin
= require('html-webpack-plugin');
//访问webpack内置的插件
//CommonsChunkPlugin就是内置插件之一
const webpack = require('webpack');
image功能:该插件用于提取
chunks
之间共享的通用模块。使用CommonsChunkPlugin
从「应用程序bundle
」中提取vendor 引用
(vendor reference
) 到vendor bundle
,并把引用vendor
的部分替换为__webpack_require__()
调用
Node API
(O(∩_∩)O~,这个是啥)
「
TODO
」即便使用Node API
,用户也应该在配置中传入plugins
属性。compiler.apply
并不是推荐的使用方式。
//some-node-script.js
//访问 webpack 运行时(runtime)
const webpack = require('webpack');
const configuration = require('./webpack.config.js');
let compiler = webpack(configuration);
compiler.apply(new webpack.ProgressPlugin());
compiler.run(function(err, stats) {
// ...
});
你知道吗:以上看到的示例和
webpack
自身运行时(runtime
) 极其类似。wepback
源码中隐藏有大量使用示例,你可以用在自己的配置和脚本中。
官网插件
HtmlWebpackPlugin
根据配置的入口和出口,自动生成
index.html
,且会自动引入相关的依赖。
- 如果你想要了解更多
HtmlWebpackPlugin
插件提供的全部功能和选项,那么你就应该多多熟悉HtmlWebpackPlugin
仓库。 - 你还可以看一下
html-webpack-template
,除了默认模板之外,还提供了一些额外的功能。
CleanWebpackPlugin
- 你可能已经注意到,由于过去的指南和代码示例遗留下来,导致我们的
/dist
文件夹相当杂乱。 -
webpack
会生成文件,然后将这些文件放置在/dist
文件夹中,但是webpack
无法追踪到哪些文件是实际在项目中用到的。 - 通常,在每次构建前清理
/dist
文件夹,是比较推荐的做法,因此只会生成用到的文件。让我们完成这个需求。 -
clean-webpack-plugin
是一个比较普及的管理插件,让我们安装和配置下。
npm install clean-webpack-plugin --save-dev
-
webpack.config.js
中配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
+ const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
entry: {
app: './src/index.js',
print: './src/print.js'
},
plugins: [
+ new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
title: 'Output Management'
})
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
- 现在执行
npm run build
,再检查/dist
文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!
WebpackManifestPlugin
该插件能够在项目根目录生成一份
manifest.json
的文件,通过该文件的映射关系可以让我们知道webpack
是如何追踪所有模块并映射到输出bundle
中的。
//安装插件:
npm install --save-dev webpack-manifest-plugin
//在 webpack.config.js中引入,大致如下:
var ManifestPlugin = require('webpack-manifest-plugin');
module.exports = {
// ...
plugins: [
new ManifestPlugin()
// ManifestPlugin方法可以接受一些选项参数options,如
// new ManifestPlugin(options)
]
};
//3.可选参数如下:
options.fileName 表示要生成文件的名称,默认为manifest.json
options.publicPath 表示生成映射文件的路径,默认为output.publicPath
还有其他的参数见官网
https://github.com/danethurber/webpack-manifest-plugin
HotModuleReplacementPlugin
image开发环境启用
HMR
:启用此功能实际上相当简单。而我们要做的,就是更新webpack-dev-server
的配置,和使用webpack
内置的HMR
插件
IgnorePlugin
import
或require
调用时,忽略以下正则表达式匹配的模块:
// requestRegExp 匹配(test)资源请求路径的正则表达式。
// contextRegExp (可选)匹配(test)资源上下文(目录)的正则表达式。
new webpack.IgnorePlugin(
requestRegExp, [contextRegExp]
)
举例:moment
包
比如我们要使用moment
这个第三方依赖库,该库主要是对时间进行格式化,并且支持多个国家语言。
import moment from 'moment'
//设置语言
moment.locale('zh-cn');
let r = moment().endOf('day').fromNow();
console.log(r);
- 这样打印出来的就是中文的时间,因为我在使用这个
API
的时候,前面设置了语言类型moment.locale
为中文zh-cn
。 - 但是,虽然我设置了语言为中文,但是在打包的时候,是会将所有语言都打包进去的。这样就导致包很大,打包速度又慢
- 所以,最好能够少打包一些没用的依赖目录进去
- 而
moment
的包含./locale/
该字段路径的文件目录就是各国语言的目录,比如./locale/zh-cn
就是中文语言 - 使用
IgnorePlugin
let webpack = require('webpack');
plugins:[
//moment这个库中,如果引用了./locale/目录的内容,
//就忽略掉,不会打包进去
new webpack.IgnorePlugin(/\.\/locale/,/moment/),
]
- 我们虽然按照上面的方法忽略了包含
./locale/
该字段路径的文件目录,但是也使得我们使用的时候不能显示中文语言了,所以这个时候可以手动引入中文语言的目录
import moment from 'moment'
//手动引入所需要的语言包
import 'moment/locale/zh-cn';
moment.locale('zh-cn');
let r = moment().endOf('day').fromNow();
console.log(r);
- 这样就OK了。既能够显示中文,又把不必要的语言包都忽略打包了
DllPlugin
DLLPlugin
和DLLReferencePlugin
用某种方法实现了拆分bundles
,同时还大大提升了构建的速度(dll
是一种最简单粗暴并且极其有效的优化方式)。
- 用
Webpack
打包的时候,对于一些不经常更新的第三方库,比如react
,lodash
,我们希望能和自己的代码分离开,Webpack
社区有两种方案
CommonsChunkPlugin
和DLLPlugin
- 对于
CommonsChunkPlugin
:-
webpack
每次打包实际还是需要去处理这些第三方库,只是打包完之后,能把第三方库和我们自己的代码分开。 - 且每次工程重新打包,
verdors
也会重新打包,打包的chunkhash
就会改变,导致每次发布,vendors
的缓存都会失效。这样增加了用户的流量消耗和首屏加载时间。
-
- 而
DLLPlugin
则是能把第三方代码完全分离开,即每次只打包项目自身的代码。引入的第三方包没有改变就不需要重新打包。 DLLPlugin
具体用法:
/**
要使用 DLLPlugin,需要额外新建一个配置文件。
所以对于用这种方式打包的项目,
一般会有下面两个配置文件:
webpack.config.js
webpack.dll.config.js
先来看下 webpack.dll.config.js
*/
const webpack = require('webpack')
const library = '[name]_lib'
const path = require('path')
module.exports = {
entry: {
vendors: ['react', 'lodash']
//上面的vendors,看到有人有的libs
},
output: {
filename: '[name].dll.js',
path: 'dist/',
library
},
plugins: [
new webpack.DllPlugin({
//执行的上下文环境,对之后DllReferencePlugin有用
context: __dirname,
path: path.join(
__dirname, 'dist/[name]-manifest.json'
),
// This must match the output.library option above
name: library
}),
],
}
//再改下 webpack.config.js 文件
const webpack = require('webpack')
module.exports = {
entry: {
app: './src/index'
},
output: {
filename: 'app.bundle.js',
path: 'dist/',
},
plugins: [
new webpack.DllReferencePlugin({
context: __dirname,
//下面的vendors有人用libs
manifest: require('./dist/vendors-manifest.json')
})
]
}
/**
manifest: require('./dist/vendors-manifest.json')
这里的路径要和 webpack.dll.config.js 里面的对应。
然后运行:
*/
$ webpack --config webpack.dll.config.js
$ webpack --config webpack.config.js
//然后你的 html 文件像下面这样引用
<script src="/dist/vendors.dll.js"></script>
<script src="/dist/app.bundle.js"></script>
/**
上面的用法也存在一些不方便的地方,
比如在 webpack.config.js 中要明确
指出对应的 manifest.json 文件。
还有当 DLL 需要更新的时候,
比如 react 升级了,或者加入新的第三方库,
都需要手动像下面这样编译一次。
*/
$ webpack --config webpack.dll.config.js
/**
---非官方插件(不需关注,没有缓存作用了吧?)----
因为上面这些问题,
所以基于官方的 DllReferencePlugin,
有一个 Dll Link Plugin。链接如下:
www.npmjs.com/package/dll-link-webpack-plugin
使用这个插件,
只需要对 webpack.config.js 作下小小的改动
*/
const webpack = require('webpack')
const DllLinkPlugin = require('dll-link-webpack-plugin')
module.exports = {
// ...
plugins: [
new DllLinkPlugin({
config: require('webpack.dll.config.js')
})
]
}
/**
直接替换掉 DllReferencePlugin,
然后传入对应的 DLL 配置文件就可以了。
每次打包的时候,只需要运行
*/
$ webpack --config webpack.config.js
/**
上面的命令便会自动生成对应的 vendors 文件,
需要更新的时候,也会自动更新。
*/
SplitChunksPlugin
-
在默认情况下,
SplitChunksPlugin
仅仅影响按需加载的代码块,因为更改初始块会影响HTML文件应包含的脚本标记以运行项目。 -
webpack
将根据以下条件自动拆分代码块:- 会被共享的代码块或者 node_mudules 文件夹中的代码块
- 体积大于30KB的代码块(在gz压缩前)
- 按需加载代码块时的并行请求数量不超过5个
- 加载初始页面时的并行请求数量不超过3个
//举例: // 文件一:impvue.js import 'vue' ... // 文件二:index.js // 动态加载 impvue.js import('./impvue') ... /** 打包之后的结果会创建一个包含 vue 的独立代码块, 当包含 impvue.js 的原始代码块被调用时, 这个独立代码块会并行请求进来。 原因: 1) vue 来自 node_modules 文件夹 2) vue 体积超过30KB 3) 导入调用时的并行请求数为2 4) 不影响页面初始加载 */
-
我们这样做的原因是因为,
vue
代码并不像你的业务代码那样经常变动,把它单独提取出来就可以和你的业务代码分开缓存,极大的提高效率。 -
SplitChunksPlugin
的默认配置
splitChunks: {
/**
chunks属性用来选择分割哪些代码块,
可选值有:
'all'(所有代码块)
'async'(按需加载的代码块)
'initial'(初始化代码块)
*/
chunks: "async",
minSize: 30000, // 模块的最小体积
minChunks: 1, // 模块的最小被引用次数
maxAsyncRequests: 5, // 按需加载的最大并行请求数
maxInitialRequests: 3, // 一个入口最大并行请求数
automaticNameDelimiter: '~', // 文件名的连接符
name: true,
/**
缓存组是个有趣的功能:
在默认设置中,
会将`node_mudules`中的模块打包进`vendors`,
引用超过两次的模块分配到`default` `bundle` 中。
更可以通过 `priority` 来设置优先级。
*/
cacheGroups: { // 缓存组
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
- 在项目中添加
SplitChunksPlugin
/**
为了方便演示,我们先安装两个类库: lodash 和 axios,
npm i lodash axios -S
修改 main.js,引入 lodash 和axios 并调用相应方法:
*/
import Modal from './components/modal/modal'
import './assets/style/common.less'
import _ from 'lodash'
import axios from 'axios'
const App = function () {
let div = document.createElement('div')
div.setAttribute('id', 'app')
document.body.appendChild(div)
let dom = document.getElementById('app')
let modal = new Modal()
dom.innerHTML = modal.template({
title: '标题',
content: '内容',
footer: '底部'
})
}
const app = new App()
console.log(_.camelCase('Foo Bar'))
axios.get('aaa')
/**
-----------------------------
使用SplitChunksPlugin不需要安装任何依赖,
只需在 webpack.config.js 中的 config对象
添加 optimization 属性:
*/
optimization: {
splitChunks: {
chunks: 'initial',
automaticNameDelimiter: '.',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: 1
}
}
},
//一般都配合runtimeChunkPlugin使用
runtimeChunk: {
name: entrypoint => `manifest.${entrypoint.name}`
}
}
/**
配置 runtimeChunk 会给每个入口添加一个只包含runtime的额外的代码块,
name 的值也可以是字符串,不过这样就会给每个入口添加相同的 runtime,
配置为函数时,返回当前的entry对象,即可分入口设置不同的runtime。
*/
/**
我们再安装一个 webpack-bundle-analyzer,
这个插件会清晰的展示出打包后的各个bundle所依赖的模块:
引入:
*/
const BundleAnalyzerPlugin =
require('webpack-bundle-analyzer').BundleAnalyzerPlugin
//使用,在plugins数组中添加即可:
new BundleAnalyzerPlugin()
- 可以看看
Webpack4
之SplitChunksPlugin
这里提及的问题
BannerPlugin
为每个
chunk
文件头部添加banner
(注释/版权)。
new webpack.BannerPlugin(options)
//options选项:
{
banner: string, // 其值为字符串,将作为注释存在
raw: boolean, // 如果值为 true,将直出,不会被作为注释
entryOnly: boolean, // 如果值为 true,将只在入口 chunks 文件中添加
test: string | RegExp | Array,
include: string | RegExp | Array,
exclude: string | RegExp | Array,
}
CopyWebpackPlugin
:拷贝静态文件
将单个文件或整个目录直接从源目录拷贝到构建目录.
//config:配置了一些参数
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
},
//上面的static目录先不管,
//我们先看这个,文件中是一些客户可自行修改的配置
//独立出来,方便修改
{
from: path.resolve(config.directory.root, 'sysconfig.js'),
to: 'sysconfig.js'
},
])
打包好的工程下:
image
image
ExtractTextWebpackPlugin
:分离CSS
文件
MiniCssExtractPlugin
这个呢?
const ExtractTextPlugin
= require("extract-text-webpack-plugin");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new ExtractTextPlugin("styles.css"),
/**
我看我们的工程中是这样配置的
具体的后续再好好看下:
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
allChunks: true,
}),
*/
]
}
它会将所有的入口
chunk(entry chunks)
中引用的*.css
,移动到独立分离的CSS
文件。因此,你的样式将不再内嵌到JS bundle
中,而是会放到一个单独的CSS
文件(即styles.css
)当中。 如果你的样式文件大小较大,这会做更快提前加载,因为CSS bundle
会跟JS bundle
并行加载。
再来看个CSS优化
(压缩)相关的另一个插件optimize-css-assets-webpack-plugin
:
/**这个插件可以接受下列配置(均为可选):
assetNameRegExp: 正则,用于匹配需要优化的资源名。默认值是 /\.css$/g
cssProcessor: 用于压缩和优化CSS 的处理器,默认是 cssnano.这是一个函数,
应该按照 cssnano.process 接口(接受一个CSS和options参数,返回一个Promise)
canPrint: {bool} 表示插件能够在console中打印信息,默认值是true
*/
// webpack4+
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// webpack4
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourcMap: true
}),
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.optimize\.css$/g,
cssProcessor: require('cssnano'),
cssProcessorOptions: { safe: true, discardComments: { removeAll: true } },
canPrint: true
}),
],
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
}),
]
};
//webpack3中一般配合 ExtractTextPlugin一起使用。
UglifyjsWebpackPlugin
此插件使用
uglify-js
进行js
文件的压缩。
- 下面说明是来自:关闭开发环境的代码压缩
UglifyJsPlugin
- 开发环境是不需要去压缩代码,主要是因为太耗性能了,每修改一个地方就要花几秒去等待页面渲染,非常浪费开发时间,解决办法就是配置不同的环境变量
- 去在开发环境的时候不要这个
UglifyJsPlugin
插件,为此,webpack4
又增加了新的Mode
,且默认值是production
;而且更新后的webpack
默认是有UglifyJsPlugin
这个配置的 - 也就是说在不设置任何环境变量的情况下,始终会有压缩代码的行为出现,导致编译极其耗时,那我的解决办法就是在
package.json
文件启动时设置环境变量:
image
Tree Shaking
:一个术语,通常用于描述移除JavaScript
上下文中的未引用代码(dead-code)
。新的webpack 4
正式版本,扩展了这个检测能力,通过package.json
的sideEffects
属性作为标记,向compiler
提供提示,表明项目中的哪些文件是pure(纯的 ES2015 模块)
,由此可以安全地删除文件中未使用的部分。
- 这种方式是通过
package.json
的sideEffects
属性来实现的。
{
"name": "your-project",
"sideEffects": false
}
- 如果你的代码确实有一些副作用,那么可以改为提供一个数组:
{
"name": "your-project",
"sideEffects": [
"./src/some-side-effectful-file.js"
]
}
- 从
webpack 4
开始,也可以通过mode
配置选项轻松切换到压缩输出,只需设置为production
。 - 配置
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
mode: "production"
};
DefinePlugin
DefinePlugin
允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用。如果在开发构建中,而不在发布构建中执行日志记录,则可以使用全局常量来决定是否记录日志。这就是DefinePlugin
的用处,设置它,就可以忘记开发和发布构建的规则。
new webpack.DefinePlugin({
/**
在vue-cli创建的项目中,
凡是src下的文件,都可以访问到下面这些变量,如VERSION,
例如main.js,App.vue等等
*/
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
//根据process.env.NODE_ENV区分环境,引入配置文件
process.env: {NODE_ENV: "development"},
})
EnvironmentPlugin
: 是一个通过DefinePlugin
来设置process.env
环境变量的快捷方式。
new webpack.EnvironmentPlugin(['NODE_ENV', 'DEBUG'])
//上面的写法和下面这样使用 DefinePlugin 的效果相同:
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
'process.env.DEBUG': JSON.stringify(process.env.DEBUG)
})
HashedModuleIdsPlugin
该插件会根据模块的相对路径生成一个四位数的
hash
作为模块id
, 建议用于生产环境。
//其他具体说明再看看
new webpack.HashedModuleIdsPlugin({
hashFunction: 'sha256',
hashDigest: 'hex',
hashDigestLength: 20
})
ZipPlugin
(好像官网没列)
发布的时候可以使用 压缩插件将资源(图片,配置文件等)压缩成一个
.zip
文件
//参数中配置了需要将工程打包
if (config.build.productionToZip) {
const ZipPlugin = require('zip-webpack-plugin')
webpackConfig.plugins.push(
new ZipPlugin({
path: config.build.assetsZipRoot,
filename: 'build.zip',
extension: 'zip',
})
)
}
ModuleConcatenationPlugin
-
webpack2
处理后的每个模块均被一个函数包裹,如下:
/* 50 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
window.lib = {}
...
/* harmony default export */ __webpack_exports__["a"] = (window.lib);
/***/ }),
- 这样会带来一个问题:降低浏览器中
JS
执行效率,这主要是闭包函数降低了JS
引擎解析速度。 - 于是webpack团队参考
Closure Compiler
和Rollup JS
,将一些有联系的模块,放到一个闭包函数里面去,通过减少闭包函数数量从而加快JS
的执行速度。 -
webpack3
通过设置ModuleConcatenationPlugin
使用这个新特性:
module.exports = {
plugins: [
new webpack.optimize.ModuleConcatenationPlugin()
]
}
- 记住,此插件仅适用于由
webpack
直接处理的ES6
模块。在使用转译器(transpiler
)时,你需要禁用对模块的处理(例如Babel
中的modules
选项)。
NpmInstallWebpackPlugin
在开发时自动安装缺少的依赖:
package.json
中需要配置了才会自动安装哦
//安装
npm install --save-dev npm-install-webpack-plugin
//用法
//不要忘记引入哦
const NpmInstallPlugin = require('npm-install-webpack-plugin')
//插件配置:
plugins: [
new NpmInstallPlugin()
],
//相当于:
plugins: [
new NpmInstallPlugin({
// 使用 --save 或者 --save-dev
dev: false,
// 安装缺少的 peerDependencies
peerDependencies: true,
// 减少控制台日志记录的数量
quiet: false,
// npm command used inside company, yarn is not supported yet
npm: 'tnpm'
});
],
//可以提供一个 Function 来动态设置 dev:
plugins: [
new NpmInstallPlugin({
dev: function(module, path) {
return [
"babel-preset-react-hmre",
"webpack-dev-middleware",
"webpack-hot-middleware",
].indexOf(module) !== -1;
},
}),
],
ProvidePlugin
自动加载模块,而不必到处
import
或require
。
//要自动加载 jquery,我们可以将两个变量都指向对应的 node 模块:
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
//然后在我们任意源码中:
$('#item'); // <= 起作用
jQuery('#item'); // <= 起作用
//使用:Lodash Map
new webpack.ProvidePlugin({
_map: ['lodash', 'map']
})
//使用:Vue.js
new webpack.ProvidePlugin({
Vue: ['vue/dist/vue.esm.js', 'default']
})
LimitChunkCountPlugin
当你在编写代码时,可能已经添加了许多代码分离点(
code split point
)来实现按需加载(load stuff on demand
)。在编译完之后,你可能会注意到有一些很小的chunk
- 这产生了大量HTTP
请求开销。幸运的是,此插件可以通过合并的方式,后处理你的chunk
,以减少请求数。
//改善`chunk`传输的插件
//LimitChunkCountPlugin
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 5, // 必须大于或等于 1
minChunkSize: 1000
})
/**MinChunkSizePlugin:
通过合并小于 minChunkSize 大小的 chunk,
将 chunk 体积保持在指定大小限制以上。
*/
new webpack.optimize.MinChunkSizePlugin({
// Minimum number of characters
minChunkSize: 10000
})
/**
AggressiveSplittingPlugin可以将 bundle拆分成更小的chunk,
直到各个 chunk 的大小达到 option 设置的 maxSize。
它通过目录结构将模块组织在一起。
*/
new webpack.optimize.AggressiveSplittingPlugin({
minSize: 30000, // 字节,分割点。默认:30720
maxSize: 50000, // 字节,每个文件最大字节。默认:51200
chunkOverhead: 0, // 默认:0
entryChunkMultiplicator: 1, // 默认:1
})
SourceMapDevToolPlugin
生成source map
的两种配置
- 配置
devtool
//最简单的生成source map的方式是,如下配置webpack.config.js,
const path = require('path');
module.exports = {
devtool: 'source-map',
entry: {
index: path.resolve(__dirname, 'src/index.js'),
},
output: {
devtoolModuleFilenameTemplate: '[resource-path]',
path: path.resolve(__dirname, 'dist/'),
filename: 'index.js',
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
query: { presets: ['@babel/preset-env'] }
}
},
]
},
};
/**
其中,devtool默认值为false,
配置为source-map表示以独立的文件形式生成source map。
因此,dist/ 文件夹下,会产生两个文件,
index.js
index.js.map
index.js文件末尾,webpack会自动添加一行注释,
//# sourceMappingURL=index.js.map
浏览器解析到这里,会自动根据index.js的相对路径,请求map文件并加载它。
*/
SourceMapDevToolPlugin
/**
除了直接配置devtool之外,
还可以使用webpack官方插件SourceMapDevToolPlugin,
进行更细粒度的source map配置。
*/
const path = require('path');
const webpack = require('webpack');
module.exports = {
// devtool: 'source-map',
entry: {
index: path.resolve(__dirname, 'src/index.js'),
},
output: {
// devtoolModuleFilenameTemplate: '[resource-path]',
path: path.resolve(__dirname, 'dist/'),
filename: 'index.js',
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: 'babel-loader',
query: { presets: ['@babel/preset-env'] }
}
},
]
},
plugins: [
new webpack.SourceMapDevToolPlugin({
filename: '[file].map',
moduleFilenameTemplate: '[resource-path]',
append: '\n//# sourceMappingURL=http://127.0.0.1:8080/dist/[url]'
}),
],
};
/**
以上配置中,我们注释掉了
devtool和devtoolModuleFilenameTemplate,
将它们配置到了SourceMapDevToolPlugin中。
这样配置,dist/目录最后也会生成两个文件,
index.js
index.js.map
由于我们使用了该插件的append功能,
修改了sourceMappingURL地址,
因此,index.js末尾source map文件的地址就变成了,
//# sourceMappingURL=http://127.0.0.1:8080/dist/index.js.map
*/
- 注:这里值得一提的是
同时配置devtool和SourceMapDevToolPlugin是不行的,
index.js文件末尾会被添加两行sourceMappingURL,
//# sourceMappingURL=http://127.0.0.1:8080/dist/index.js.map
//# sourceMappingURL=index.js.map
而且map文件的内容也不正确,是一个空的map文件,
-
EvalSourceMapDevToolPlugin
:对 eval source map 进行更细粒度的控制 -
webpack——devtool
里的7
种SourceMap
模式
我们先来看看文档对这7
种模式的解释:
模式 | 解释 |
---|---|
eval | 每个module会封装到 eval 里包裹起来执行,并且会在末尾追加注释 //@ sourceURL. |
source-map | 生成一个SourceMap文件. |
hidden-source-map | 和 source-map 一样,但不会在 bundle 末尾追加注释. |
inline-source-map | 生成一个 DataUrl 形式的 SourceMap 文件. |
eval-source-map | 每个module会通过eval()来执行,并且生成一个DataUrl形式的SourceMap |
cheap-source-map | 生成一个没有列信息(column-mappings)的SourceMaps文件,不包含loader的 sourcemap(譬如 babel 的 sourcemap) |
cheap-module-source-map | 生成一个没有列信息(column-mappings)的SourceMaps文件,同时 loader 的 sourcemap 也被简化为只包含对应行的。 |
- 注1:webpack 不仅支持这 7 种,
而且它们还是可以任意组合上面的eval、inline、hidden关键字,
就如文档所说,你可以设置 souremap 选项为:
cheap-module-inline-source-map。
- 注2:如果你的modules里面已经包含了SourceMaps,
你需要用source-map-loader 来和合并生成一个新的 SourceMaps。
还有很多其他的,需要时自行搜索学习即可
......
网友评论