Webpack

317 阅读27分钟

一、webpack

1.项目安装package.json: npm init
2.下载webpack/webpack-cli:yarn add webpack webpack-cli --dev
3.webpack版本:yarn webpack --version
4.webpack打包: yarn webpack

查看打包前后对比
--打包前:
(1)查看命令:browser-sync . / http-server
(2)html代码:<script type="module" src="src/index.js"></script>
--打包:yarn webpack【默认打包从src/index.js->dist/main.js中】
--打包后:
(1)查看命令:browser-sync . / http-server【一直链接即可】
(2)html代码:<script src="dist/main.js"></script>

二、webpack配置文件

在项目中(src同级)新建webpack.config.js文件,该文件是运行在node环境当中的js文件,我们需要按照commonjs去引用代码。 output.path必须是绝对路径!!! lfb8f42c080409763b9d44217df324754-s-m342c4de14a3bf1e1738f18398027f220.jpg

三、webpack工作模式

打包过程中会报错,如下

l3fdd0524d27b7cf44266b5f53c0c5c35-s-m7d4bec53de4eb48491787d5116020187.jpg 给webpack传参数

1.生产模式:yarn webpack/yarn webpack --mode production【会自动启用优化,优化打包结果】
2.开发模式:yarn webpack --mode development【会优化打包速度,添加一些调试过程需要的辅助在代码当中】
3.none模式:yarn webpack --mode none 【最原始模式的打包】

除了上面的命令传参数,还可以配置到配置文件

l4a1bba70831ce58f37c25a22bb114348-s-m1fe9a5cdfab199dd1ff714ad53a01d8e.jpg

四、webpack打包结果运行原理

把我们所有模块放到同一个文件当中,并提供一些基础代码,让我们的模块关系依然相互依赖

五、Webpack 资源模块加载

1.加载

打包css文件,入口文件entry:'./src/index.css'

lea5469ffc7c7c9692a75d3a92524dfc1-s-m27d8369e5634ae904f851c13fccf0a9b.jpg 报模块解析错误!意思:在模块解析过程中遇到了非法字符,因为webpack内部默认只处理javascript文件,也就是它把我们打包后的全都当成js文件处理,所以处理css文件时报非法字符错误。绿色框给出做法,需要加载能解析的loader!
解决:yarn add css-loader --dev

l1048c5c22beb75445b1d5756bc11ba54-s-mba2826d4bdefe737d58d7d3ef9bc182a.jpg 作用:将css文件转换为一个js模块

l78cb543f1cd4a3b7a3dfbb33fb312d30-s-mb6ac2e4c7d6f65c22e0418331220667d.jpg 将css代码push到一个数组(css-loader处理的)当中,还需下载style-loader,就是把通过css-loader处理的代码通过style加在页面上。

l7a079707fafdf04fb5bc384a26b9adaf-s-m26b21acc63ac8aa89b0e96d472bc5503.jpg Loader是webpack的核心特性,借助于Loader可以加载任何类型的资源!

2.导入资源模块

ld3fa7f296a357333d70a0896d2c9e79b-s-mdb5c35e9bb7f60e66fbc98dc17d19859.jpg  element.classList.add('heading') 编译

l946e5c77e7bc1fa9689f78351e8bcb90-s-m8f6fc2dd8d02dc4d152787864b016908.jpg javascript驱动整个前端应用

l84700a9c6cc5f6b27b620eb9562b6163-s-m4d9601beb42ba65b7946c59cd5a0781c.jpg

3.文件资源加载器

安装文件加载器:yarn add file-loader --dev

la0c775fc9f439f5f5128d4f1093b9876-s-md325fd007509015000e74218c173c2f8.jpg 因为图片默认地址是整个项目下,不是打包后的dist下,修改:

le7a42ed1c8d6d7349ed96b3064fb9591-s-m0bd07a128aaed37e9f417d24c2f4a7a2.jpg

l1315245d195de5805fb0bdfedd098e93-s-me190f1c5b4a791bf84f521e0379bb8bb.jpg 图解:

l428eaffd05398e2c67bf4a4215dca6c2-s-m775a4affb13aeaa4ceefe29947af7326.jpg

4.Webpack URL 加载器

