项目中常用的webpack插件

494 阅读11分钟

本文原创:linqiumei、huoyinghui

不同项目,使用webpack版本不同,对应的插件也不同。

如何查看项目中webpack的版本?

1.在package.json中的script脚本命令中,添加"webpack": "webpack --version",终端运行npm run webpack

2.项目目录的终端运行./node_modules/.bin/webpack –v

3.项目目录的终端运行npx webpack -v

npx会自动查找当前依赖包中的可执行文件,找不到会在PATH中寻找,找不到,就会自动安装。npm5.2起支持npx

webpack -v查询的是全局环境中的webpack版本

项目中webpack配置文件结构

1.webpack_config_node.png

webpack的重要属性

entry: 定义入口文件

output: 定义输出文件

module: 设置项目中不同类型模块的处理方式

resolve: 设置模块解析方式

plugins: 使用插件自定义webpack构建过程

webpack.base.js

2.base_entry_oputput.png

entry:
定义了webpack打包的入口文件
output:
-- path: 定义输出文件存储位置
-- publicPath: 用于配置异步加载/按需加载等线上资源的url前缀,默认为空字符串,为相对路径;const publicPath = path.join('/account/')
-- filename: 定义打包资源输出到path指定目录位置的文件名
其中,[name]指代entry中键值对的键值,[hash]为打包时会生成唯一的标志符,指代打包过程,同一打包过程的文件hash是一样的
-- chunkFilename: chunk是webpack进行模块依赖分析时,代码分割出来的代码块。用于配置没有入口起点的代码块输出的文件名
-- globalObject: 因为默认传入的参数是window,但是umd书写规范中,传入的参数应该是this

3.base_module.png

module:
-- loader在module.rules中配置,如使用vue-loader加载vue文件,使用css-loader加载.css文件。

4.base_resolve.png

resolve:
-- extensions: 如果有多个文件有相同的名字,但是后缀名不同,webpack会按数组顺序解析列在首位的文件,并跳过其他后缀
-- alias: 别名,简化模块引入的代码
-- symlinks: 是否将符号链接解析为真实路径,默认为true

5.base_plugins.png

plugins
-- vueLoaderPlugin: 必须插件,将定义的其他规则应用到.vue文件中相应语言块。
-- HtmlWebpackPlugin: 简化了HTML文件的创建
当使用webpack打包时,HtmlWebpackPlugin创建一个html文件,并把webpack打包后的静态文件自动插入到html文件中。默认会在output.path目录下创建一个index.html文件,并在文件中插入一个script标签,src为output.filename。css资源会包含在html头部的link标记中。
6.base_htmlWebpackPlugin1.png

-- DllReferencePlugin: 配合DllPlugin使用,webpack.dll.js中生成manifest.json文件,DllReferencePlugin打包时,不会打包manifest.json中的文件,而是使用相应的全局函数处理,用于提高构建速度。如第三方库vue、vue-loader等打包
-- DefinePlugin: 可以在编译时定义全局变量,打包的时候对这些变量进行替换

7.base_DefinPlugin.png

webpack.dll.js

将第三方库依赖打包到一个单独的dll文件目录下,并生成manifest.json文件

8.dll.png

optimization
-- minimize: 是否压缩
plugins
-- ProvidePlugin: 每当vue作为自由变量时,模块会自动用已加载vue的内容填充模块
-- CleanWebpackPlugin: 用于npm run dll时,清除之前打包的文件
-- AssetsPlugin: 生成一个记录版本号的文件
-- DllPlugin: 将第三方库代码分离,每次文件更改的时候,只打包项目自身的代码。将vue、vue-router第三方库单独打包

webpack.dev.js

9.dev.png

plugins:
-- HotModuleReplacementPlugin: 热加载模块,不能用于生产环境,因为watch状态消耗性能,生产环境中,代码稳定,更新几率小

webpack.prod.js

10.prod.png

devtool:
控制是否生成以及如何生成source map,有助于定位代码错误位置
performance:
展示性能提示,当加载影响性能的资源时,可以输出一个警告提醒。warning、error、 false
optimization:
-- minimizer: 用于压缩js文件,不支持es6语法
-- uglifyjs-webpack-plugin: 用于压缩js文件,不支持es6语法,可用terser-webpack-plugin替代
-- OptimizeCssAssetsPlugin: 用于优化css文件的输出,摈弃重复样式,去除样式规则中的多余参数,移除不必要的浏览器前缀等。autoprefixer是否去除浏览器前缀
plugins:
-- MiniCssExtractPlugin: 将css提取到单独的文件中,并支持按需加载
-- DefinePlugin: 定义全局变量
-- hashedModuleIdsPlugin: 根据模块的相对路径,生成四位数的hash作为模块id,用于生产环境。
-- CleanWebpackPlugin: 清除打包内容:verbose: 是否将日志输出到控制台,默认为false;dry: 默认为false,为true的时候,模拟删除,不会真的删除文件;exclude: 忽略不用删除的文件
-- CopyWebpackPlugin: 将单个文件或整个目录复制到构建目录中