l8dda832748a9f76225e267b54ce85ae0-s-m95d9294f97f89be670c8c1d275e90ea1.jpg url已经包含文件内容,在使用这种url不会再去发起http请求。 还有图片可以用base64形式表示

l001edaad5718f060d39691d61944b6c1-s-mbf6a9d9df349469eead0dc989996bee7.jpg

l12f3686e5cad9be11caba7ba459223cc-s-mc9c8aefe1ea3ed7db831a55041038a79.jpg

ld9a54dde7273b3c742ee6b66f98296a4-s-m20f4a74b6575d39a58add18858bdc776.jpg

l031bcdecd77ba60446a3c014b14e041f-s-m47dc5077756404d2e281de92e7061d7a.jpg 需要同时安装url-loader和file-loader

5.webpack常用加载器分类

l05c4f2cc0435fe7bd1c9b81bc65067dc-s-md2595632aa74ed7f7f1bd0bd0b3a45b8.jpg

(1)编译转换类

l9db5f736f4f2a5401b0b7fe3cd1bed6b-s-mda3bcd751861e819da79cb1da3981127.jpg

(2)文件操作类

ld7bb89abfc3f04c6a59c3f7e5c856cf8-s-m0529c5d193b8ddbfbf67e77f0c9ebf86.jpg

(3)代码检查类

la677104241f85857b28862f67d3a9df3-s-md01038a014ac8f9bfe94bdd0dff12e1c.jpg

6.Webpack 与 ES 2015

lde4ad955d03d9fbe0d24b6861809396c-s-me7c77b3a33bfa329b8be735daac41aae.jpg bundle.js支持es6,打包生成了es6语法,如果想要打包生成es5语法需要下载安装babel-loader。
命令:yarn add babel-loader --dev
l06d7f8a54f98831c89ff23eb95e4db1a-s-m528a6baccb6bc2ebd7a4b29e0636876e.jpg
查看bundle.js lc9a4e06777a6607792d3bfb7022c9263-s-me7c77b3a33bfa329b8be735daac41aae.jpg 还是没有被编译成es5语法。因为需要修改配置文件: l82c02abe145eea5e4ee8921ab3ee9aa9-s-mf442b02ccd1fa1d2fdd6912f101aa12d.jpg
期间报错 babel-core,根据错误安装了一路
yarn add babel-core --dev
yarn add @babel/preset-env --dev
yarn add babel-loader@7.1.5 --dev
yarn add @babel-core --dev
yarn add babel-core@7 --dev
//不知具体是不是都需要这些
lee00902774ad7180bcb880a94c8fc032-s-m9a1e499409a30a85e812e0c1b4ffaa63.jpg l2c7d9afcbc7cab5b46495b0e9eb4f8b3-s-mfad3846ecdef092799baf4736bb7ca1c.jpg l4b43825e52f4e2f606792b50b7be2ef9-s-me85f226b44b8802dbb77ece9b676fa57.jpg

7.Webpack 加载资源的方式

(1)遵循ES Modules标准的important声明

image.png

(2)遵循CommonJS标准的require函数

image.png

(3)遵循AMD标准的define函数和require函数

image.png
除了以上三种,还有Loader加载的非JavaScript也会触发资源加载,如(4)、(5)。

(4)css-loader中,样式代码中@important指令和url函数

image.png
遇到url资源图片,针对我们遇到的文件去找到相应的loader.

(5)HTML代码中图片标签的src属性[html-loader]

image.png
a标签的href属性 image.png

8.Webpack 核心工作原理

(1)根据配置找到打包入口

image.png

(2)根据import/require解析推断这个文件所依赖的资源模块,然后分别解析每一个资源模块和依赖,最后形成关系依赖关系树

image.png

(3)递归依赖树,找到模块所对应的加载器,让对应的加载器去加载对应模块,最后将加载的结果放到bundle.js中,从而实现整个项目的打包

image.png

9.Webpack 开发一个 Loader

(1)项目中加入一个.md文件,并且引入到main.js中

image.png

(2)编写一个markdown-loader.js文件,并且在webpack配置文件中使用

image.png image.png

(3)执行命令 yarn webpack

结果打印成功,也输出了hello!,但是有报错 image.png 原因: image.png webpack加载资源过程,类似于一个管道,可以依次使用多个loader,但是要求我们最终要输出一段js代码。我们返回的字符串不是标准的js代码,解决办法 image.png 要么(1)直接返回js代码,要么(2)用其他加载器转成js代码。
(1-1) image.png 打包不报错了,打包结果 image.png (1-2)安装md解析模块:yarn add marked --dev image.png (1-3)在返回的代码当中,直接使用es module方式导出,将module.exports = 改为 export default image.png 结果 image.png (2)返回一个html字符串交给下一个loader去处理 image.png 下载html-loader:yarn add html-loader --dev image.png use规则:数组倒叙执行,先执行后面的再向前执行,顺序不要错!!!
结果 image.png .md -(markdown-loader)-> html字符串 -(html-loader)->导出这个html字符串的js代码
loader负责资源文件从输入到输出的转换,loader是一个管道的概念,对于同一个资源可以使用多个loader,依次交个下一个去处理,如css-loader --> style-loader! 关于loader参考链接

六、Webpack 插件机制介绍

目的:增强webpack自动化能力。loader专注于实现资源模块加载,plugin解决其他自动化工作(如:清除dist目录、拷贝静态文件至输出目录、压缩输出代码)。webpack+plugin实现大多前端工程化工作。

1.Webpack 自动清除输出目录插件

安装:yarn add clean-webpack-plugin --dev
使用: image.png 这样打包生成的dist文件中就只剩本次打包结果了,其他以前的都会被删除。

2.Webpack 自动生成HTML插件

  • 为html文件中引入的外部资源如script、link动态添加每次compile后的hash,防止引用缓存的外部文件问题
  • 可以生成创建html入口文件,比如单页面可以生成一个html文件入口,配置N个html-webpack-plugin可以生成N个页面入口 image.png 希望自动生成使用bundle.js的HTML。
    安装:yarn add html-webpack-plugin --dev
    html-webpack-plugin默认导出插件类型,不需要解构内部成员。 image.png 报错, image.png 解决:最好配置一个index.html文件,因为好像中间过程使用了ejs,但并没有相关的loader导致报错 image.png 打包成功,在dist文件夹下生成index.html image.png 打包生成的路径dist/bundle.js,不应该在dist下 image.png 再次打包 image.png

3.改进html-webpack-plugin生成结果

index.html image.png webpack.config.js image.png 生成的dist/index.html image.png

4.多个入口文件

image.png

5.Webpack 插件使用总结

安装:yarn add copy-webpack-plugin --dev image.png 使用copy-webpack-plugin将文件夹下的东西复制到生成文件夹下【默认】 image.png

6.Webpack 开发一个插件

相比于loader,plugin拥有更宽的能力范围。plugin通过钩子机制实现。 webpack工作过程中有很多环节,为了便于插件的扩展,webpack几乎给每一个环节都埋下了一个钩子,在我们开发插件的时候,我们就可以通过往这些节点上去挂载不同的任务就可以轻松扩展webpack了。
具体有哪些钩子,查看:webpack官方文档
webpack要求我们的钩子必须是一个函数或者是一个包含apply方法的对象。一般我们会把插件定义为一个类,然后在这个类中定义apply方法。

例子:写一个能去掉打包生成的bundle.js文件中的大块注释的插件。 使用插件前打包成样子: image.png 编写: image.png 打包后: image.png webpack的plugin通过往生命周期的钩子中挂载函数实现扩展!!!

七、增强 Webpack 开发体验

上面的开发环境顺序: image.png 理想的开发环境:

  • 1.以HTTP Server运行
  • 2.自动编译+自动刷新
  • 3.提供source Map支持

1.Webpack 自动编译

使用webpack cli中的watch工作模式去实现,监视文件变化,自动打包更新。 命令: yarn webpack --watch
控制台实时编译,无需手动yarn webpack编译。 http-server打开前端页面,每次更改编译后,刷新页面就能看到最新代码。

2.Webpack 自动刷新浏览器

browser-sync dist --files "**/*"去启动前端页面。 webpack自动打包代码到dist文件当中,dist中的文件变化又被browser-sync监听了,从而自动编译自动刷新。【代码更改,自动编译后,浏览器自动刷新】。
弊端:

  • 1.操作上更麻烦(同时使用两个工具)
  • 2.效率上降低(webpack会不断写入磁盘,browser-sync不断从磁盘读取内容)