webpack5


首先可以看一下当前webpack5的开发进度

picture1.png

相较于webpack4,webpack5有了很大的变化,先简单列举一下新旧版本的几个不同点:

  1. webpack5清除webpack4标记即将过期的功能
  2. 通过持久缓存提高构建性能cache
  3. 确定性chunkId和moduleId,优化长期缓存
  4. 更好的Tree Shaking
  5. 支持生成es6/es2015的代码
  6. SplitChunk配置优化

清除webpack4标记即将过期的功能点就先掠过,其余几点下面我们一一介绍。

通过持久缓存提高构建性能

首先说明cache在构建中的作用,cache在首次打包构建项目时,会将编译结构缓存起来,二次构建时,会根据缓存判断当前哪些已经构建过并且没有改动,这些文件就不会被重新编译,从而减少项目构建时间。

在webpack4中,cache是这样设置的:

cache: true // 或false

通过cache-loader将编译结构写入硬盘缓存或者采用babel-loader并配置option.cacheDirectory将编译结果写入磁盘

而在webpack5中对cache做了升级,缓存默认是开启状态,缓存的文件存于内存中,我们也可以配置缓存的类型及存储的位置:

cache: {
    type: 'filesystem’,
    cacheDirectory: path,
    cacheLocation: path + name,
    buildDependencies: {
    config: [__filename],
    },
}

以上几个属性的含义可以简单理解为:

type: 缓存类型,值为 'memory'或‘filesystem’,分别代表基于内存的临时缓存,以及基于文件系统的持久化缓存。

cacheDirectory: 缓存目录,默认目录为 node_modules/.cache/webpack,也可以自定义设置

name: 缓存名称,同时也是 cacheDirectory 中的子目录命名,默认值为 Webpack配置中 的config.name{config.name}-{config.mode}

cacheLocation: 缓存真正的存放地址, path.resolve(cache.cacheDirectory, cache.name), 该属性在赋值情况下将忽略 cacheDirectory 和 name 属性

优化长期缓存

我们都知道,webpack基本配置会有入口文件,类似的设置如下:

entry:{
    app: './index.js'// 入口
},
output:{
    filename:'./bundle.js'// 出口
}

但是有很多文件是不会配置entry的,这些未从entry打包的chunk文件,都会以0,1,2,3…加chunkhash的文件命名方式输出。这样就引发了一些问题,举个例子:

假设我们打包了page0.js、page1.js、page2.js,打包后的文件名暂时称为1.js、2.js、3.js,这样使用是不会有任何问题的,但现在如果我们删除或者暂时不使用1.js后,原来的2.js会变为1.js,3.js会变为2.js,此时再引用2.js就不对了,这就是出现了缓存失效问题。

出现的这种问题,目前有解决方案,就是使用import(/* webpackChunkName: “home” */ ‘./home’)这种魔术式注释来给打包文件命名,虽然命名问题解决了,可是打开文件内容会发现,chunkId仍然发生了变化。

page1.js第一次打包时的chuankId:

14.page_chunkId_1.png

page1.js第二次打包后的chunkId:

15.page_chunkId_2.png

那么webpack5是怎么解决的呢

在webpack5中,有确定的 chunkId、moduleId 以及导出名称,生产模式下,默认是启用chunkIds、 moduleIds的,具体是以确定性的方式为模块和分块分配短的(3 或 5 位)数字 ID,使得生成的缓存失效频率降低,配置可以这样写:

optimization: {
	  chunkIds: "deterministic", // 顾名思义,deterministic为确定性的
    moduleIds: "deterministic"
}

当然,chunkIds和moduleIds还有其他的配置选项

moduleId:

optimization选项值
false告诉webpack不应使用任何内置算法, 通过插件提供自定义算法
natural按使用顺序的数字ID,沿用v4数值命名
named方便调试的高可读性id,文件命名
deterministic根据**模块路径**生成简短的hash值
size根据模块大小生成的数字id,沿用v4

chunkId:

optimization选项值
false告诉webpack不应使用任何内置算法, 通过插件提供自定义算法
natural按使用顺序的数字ID,沿用v4数值命名
named方便调试的高可读性id,文件命名
deterministic根据代码**块内容**决定,注意与moduleId的此选项区分
totalSize根据请求到的解析资源size计算的id

减小Bundle体积

  1. 分析导入导出模块之间的依赖关系

webpack可以通过优化配置选项中的innerGraph启用模块依赖,具体如下:

optimization: {
		innerGraph: true
}

依赖图的好处是可以理清各个模块之间从export到import是怎样的引用关系,webpack对模块中的符号进行分析以找出从export到import的依赖关系,举个🌰:(内部依赖图可以记录到当前文件引用了something模块)

import {something} from './something'
function usingSomething() {
    return something;
}
export function test() {
     return usingSomething();
}
  1. 嵌套tree-shaking

Tree-shaking,翻译过来就是树摇,一个文件就是一棵树,会有主干和分支,也会有叶子,如果树被晃动,就会有叶子落下。tree-shaking就是将文件中不需要的代码删除掉,用来减小文件的体积,在webpack5中,不仅可以做到摇无用代码,还可以嵌套查询是否有无用代码从而进行删除。

首先做这样的配置:

optimization: {
    sideEffects: false,
    usedExports: true
}
// inner.js
export const a = 1;
export const b = 2;

// module.js
import * as inner from './inner'
export {inner}

// user.js
import * as module from './module';
console.log(module.inner.a);

通过内部模块依赖图,检测到inner.js中的常量 b 没有被使用,就会将其删除掉,是不是很腻害:happy:

注意!tree-shaking虽然在减小文件体积方面做的很到位,但使用需谨慎,要保证被删除的代码没有副作用才行,因为只要是没有使用到的文件,都会被摇掉,那如果有些文件不使用也不想被优化呢,比如需要写一个组件,但在库里没有使用它,这就有可能在打包时被优化掉了。

SplitChunk配置优化

splitChunk是webpack优化配置中的一个配置项,可以将Node Modules中代码单独打包成一个chunk最终输出,配置成功的话,可以自动分析多入口chunk中,有没有公共的文件,如果有,就会打包成一个单独的chunk。

先在index文件里引入jquery库,简单写两行代码:

import $ from 'jquery'

function sum(...args) {
  return args.reduce((a, b) => a + b, 0)
}
console.log(sum(1,2,3,4))

看一下不配置splitChunk的打包情况:

17.splitchunk_2.png

18.splitchunk_3.png

看第一张图,index.js中只写了几行代码,但体积却有852KiB,从第二张图可以得到答案,就是webpack将jquery和index中的代码打包到了一个文件里。

接下来配置一下splitchunk:

optimization: {
  splitChunks: {
    chunks: 'all',
    minSize: {// webpack5之前只可以设置 minSize: size
      javaScript: 2000,
      style: 10000
    }
  }
}

splitchunk不是webpack5才有的属性,但minSize的细化配置是webpack5升级的,可以在其中配置单独打包文件满足的条件,比如javaScript: 2000,如果文件体积小于2000,是不会单独抽离出来打包的。

再来看下同样代码的打包情况:

20.splitchunk_5.png

可以看到,第三方组件库被单独打包了,虽然一个文件引用了jquery库,表现的作用不明显,但如果多个文件都引用了这个库,作用就很明显了,一个是每个文件中都会打包jquery,一个是jquery只打包一份。

webpack使用遇到的问题


1.本地绑定localhost.jd.com域名,然后通过绑定域名访问项目,但是页面会报Invalid Host header,如下图所示

21.problem_one.png

问题原因:新版的webpack-dev-server增加了安全验证,默认检查hostname,如果hostname不是配置内的,将中断访问。

解决方法:在webapck配置中找到devServer配置项,添加{disableHostCheck: true},然后重启项目即可,如图

solutionOne.png

2.debug模式启动项目报找不到localhost

问题原因: 本地代理时,没有指定本地host

解决方案:在host配置中添加上本地回环地址127.0.0.1 localhost的映射,例如: 127.0.0.1 localhost.test.com

3.设置div超出两行显示省略号,但因为css样式中有浏览器前缀,线上打包会将前缀去掉导致样式失效。

问题原因:webpack.prod.js文件配置压缩时,引入了OptimizeCSSAssetsPlugin插件,这个插件默认会将浏览器前缀去掉

解决方案:为该插件设置参数autoprefixer为false


欢迎计算机前端相关领域小伙伴加入我们,具体的招聘信息可进入公众号查看,欢迎关注。

关注我们吧.jpg