3.Webpack Dev Server

提供用于开发的HTTP Server,集成自动编译和自动刷新浏览器等功能。
安装:yarn add webpack-dev-server --dev
运行:yarn webpack-dev-server
webpack去打包我们的应用并且会启动http-server去运行我们的打包结果。在运行过后还会去监听代码变化,一旦源文件发生变化就会自动重新打包。这一点和watch一样。但是webpack-dev-server为了提供打包效率,并没有将打包结果写入磁盘中,而是暂时存放在内存当中。内部的http-server也是从内存当中把打包结果独取出来发送给浏览器。
yarn webpack-dev-server --open 自动唤起浏览器去打开运行地址。

4.Webpack Dev Server 静态资源访问

【Webpack Dev Server默认只会serve打包输出文件】Webpack Dev Server默认会将构建结果输出的文件,全部作为开发服务器的资源文件。只要是webpack打包能够输出的文件都可以直接被访问到。但是其他静态资源文件也需要被访问,就需要额外的去告诉webpack一个serve。
下载:yarn add webpack-dev-server --dev
使用:在webpack.config.js中,注释CopyWebpackPlugin, 原因:开发阶段最好不要使用这个插件,因为会频繁重复的执行打包任务,开销很大!! image.png 报错:Error: Cannot find module 'webpack-cli/bin/config-yargs'
原因:webpack 5+,webpack-cli 4+,降低webpack-cli版本
下载:webpack-cli3+:npm install webpack-cli@3 -D 启动OK~ 访问启动地址下的favicon.ico:http://localhost:8081/favicon.ico,可以正常展示,证明devServer生效,可以找到public下的文件! contentBase可以去为webpack devServer额外为开发服务器指定查找资源目录。

5.Webpack Dev Server 代理 API

跨域资源共享(CORS)。使用CORS的前提是API必须支持,并不是任何情况下API都应该支持。若是同源部署(同协议、同域名、同端口),没必要使用CORS。
问题:开发阶段接口跨域问题。在开发阶段去代理服务,就是把本地服务代理到开发服务器上。Webpack Dev Server支持配置代理。
目标:将Github API 代理到开发服务器。 请求名称:需要被代理的请求路径前缀。 image.png 启动服务:yarn webpack-dev-server image.png 打开浏览器访问:http://localhost:8081/api/users
可以得到结果:(以下结果是代理到公司内部接口,没有权限结果,但是接口是通的)
image.png
tips:主机名是HTTP协议中的相关概念!

八、Source Map

在编译构建过程中,将我们的源代码变成能在生产环境中运行的代码。运行代码与源代码之前完全不同。如果需要调试应用,或者运行中出现了无法预料的错误,无法定位。因为调试和报错都是基于转换之后的运行代码。Source Map解决以上问题。Source Map源代码地图,去映射转换之后的代码和源代码之间的关系。

1.Webpack 配置 Source Map

在webpack.config.js中配置

image.png

运行打包命令: yarn webpack 查看打包结果,可以看到生成了bundle.js.map文件,同时bundle.js的最后一行注释了使用的打包文件

image.png 以下内容验证能找到错误对应源码

  • (1)修改某个文件,写语法错误

image.png

  • (2)运行打包命令 yarn webpack
  • (3)进入打包生成的dist文件 cd dist,启动server运行命令: http-server
  • (4)在浏览器打开查看

image.png

  • (5)进入到具体报错位置

image.png 目前为止,webpack对source map支持多种模式,不同模式生成速度和效率不同。

2.Webpack eval 模式的 Source Map

webpack中的devtool不同模式对比(初次构建速度、监视模式重新打包速度、是否适合在生产环境当中使用、所生成的source map的质量) image.png 设置:devtool: 'eval' image.png 是的包裹后的模块代码,eval模式会将每个模块转换的代码都放在eval函数中去,并且在eval函数执行的字符串最后通过sourceURL的方式去说明一个文件对应路径。 image.png eval模式不生成sourcemap的.map文件,所以是速度最快的,效果简单只能定位文件名称,不能知道具体的行列的信息。 image.png

3.Webpack devtool 模式对比

按照2里面的不同模式,打包会报错,因为版本问题。目前使用的是webpack5。 参照webpack5webpack5 devtool image.png 特点: image.png

4.Webpack 选择 Source Map 模式

开发环境:eval-cheap-module-source-map image.png 生产环境:none
Source Map会暴露源代码!

九、 Webpack HMR

1. Webpack 自动刷新的问题

webpack dev server提供对开发者友好的开发服务器。 Webpack HMR(模块热替换):应用运行过程中实时替换某个模块,运行状态不受影响。自动刷新会导致页面状态丢失,热替换只将修改的模块实时替换至应用中。HMR极大程度的提高了开发者的工作效率。HMR集成在webpack-dev-server中,通过运行 webpack-dev-server --hot去开启,或者可以通过配置文件开启。
使用webpack-dev-server 需要自己手动去配置。 image.png yarn webpack-dev-derver --open 修改样式文件,热更新方式修改成功!js文件自动刷新了,并不是热更新。

webpack中的HMR并不可以开箱即用,还需要我们做些而外操作。

  • 1.样式文件为何会热更新?因为样式文件是loader处理的,style-loader自动处理的热更新。
  • 2.js文件为什么不能自处理热更新?样式模块只需要把更新过后的样式替换就行。但js文件没有任何规律,可能导出的是对象,字符串,函数,毫无规律的,所以没有办法自己统一处理热更新。如果使用如vue-cli,为什么js会热更新,因为框架下的开发,每种文件都是有规律的,如统一导出对象,而且通过脚手架创建的项目内部都集成了MHR方案。
    所以我们需要手动处理js模块更新过后的热替换!

2.Webpack 使用 HMR API

image.png module函数的hot属性提供了一个accept方法,用于注册某个模块更新过后的处理函数。参数:1.依赖模块的路径 2.依赖更新后的处理函数。 image.png 手动处理模块热替换,浏览器就不会自动刷新了。反之,如果没有手动处理,浏览器就会自动刷新。

3.Webpack 处理 JS 模块热替换--以文本编辑器更新为例

image.png 因为不同模块有不同逻辑,不同业务逻辑也需要不同的替换方案,所以webpack无法提供通用的替换方案。

4.Webpack 处理图片模块热替换

image.png 直接重新设置图片的src就行。

5.Webpack HMR 注意事项

(1)处理HMR的代码报错会导致自动刷新。

自动刷新过后,页面错误信息被清除了,不易发现哪里出错。推荐使用hotOnly,因为hot热更新失败就会自动回退使用自动刷新情况。hotOnly不会去使用自动刷新。浏览器不自动刷新,错误代码也很容易捕捉到了。 image.png

(2)没启用HMR情况下,HMR API报错。

image.png 运行报错 image.png image.png module.hot是HMR插件提供的,没有开启插件,就没有这个对象。需要判断是否存在这个对象,再去使用。 image.png

(3)代码中有很多业务无关代码,是否会有影响。

devServer未开启,webpack.HotMoudleReplacementPlugin未使用情况,yarn webpack打包结果如下 image.png 代码中处理热替换代码被移除掉了,只剩if(false){},if(false){}代码压缩后会自动去掉,所以不会影响我们生产环境中的运行状态。

十、Webpack 生产环境优化 --不同环境下的配置

webpack使用source Map、HMR等都是为了让开发者有更好的开发体验。这些体验提升的同时也让打包体积变大,因为会自动向打包结果中增加额外的代码的内容。生产环境和开发环境有很大的差异,生产环境注重运行效率,开发环境只注重开发效率。针对以上问题。webpack4提供了mode,为不同工作环境创建不同配置。

1.配置文件根据环境不同导出不同配置。

image.png image.png 上图是使用yarn webpack打包结果,没有public文件内容,打包生成的是开发环境内容。 使用yarn webpck --env production image.png

2.一个环境对应一个配置文件(不同环境的配置文件)。

一般是有三个文件,一个公共配置文件、两个不同环境配置文件。 使用Object.assign()方法,最后一个对象会覆盖掉前一个对象中的同名属性,值属性的覆盖没有问题,但是像插件是配置一个数组,Object.assign()会完全覆盖掉前面的,我们想添加一两个插件就不合适了。所以1.使用loadsh提供的merge方法2.使用更为专业的webpack-merge模块。
安装:yarn add webpack-merge --dev image.png 生产环境打包命令: yarn webpack --config webpack.prod.js
可以将命令定义到npm scripts中去使用,yarn build image.png

十一、Webpack DefinePlugin

webpack4开始使用了很多优化打包结果插件。

1.DefinePlugin,为代码注入全局成员。

在production模式下,默认会启用这个插件,并且往我们代码当中注入process.env.NODE_ENV常量,很多第三方模块是通过这个成员去判断当前的运行环境,从而去决定是否去执行如打印日志等这样的一些操作。 image.png image.png 打包后,此时API_BASE_URL被注入全局,查看打包结果bundle.js image.png 查看打包结果可知,将我们设置的值之间替换到打包结果当中,所以报错了。我们所传的内容实际上要求的是一个代码片段,即一段符合js代码的语法。 image.png 或者使用JSON.stringfy将值转换成表示这个值的代码片段。 image.png 打包结果OK! DefinePlugin可以为代码注入可能发生变化的值。

2.Tree Shaking(摇树),摇掉代码中未引用部分,即未引用代码(dead-code)。

(1)自动检测代码中未被引用的代码,然后移除他们。

image.png Tree Shaking在生产环境模式下自动开启。打包后没有找到console.log等冗余代码。 image.png

(2)Webpack 使用 Tree Shaking

Tree Shaking并不是某一个配置选项,是一种功能搭配使用过后的优化效果,这种功能会在production模式下自动启用。 image.png optimization是集中配置webpack中优化功能的。 useExports:true表示在输出结果中只导出外部使用了的成员。 image.png 打包后可见不再导出未被使用的代码了 image.png 使用minimize:true压缩打包代码 image.png 打包后,可见冗余代码全部被优化掉了 image.png 如果把代码看作一颗大树,那么useExports负责标记枯树叶,minimize负责爸干枯的部分摇下来!

3.Webpack 合并模块

使用concatenateModules继续优化我们的输出。普通的打包结果是将我们的每一个模块单独放在一个函数当中。如果模块很多,那么输出结果就有很多这样的模块函数。 image.png 打包后,所有模块都在同一个配置当中。concatenateModules的作用就是尽可能将所有模块合并输出到一个函数当中,既提升了运行效率又减少了输出代码的体积。这个特性又被称为Scope Hoisting(作用域提升,webpack3当中提出的) image.png

4.Webpack Tree Shaking 与 Babel

由于早期webpack发展比较快,变化也就比较多。所以当我们查找资料时得到的结果并不一定适用于我们当前所使用的版本。很多资料表示如果我们使用了babel loader就会导致Tree Shaking失效,以下是说明。 首先Tree Shaking使用前提是使用ES Modules的方式去实现模块化,webpack在打包所有模块之前先将模块根据不同配置交给不同的loader去处理,最后再将所有loader处理后的结果打包到一起(由webpack打包的代码必须使用ESM)。为了转换代码中的ECMAScript的新特性,很多时候我们选择babel-loader去处理js。babel在处理代码时就有可能将ES Modules转为CommonJs。 image.png babel/preset-env里面就有这个插件,所以当babel/preset-env工作时,我们代码当中的ES Modules部分就会被转为CommonJs方式,所以webpack打包时就拿到的是CommonJs方式的代码,所以Tree Shaking不能生效。 image.png 打包后发现Tree Shaking并没有失效,这是因为在最新版本的babel-loader当中就已经自动帮我们关闭了es modules转换的插件。 image.png image.png 所以webpack打包后还是正常代码。tree shaking也就可以正常工作了。 image.png 打包后发现我们配置的useExports没有生效,即便我们开启压缩代码tree shaking也没有办法正常工作了。 image.png 如果不清楚版本是否有以上问题,可以配置 modules:false确保preset-env不会开启ESM转换的插件了 image.png

5.Webpack sideEffects(副作用)

允许我们通过配置的方式去标识我们的代码是否有副作用,从而为tree shaking提供更大的压缩空间。副作用是指:模块执行时除了导出成员之外所做的事情。sideEffects一般用于npm包标记是否有副作用使用的。 当我们在a.js文件引用了多个模块,在index.js中又引用a.js时,我们只想引用一个模块,此时打包会发现所有模块都会被打包进来。 image.png sideEffects在production模式下会被默认开启。开启过后webpack打包时就会检查当前代码所属的packsge.json当中有没有sideEffects的标识,以此来判断这个模块是否有副作用。如果这个模块没有副作用,那么这些没有用到的模块就不会被打包。 image.png 当前这个packsge.json所影响的这个项目当中所有的代码都没有副作用。一旦这些没有用到的代码没有副作用就会被移除掉。没有用到的就不会被打包进来了。 image.png 使用sideEffects的前提是:确定你的代码真的没有副作用,否则在打包时就会误删掉那些有副作用的代码。 image.png image.png 上面为Number添加pad做扩展的这个操作就属于extend这个模块的副作用代码,因为在导入这个模块后Number原型上就会多一个方法。如果此时我们还标识项目中所有代码都没有副作用的话,再次打包,打包结果可见,刚刚的扩展操作不会被打包进来,因为他是副作用代码,但是我们的配置中已经声明了没有副作用,所以他们就被移除掉了。 image.png 还有js当中载入的css模块也是副作用模块,同样会面临刚刚的问题。 image.png 解决办法1.在package.json当中去关掉副作用。2.标识当前项目当中哪些文件是有副作用的。这样webpack就不会去忽略那些有副作用的模块了。 image.png 打包,查看结果返现有副作用的两个模块也被打包进来了 image.png

十二、Webpack Code Splitting (代码分割)

webpack打包弊端:所有代码最终都被打包到一起。如果应用复杂,模块很多,那么打包出来的bundle.js体积过大,超过2-3M是非常常见的。大多数情况下并不是每个模块在启动时都是必要的。但是这些模块都被打包到一起了,我们需要任何模块都必须要整体加载下来过后去使用。应用一般在浏览器端打开,会浪费很多流量和带宽。更为合理的方案是分包,按需加载,这样会大大提高响应速度和运行效率。以前所主流的HTTP1.1版本本身就有很多缺陷,如存在同域并行请求限制,且每次请求都会有一定的延迟,请求的Header请求头和响应头都会浪费带宽流量。所以模块打包是必要的!代码分割通过把我们的模块按照所设计的规则,打包到不同的bundle当中,从而提高应用响应速度

  • 1.多入口打包:即同时有多个打包入口同时打包,此时就会输出多个打包结果。
  • 2.动态导入:采用ESM的动态导入,去实现模块的按需加载,此时webpack会把动态导入的模块单独输出到一个bundle当中。

1.多入口打包

多用于传统的多页应用程序,一个页面对应一个打包入口,对于不同页面之间公共的部分再去单独提取。 image.png 打包后有两个入口、两个bundlejs文件 image.png 有问题两个都同时引入了 image.png 修改配置文件 image.png 打包后 image.png image.png 不同入口中肯定会有公共模块--需要提取公共模块 image.png 打包生成公共模块文件 image.png

2.动态导入

按需加载:需要用到某个模块时,再加载这个模块。webpack中支持使用动态导入方式去实现模块的按需加载。所有动态导入的模块都会被自动提取到单独的bundle当中,从而实现分包。 image.png 打包完成后,会发现dist下多了三个bundle.js文件。这三个js文件就是动态导入自动分包所产生的。这三个文件分别时刚刚导入的两个模块,以及这两个模块公共部分所提取出来的bundle。 image.png 整个过程我们无需配置任何一个地方,只需要按照es moudle动态导入成员的方式去导入模块就可以,webpack内部会自动处理分包和按需加载。如果使用但也应用开发框架,如react、vue的话,在项目当中的路由映射文件,可以通过这种动态导入的方式实现按需加载。
默认通过动态导入产生的bundle文件,他的名称就只是一个序号。需要给生成的bundle命名的话,可以使用webpack的Magic Comments(魔法注释)去实现。 image.png 打包后 image.png 如果ChunkName相同就会被打包到一起,打包到一个模块当中。根据这样的情况就可以灵活组织动态加载的模块所输出的文件了。 image.png image.png

十三、Webpack MiniCssExtractPlugin -- 提取css到单个文件

MiniCssExtractPlugin可以将css代码从打包结果当中提取出来的插件,通过这个插件可以实现css模块的按需加载。 安装:yarn add mini-css-extract-plugin --dev mini-css-extract-plugin作用是自动提取代码当中的css到一个单独的文件中。目前使用的样式模块先交给css-loader解析再交给style-loader处理(将样式代码通过style标签方式注入页面,使样式得以工作) image.png 使用mini-css-extract-plugin样式会单独存放文件当中,所以就不需要style标签方式注入了,而是直接link方式去引入,所以就不需要使用style-loader了,而是使用MIniCssExtractPlugin.loader的方式去实现样式文件通过link标签的方式去注入。 image.png 样式提取到单独文件了,但是如果样式文件不大,link到页面中多了一次请求,就不太适合了。当css大小超过150KB才需要考虑是否提取到单独文件当中。

十四、Webpack OptimizeCssAssetsWebpackPlugin -- 压缩输出的css文件

用生产模式去打包会发现样式文件没有被压缩,是因为webpack内置的压缩插件只是针对js进行压缩,对于其他文件的压缩都需要额外的插件去完成。使用 optimize-css-assets-webpack-plugin去压缩css文件 安装:yarn add optimize-css-assets-webpack-plugin --dev image.png 打包可见样式文件被压缩了 image.png 这样所有模式打包css都会被压缩了,我们希望当生产模式下去压缩,否则不去压缩。那么在optimization中的minimizer中去配置需要使用的压缩插件(生产模式自动启用minimizer)。 image.png 此时使用生成模式打包,发现css文件被压缩了,但是js文件未被压缩。这是因为当我们配置了optimization,系统就会认为我们是要手动配置压缩文件,js压缩插件并没有,所以没有被压缩。 安装js压缩插件:yarn add terser-webpack-plugin --dev image.png 再次打包发现js文件和css文件都被压缩打包了。

十五、Webpack 输出文件名 Hash

一般我们去部署前端的资源文件时,都会启用服务器的静态资源缓存,对于用户的浏览器而言,就可以缓存住我们当中的静态资源,后续就不用再去请求服务器来得到资源文件了,因此我们应用的响应速度会有大幅度的提升。不过开启服务端的静态资源缓存也会有些小问题。如果在缓存策略当中我们设置的失效时间过短的话,效果就不是特别明显,如果失效时间设置的过长,一点资源更新,又无法及时更新到应用客户端。为了解决这个问题,我们建议在生产模式下,我们需要给输出的文件名添加Hash值,这样一旦我们的资源文件发生改变,文件名称也会跟着去变化。对于客户端而言,全新的文件名就是全新的请求,也就没有缓存的问题了,这样我们就可以把服务端设置的失效时间设置的非常长,也不用担心文件失效过后的问题了。

1.hash 项目中有任何一处改动,项目中的打包后的hash值都会跟着变化。

image.png

2.chunkhash 打包过程中,只要是同一路的打包chunkhash都是相同的。

样式文件是从代码中单独提取的,所以并不是单独的chunk,所以可以看到main、posts、album三者hash并不相同,而css文件所对应的js文件chunkhash是一样的。 image.png 单独修改index.js只有打包对应生成的文件(main.js)hash发生了变化,其他文件没有变化,若修改posts.js文件,打包生成的js和css的hash都会发生变化,同时main.js也会发生变化,因为main.js是入口文件,因为引用了posts,导致被动发生变化。相比于hash,chunkhash的控制要更精确一些image.png

3.contenthash 根据输出文件的内容生成的hash值,不同文件就有不同的hash值。

单独修改index.js只有打包对应生成的文件(main.js)hash发生了变化,其他文件没有变化,若修改posts.js文件,打包生成的js会发生变化,css不会发生任何变化。同时main.js也会发生变化,因为main.js是入口文件,因为引用了posts,导致被动发生变化。相比于前两者,contenthash精确到了文件级别的hash,只有文件发生变化才会去更新文件名,这个是最适合去解决缓存问题的image.png webpack允许我们去指定hash的长度,设置如下:(控制缓存8位的contenthash最为合适) image.png