webpack
2020-06-05 14:16:48 0 举报
AI智能生成
登录查看完整内容
为你推荐
查看更多
前端面试
作者其他创作
大纲/内容
webpack
自己的听课笔记
3.webpack核心概念
Loader
loader是什么?
它是一个打包方案,webpack只知道如何打包js文件,不知道如何打包非.js文件,loader可以告诉webpack如何打包这些文件
常用loader
vue-loader
打包.vue文件
file-loader
打包图片、.txt等
打包图片时,单独生成一个图片文件
url-loader
与file-loader的区别
file-loader 单独生产一个图片文件url-loader 把图片转换成一个base64的字符串,然后直接把这个base64的字符串放到bundle.js里,而不是单独生成一个图片文件
利弊
好处是,可以少发一次http请求,适合图片比较小的情况
坏处是,如果图片过大,bundle.js会变得很大,请求时间过长,页面很久才能展示出来
改进
图片大于2048字节(2KB)时使用file-loader图片小于2048字节时,使用url-loader,把图片变成base64放到bundle.js里
使用loader打包静态资源
图片
{ loader: 'font color=\"#c41230\
[ext] 资源扩展名
[name] 资源的基本名称
[path] 资源相对于 context的路径
[hash] 内容的哈希值
字体
iconfont
阿里矢量图标库
下载图标,把iconfont.css里的样式粘贴到本地样式文件里,修改字体src
它告诉webpack去打包后缀为.eot .ttf .svg的字体
样式
style-css
在得到css生成的内容以后,把这段内容挂在到页面的<head>里
css-loader
分析几个css文件之间的关系,把几个css文件合并成一段css
css-loader扩展项
importLoaders: 2
用于配置「css-loader 作用于 @import 的资源之前」有多少个 loader对scss文件里@import的scss文件如何进行打包
modules:true
对css进行模块化打包,可以防止样式冲突
sass-loader
把.scss文件翻译成css
postcss-loader
自动添加厂商前缀
postcss.config.js
autoprefixer插件
plugins
使打包更加便捷
plugin可以在webpack运行到某一个时刻时帮你做一些事情,比如html-webpack-plugin会在打包结束这一个时刻自动生成html文件
常用插件
html-webpack-plugin
在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到这个html文件中
以template指定的文件为模板生成html文件
clean-webpack-plugin
在打包之前,先删除dist目录
hot-module-replacement-plugin
热更新
Entry与Output
多个入口起点
filename是输出的js的文件名
[name]里的name就是entry里的key值
打包多个入口文件
静态资源放在CDN时
output: { publicPath: 'http://cdn/...' }
打包后的html引入的js文件中会加上 publicPath前缀
SouceMap
它是一个映射关系,它会建立打包后的文件和源文件之间的映射关系,告诉你源文件的什么位置出错
打包会变慢,因为它要构建映射关系
devtool配置选项
devtool: \"source-map\"
打包后生成 .js.map文件该文件对源代码和打包后的代码进行映射
devtool: \"inline-source-map\"
打包后不会生成.js.map映射关系以base64的形式被引入打包后的js文件中的souceMappingURL中
devtool: \"cheap-inline-source-map\"
只提示哪一行出错不提示列,只针对业务代码不针对loader生成sourcemap,可以提升打包性能
devtool: \"eval\"
最快,但是业务复杂时提示不全面
devtool: \"cheap-module-source-map\"
module 针对loader生成sourcemap
production环境选用这个较好
devtool: \"cheap-module-eval-source-map\"
development环境选这个较好,性能快,信息全
WebpackDevServer
webpack --watch
可以监听变动并自动打包
它不能起服务器,每次打包后需要手动刷新浏览器
webpack-dev-server
功能
监听变动并自动打包
自动打开、刷新浏览器
模拟服务器特性
开启一个web服务器,localhost:8080,启动完服务器打包代码到dist目录下
以http的方式打开浏览器,可以发ajax请求而以file
基础配置
contentBase:WebpackDevServer 服务器要启动在哪一个文件夹下
proxy 接口代理
port 端口号
Hot Module Replacement(HMR)
当file文件的内容改变时,执行后边的回调函数进行热更新
样式文件 vue react不需要手写这段HMR代码,是因为css-loader vue-loader内置了这个功能
Bable
把ES6代码转换成浏览器可以识别的ES5代码
安装
npm install babel-loader @babel/core --save-devnpm install @babel/preset-env --save-devnpm install @babel/polyfill --save
@babel/core
是babel的核心库,它能让babel去识别js代码里的内容,然后把js代码转成AST抽象语法树,然后再把抽象语法树编译转成新的语法
babel-loader
负责打通跟webpack的关联,并没有转换
@babel/preset-env
负责转换新的JavaScript句法(syntax)而不转换新的API
@babel/polyfill
弥补ES5缺失的变量或者函数,设置useBuiltIns为'usage'可以只打包已经用到的API语法,按需引入减少文件体积
配置
打包业务代码
presets:[[\"@babel/preset-env\
打包业务代码,只需要设置@babel/preset-env
在chrome大于67以上的版本进行语法转换,如果浏览器本身支持ES6语法,则不进行转换
@babel/pollyfill会污染全局环境
打包内库代码
plugins:[[\"@babel/plugin-transform-runtime\
生成一些第三方模块或者UI组件时,打包这些库时不希望污染全局变量,@babel/plugin-transform-runtime会以闭包的方式注入对应的内容,当可以防止污染全局变量
\"corejs\
\"corejs\":要设置为2,当页面不存在map promise方法时,才会把代码打包进来
需要额外安装@babel/runtime-corejs2
babel配置也可以单配在.babelrc文件里,将.babelrc放到项目的根目录下
哪些文件转换?哪些不转换?
font color=\"#c41230\
include: 表示目录中的哪些 .js 文件需要进行转换exclude: babel-loader做语法解析时忽略node_modules这些第三方模块
4.webpack进阶
Tree Shaking
当引入一个模块时,不引入该模块所有的代码,只引入需要的代码,剔除掉无用代码
只支持ES Module,因为它的底层是静态引入不支持CommonJS,因为它的底层是动态引入
webpack.config.js
生产环境
font color=\"#c41230\
开发环境
usedExports: true表示,哪些模块用export导出了就打包,如果模块没有export打包时就忽略掉
在development环境下打包时,即使用了treeShaking,也不会把代码直接从打包后的js文件里剔除掉,但是会提示exports used因为开发环境需要做一些调试,如果直接剔除可能会导致soucemap对应的行数出错
package.json
\"sideEffects\": []
把不需要进行treeShaking的文件写到[],比如css文件['*.css']
\"sideEffects\":false
如果对所有文件都进行treeShaking,设为\"sideEffects\":false
webpack和code splitting
为什么要分割?
比如引入lodash,仅使用其中一两个方法,仍会把所有的lodash打包进来,造成打包文件过大,加载时间长
分割方法
手动代码分割
新建一个lodash.jsimport _ from 'lodash'window._ = _ ;
entry:{ lodash:'./src/lodash.js'}
使用webpack配置进行代码分割
两种情形下的分割配置
同步引入模块代码
webpack.common.jsoptimization:{ splitChunks:{ chunks: 'all' }}
异步引入模块代码import('loadash').then。。。
无需配置optimization动态引入语法需安装 npm install babel-plugin-dynamic-import-webpack --save-dev.bebelrc 添加配置:\"plugins\": [\"dynamic-import-webpack\"]
代码分割与webpack无关
代码分割的三种方法
Entry Points:入口文件设置的时候可以配置CommonsChunkPluginDynamic Imports:动态导入
splitChunksPlugin参数详解
splitChunks: { chunks: \"async\
Lazy Loading
其实懒加载就是通过import来异步的加载一个模块。什么时候执行import语法时,它对应的模块才会被载入。好处是借助Import这种语法,我们可以让页面加载的更快。
minChunks: 2假设我们打包以后会有很多个文件,如果有两个以上文件里面需要引用lodash 那我们就需要对lodash进行代码分割。
打包分析 Preloding Prefeching
打包分析
生成打包过程的描述文件: webpack --profile --json > stats.json
打包分析工具bundle 分析
https://github.com/webpack/analyse/
webpack-chart
webpack-bundle-analyzer
预加载
使用场景
异步加载交互代码时:例如当点击的时候再加载异步代码,虽然提高了页面初始化速度,但是对用用户点击的体验不好,速度太慢;为了解决懒加载带来的问题:使用prefetch preload使用预加载,比如打开一个网站,首页加载完,在首页加载完成后利用空闲时间,预先加载登陆模态框,等用户登陆时模态框可以很快的加载出来
使用方法
使用魔术注释import(/* webpackPrefetch: true */ 'LoginModal');
区别
prefetch 会等主流程都加载完成,等待空闲再加载;(最优)preload 和主线程一起加载
优化
代码使用率
打开控制台 :ctrl+shift+p 输入coverage 查看js代码使用率
缓存能带来的性能提升是非常有限的,应该重点考虑提升代码的使用率,有些交互之后才能用到的代码,可以放到单独的异步模块里 提高加载速度及页面利用率
CSS文件的代码分割
css代码分割插件mini-css-extract-plugin
解决什么问题
css in js默认会把css打包进js中
作用
css代码分割,可以将css单独打包
缺点
暂时不支持HMR,因此只能在线上使用
安装使用
1、安装插件
npm install --save-dev mini-css-extract-plugin
2、配置webpack.prod.js
引入插件, require...
plugins中使用插件
new MiniCssExtractPlugin({...})
更改loader,开发环境和线上环境分开配置loader,配置loader为MiniCssExtractPlugin.loader
3、经过1和2,还不能把css拆分出来,原因在于对css文件进行了tree shaking
webpack.prod.js
optimization:{ usedExports:true }
sideEffects:[ '*.css']
css文件不进行tree shaking
4、css压缩
对抽离出来的css文件进行代码的合并压缩
optimize-css-webpack-plugin
扩展用法
Extracting CSS based on entry
根据入口分割css
将不同入口文件中加载的所有模块的css样式打包到一个文件里比如有两个入口文件index.js index1.js,将index.js index1.js中加载的所有模块的样式分别进行打包
配置 cacheGroupsoptimization:{ splitChunks:{ cacheGroups:{ 。。。 } }}
Extracting all CSS in a single file
当有多个入口文件时,将所有入口文件里引入的css文件全部打包到一个css文件里
将所有 .css文件打包到一个名为styles的文件里
webpack与浏览器缓(caching)
缓存
浏览器缓存存在什么问题
代码打包,将dist目录下的文件上传到服务器用户第一次打开页面,浏览器加载打包后的文件,用户再次刷新,则浏览器从缓存中拿文件修改源代码,重新打包,将dist目录下的文件上传到服务器用户再次刷新,浏览器请求文件时发现本地有缓存(因为文件的名字没有改变),则从缓存中拿文件,就不会加载新上传的文件
怎么解决
在每次修改源文件后,打包时使用contenthash改变文件的名字,可以解决上述问题
contenthash
contenthash,是根据content产生的一个hash字符串,content不变hash字符串就不会变如果源代码没有改变,那么打包生成文件的contenthash永远都不会变
output: { filename: '[name].font color=\"#c41230\
修改源文件,重新打包,将dist目录下的文件(main.js、vendor.js、index.html)放到服务器上去当用户再次访问线上的页面时,流程如下:打开index.html,浏览器加载index.html中<script>引入的js文件,加载vendor.js时,发现vendors.***.js里的contenthash值没变,则使用本地缓存加载main.js时,main.******.js里的contenthash值变了,则去服务器上加载最新的main.js文件
shimming的作用
解决webpack打包时存在的兼容性问题,修改webpack的默认行为,实现webpack自身无法实现的一些效果
eg: 自动引入第三方库如果使用了一些版本比较老的第三方模块,它里边用到了jquery或者lodash等第三方库,而这些模块并没有使用ES6 moudle的 import引入这些第三方库,这时候打包会出错,使用providePlugin,可以在模块中自动引入依赖的第三方库
用途
自动引入第三方库
new webpack.providePlugin({ $: 'jquery'})
当发现一个模块中使用了$,就会在模块里自动引入jquery这个模块,然后把$作为模块的名字即,在底层完成了 import $ from 'jquery'
改变this的指向
安装 imports-loader修改配置 loader: 'imports-loader?this=>window'
this默认指向它所在的模块改变this的指向,让它指向window
5.webpack实战配置案例
Library打包(库打包)
引子
当自己实现一个库时,打包后的文件给别人使用,用户可能会使用各种引入方式,比如 ES6 module 、CommonJS 、AMD、 <script>。。。你要如何配置才能支持用户的各种引入方式?
ouput:{ font color=\"#c41230\
<script>标签引入
生成一个全局变量root,用户可以在<srcipt src='root.js'></script>中引入
模块化引入
libraryTarget:' umd'
表示用户可以用任意的模块引入方式引入这个这个模块
libraryTarget:' this'
变量root会注入到this对象中
libraryTarget:' window'
变量root会注入到window对象中
防止重复引入
externals:['lodash']
externals的值为数组或对象
表示打包过程中要忽略打包的库,防止使用时用户重复引入库
发布npm包
npm adduser 用户 密码 邮箱 npm publish 把项目发布到npm的仓库里
TypeScript的打包配置
typescript
它是javascript的一个超集
规范了js语法,提升代码的可维护性
安装配置
npm install ts-loader typescript -D
创建tsconfig.js文件,并配置
配置webpack.config.js
多页面打包配置
多页面应用的本质就是创建多个HtmlWebpackPlugin
增加入口
创建htmlWebpackPlugin
chunks的作用
让index.html中只引入index.js,list.html中只引入list.js如果不配置chunks,.html中会引入所有的.js文件
6.webpack底层原理及脚手架分析
如何编写一个loader
loader本质
它是一个函数
写一个loader并使用
使用自己写的loader
配置webpack
写一个loader
编写一个loader
不能写成箭头函数,要写成声明式函数,因为这个函数里需要用this,webpack在调loader时,会把this做一下变更,变更之后才能使用this里的一些方法
source是引入文件的内容,即源代码,loader可以针对该源代码进行一些加工,返回你想要的内容
常见使用场景
捕获业务代码异常
可以对业务代码进行异常捕获,当检测到function时,把它放入try{function(){}}catch(e){}中
实现整个网站的国际化
伪代码
在业务代码中,使用{{title}}占位符,loader会根据不同的语言环境,把网站打包成不同的语言版本
如何编写一个plugin
插件本质
它是一个类
编写&使用插件
定义一个插件
一个插件的基本格式
使用插件
在webpack.config.js中配置plugins
以new 一个类实例的形式来使用
插件的参数
传递参数
webpack配置文件
用name给插件传递参数
接受参数
在插件中,options接受参数
webpack性能优化
提高打包速度
1. 跟上技术的迭代(Node,Npm,Yarn)
新版本的webpack node等性能更好,打包更快
2. 在尽可能少的模块上应用Loader
合理的使用include和exclude,减少loader的作用范围,减少一些无用的分析可以加快打包速度
3. Plugin尽可能精简并确保可靠
尽量少的使用插件,并确保每一个插件的可靠性和性能(尽量使用官方插件,性能有保证)在开发环境下,不要使用代码压缩插件,节约代码压缩这部分的打包时间
4.resolve参数合理配置
这个作用是引入文件的时候省略后缀名
要合理配置,如果配置过多,需要查找很多次有没有这些后缀的文件,会有性能损耗因此,建议只把.js .jsx .vue 配置到extensions里,资源类的引入时直接带着后缀引入
指定一个路径的别名,当引入child时,其实是引入的'../src/child'
5.使用DllPlugin提高打包速度
问题
引入很多第三方库,每次打包时,都要从node-modules中取出这些库,然后把这些库打包到源代码中每次打包时重复打包这些第三方库,导致打包时间很长
解决思路
而这些第三方库几乎不怎么变化,第一次打包时把这些第三方库打包成一个文件存起来,之后再打包时,直接用第一次打包存起来的代码
目标:
1.第三方模块打包一次
2.引入第三方模块的时候,使用.dll文件引入
6.控制包文件大小
没用的包去除掉,或者不要引入
把大文件拆分成小文件
7.font color=\"#c41230\
webpack默认是通过nodejs来运行的,所以它是单进程的打包可以借助node的多进程提升打包速度
多进程打包
parallel-webpack
多页应用打包时,通过parallel-webpack对多个页面一起进行打包
8.合理使用sourceMap
打包时生成sourcemap,内容越详细,打包越慢,根据情况合理使用
9.结合stats分析打包结果
10.开发环境内存编译
在开发环境使用devServer,它在做打包时不会生成dist目录,它会把编译生成的文件放到内存里,内存的读取比硬盘快
11.开发环境无用插件剔除
比如在开发环境,不需要进行代码压缩,所以不要使用压缩代码的插件
这些文章太旧了,很多插件已经弃用了白整理了啊 ~ ~ ~
面试题1
几个常见的loader
babel-loader:把 ES6 转换成 ES5
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。css-loader:加载 CSS,支持模块化、压缩、文件导入等特性sass-loader: Loads a SASS/SCSS file and compiles it to CSS.
source-map-loader:加载额外的 Source Map 文件,以方便断点调试
ts-loader
webpack v5中的
几个常见的plugin
html-webpack-plugin: 为html文件中引入的外部资源,可以生成创建html入口文件
DllPlugin
这个插件是在一个额外的独立的 webpack 设置中创建一个只有 dll 的 bundle(dll-only-bundle)。这个插件会生成一个名为 manifest.json 的文件,这个文件是用来让 DLLReferencePlugin 映射到相关的依赖上去的。
SplitChunksPlugin
Since version 4 the CommonsChunkPlugin was removed in favor of optimization.splitChunks and optimization.runtimeChunk options.
HotModuleReplacementPlugin
永远不要在生产环境(production)下启用 HMR
DefinePlugin
DefinePlugin 允许创建一个在编译时可以配置的全局常量。这可能会对开发模式和发布模式的构建允许不同的行为非常有用
uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
clean-webpack-plugin:删除打包文件
webpack有哪些优点
1、专注于处理模块化的项目,能做到开箱即用,一步到位2、可通过plugin扩展,完整好用又不失灵活3、使用场景不局限于web开发4、社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展5、良好的开发体验
分别介绍bundle,chunk,module是什么
bundle:是由webpack打包出来的文件,chunk:代码块,一个chunk由多个模块组合而成,用于代码的合并和分割。module:是开发中的单个模块,在webpack的世界,一切皆模块,一个模块对应一个文件,webpack会从配置的entry中递归开始找出所有依赖的模块。
面试题2
5.Loader和Plugin的不同?
不同的作用
Loader直译为\"加载器\"。Webpack将一切文件视为模块,但是webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力。Plugin直译为\"插件\"。Plugin可以扩展webpack的功能,让webpack具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
不同的用法
Loader在module.rules中配置,也就是说他作为模块的解析规则而存在。 类型为数组,每一项都是一个Object,里面描述了对于什么类型的文件(test),使用什么加载(loader)和使用的参数(options)Plugin在plugins中单独配置。 类型为数组,每一项是一个plugin的实例,参数都通过构造函数传入。
6.webpack的构建流程是什么?从读取配置到输出文件这个过程尽量说全
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:1、初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;2、开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;3、确定入口:根据配置中的 entry 找出所有的入口文件;4、编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;5、完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;6、输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;7、输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
7.是否写过Loader和Plugin?描述一下编写loader或plugin的思路?
Loader像一个\"翻译官\"把读到的源文件内容转义成新的文件内容,并且每个Loader通过链式操作,将源文件一步步翻译成想要的样子。编写Loader时要遵循单一原则,每个Loader只做一种\"转义\"工作。 每个Loader的拿到的是源文件内容(source),可以通过返回值的方式将处理后的内容输出,也可以调用this.callback()方法,将内容返回给webpack。 还可以通过 this.async()生成一个callback函数,再用这个callback将处理后的内容输出出去。 此外webpack还为开发者准备了开发loader的工具函数集——loader-utils。相对于Loader而言,Plugin的编写就灵活了许多。 webpack在运行的生命周期中会广播出许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
9.如何利用webpack来优化前端性能?(提高性能和体验)
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。1、压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css2、利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径3、删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现4、提取公共代码。
10.如何提高webpack的构建速度?
1、多入口情况下,使用CommonsChunkPlugin来提取公共代码(CommonsChunkPlugin被弃用了)2、通过externals配置来提取常用库3、利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。4、使用Happypack 实现多线程加速编译(效果不好)5、使用webpack-uglify-paralle(官网查不到)l来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度6、使用Tree-shaking和Scope Hoisting来剔除多余代码
11.怎么配置单页应用?怎么配置多页应用?
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述多页应用的话,可以使用webpack的 AutoWebPlugin(查不到)来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:1、每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表2、随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
面试题3
一、优化构建速度
缩小文件的搜索范围
使用DllPlugin减少基础模块编译次数
使用HappyPack开启多进程Loader转换(happypack 效果不好)
使用ParallelUglifyPlugin(查不到)开启多进程压缩JS文件
二、优化开发体验
使用自动刷新
Webpack监听文件
1. 启动时 webpack --watch
2. 在配置文件中设置watch:true
DevServer刷新浏览器
开启模块热替换HMR
开启方式
1、webpack-dev-server --hot2、使用HotModuleReplacementPlugin,比较麻烦
开启后如果修改子模块就可以实现局部刷新
三、优化输出质量-压缩文件体积
区分环境--减小生产环境代码体积
压缩代码-JS、ES、CSS
压缩JS:Webpack内置UglifyJS插件、ParallelUglifyPlugin(官网都查不到)
压缩ES6
uglify-webpack-plugin(v5中的)提供了压缩ES6代码的功能
另外要防止babel-loader转换ES6代码,要在.babelrc中去掉babel-preset-env,因为正是babel-preset-env负责把ES6转换为ES5
压缩CSS:
css-loader?minimize(查不到minimize)
css-loader内置了cssnano,只需要使用 css-loader?minimize 就可以开启cssnano压缩
cssnano基于PostCSS,不仅是删掉空格,还能理解代码含义
PurifyCSSPlugin(查不到)
需要配合 extract-text-webpack-plugin (v5 中的)使用,它主要的作用是可以去除没有用到的CSS代码,类似JS的Tree Shaking
使用Tree Shaking剔除JS死代码
它正常工作的前提是代码必须采用ES6的模块化语法,因为ES6模块化语法是静态的(在导入、导出语句中的路径必须是静态字符串,且不能放入其他代码块中)
启用Tree Shaking
1、修改.babelrc以保留ES6模块化语句
四、优化输出质量--加速网络请求
使用CDN加速静态资源加载
1、HTML文件:放在自己的服务器上且关闭缓存,不接入CDN2、静态的JS、CSS、图片等资源:开启CDN和缓存,同时文件名带上由内容计算出的Hash值,这样只要内容变化hash就会变化,文件名就会变化,就会被重新下载而不论缓存时间多长
多页面应用提取页面间公共代码,以利用缓存
原理
大型网站通常由多个页面组成,每个页面都是一个独立的单页应用,多个页面间肯定会依赖同样的样式文件、技术栈等。如果不把这些公共文件提取出来,那么每个单页打包出来的chunk中都会包含公共代码,相当于要传输n份重复代码。如果把公共文件提取出一个文件,那么当用户访问了一个网页,加载了这个公共文件,再访问其他依赖公共文件的网页时,就直接使用文件在浏览器的缓存,这样公共文件就只用被传输一次。
相比于webpack3,4.0版本用optimization.splitChunks配置替换了3.0版本的CommonsChunkPlugin插件
分割代码以按需加载
单页应用的一个问题在于使用一个页面承载复杂的功能,要加载的文件体积很大,不进行优化的话会导致首屏加载时间过长,影响用户体验。做按需加载可以解决这个问题
具体方法如下
1、将网站功能按照相关程度划分成几类2、每一类合并成一个Chunk,按需加载对应的Chunk3、例如,只把首屏相关的功能放入执行入口所在的Chunk,这样首次加载少量的代码,其他代码要用到的时候再去加载。最好提前预估用户接下来的操作,提前加载对应代码,让用户感知不到网络加载
做法
一个最简单的例子:网页首次只加载main.js,网页展示一个按钮,点击按钮时加载分割出去的show.js,加载成功后执行show.js里的函数
import(/* webpackChunkName:show */ './show').then() 是实现按需加载的关键,Webpack内置对import( *)语句的支持,Webpack会以./show.js为入口重新生成一个Chunk。代码在浏览器上运行时只有点击了按钮才会开始加载show.js,且import语句会返回一个Promise,加载成功后可以在then方法中获取加载的内容。这要求浏览器支持Promise API,对于不支持的浏览器,需要注入Promise polyfill。/* webpackChunkName:show */ 是定义动态生成的Chunk的名称,默认名称是[id].js,定义名称方便调试代码。
五、优化输出质量--提升代码运行时的效率
使用Prepack提前求值
prepack-webpack-plugin
现在Prepack还不够成熟,用于线上环境还为时过早
使用Scope Hoisting
作用域提升
需要分析模块间的依赖关系,所以源码必须是采用了ES6模块化的
六、使用输出分析工具
启动Webpack时带上这两个参数可以生成一个json文件,输出分析工具大多依赖该文件进行分析
1、官方工具Webpack Analyse
2、webpack-bundle-analyzer
可视化分析工具,比Webapck Analyse更直观
七、其他Tips
1、配置babel-loader时,use: [‘babel-loader?cacheDirectory’] cacheDirectory用于缓存babel的编译结果,加快重新编译的速度。另外注意排除node_modules文件夹,因为文件都使用了ES5的语法,没必要再使用Babel转换。2、配置externals,排除因为已使用<script>标签引入而不用打包的代码,noParse是排除没使用模块化语句的代码。3、配置performance参数可以输出文件的性能检查配置。4、配置profile:true,是否捕捉Webpack构建的性能信息,用于分析是什么原因导致构建性能不佳。5、配置cache:true,是否启用缓存来提升构建速度。6、可以使用url-loader把小图片转换成base64嵌入到JS或CSS中,减少加载次数。7、通过imagemin-webpack-plugin压缩图片(查不到imagemin-webpack-plugin)8、开发环境下将devtool设置为cheap-module-eval-source-map,因为生成这种source map的速度最快,能加速构建。 在生产环境下将devtool设置为hidden-source-map
博客总结
优质资料
带你深度解锁Webpack系列(基础篇)
带你深度解锁Webpack系列(进阶篇)
带你深度解锁Webpack系列(优化篇)
一步步从零开始用 webpack 搭建一个大型项目
图片/字体文件处理
把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
将资源转换为 base64 可以减少网络请求次数,但是 base64 数据较大,如果太多的资源是 base64,会导致加载变慢,因此设置 limit 值时,需要二者兼顾
source-map-loader
加载额外的 Source Map 文件,以方便断点调试
处理样式文件
style-loader
Adds CSS to the DOM by injecting a <style> tag
The css-loader interprets @import and url() like import/require() and will resolve them
处理兼容性问题,自动生成浏览器前缀
Loads a SASS/SCSS file and compiles it to CSS.
less-loader
Compiles Less to CSS.
按照模板生成一个html文件
DLLReferencePlugin
删除打包文件,每次打包前清空dist目录
处理CSS
mini-css-extract-plugin
抽离CSS,即将CSS文件单独打包,这可能是因为打包成一个JS文件太大,影响加载速度,也有可能是为了缓存(例如,只有JS部分有改动)
optimize-css-assets-webpack-plugin
将抽离出来的css文件进行压缩
一些弃用的或者项目中无须使用的
uglifyjs-webpack-plugin
通过UglifyES压缩ES6代码
进阶
按需加载
import()
很多时候我们不需要一次性加载所有的JS文件,而应该在不同阶段去加载所需要的代码。webpack内置了强大的分割代码的功能可以实现按需加载。
import() 语法,需要 @babel/plugin-syntax-dynamic-import 的插件支持,但是因为当前 @babel/preset-env 预设中已经包含了 @babel/plugin-syntax-dynamic-import,因此我们不需要再单独安装和配置。
构建结果
webpack 遇到 import(****) 这样的语法的时候,会这样处理:
以**** 为入口新生成一个 Chunk当代码执行到 import 所在的语句时,才会加载该 Chunk 所对应的文件(如这里的1.bundle.8bf4dc.js)
首先配置 devServer 的 hot 为 true
2. 并且在 plugins 中增加 new webpack.HotModuleReplacementPlugin()
我们配置了 HotModuleReplacementPlugin 之后,会发现,此时我们修改代码,仍然是整个页面都会刷新。不希望整个页面都刷新,还需要修改入口文件
3. 在入口文件中新增
此时,再修改代码,不会造成整个页面的刷新
多页应用打包
目录
配置chunks 参数,指定html引入的js文件
查看 index.html 和 login.html 会发现,都同时引入了 index.f7d21a.js 和 login.f7d21a.js,通常这不是我们想要的,我们希望,index.html 中只引入 index.f7d21a.js,login.html 只引入 login.f7d21a.jsHtmlWebpackPlugin 提供了一个 chunks 的参数,可以接受一个数组,配置此参数仅会将数组中指定的js引入到html文件中,此外,如果你需要引入多个JS文件,仅有少数不想引入,还可以指定 excludeChunks 参数,它接受一个数组
resolve 配置
resolve 配置 webpack 如何寻找模块所对应的文件。webpack 内置 JavaScript 模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,但你可以根据自己的需要修改默认的规则。
配置项
modules
resolve.modules 配置 webpack 去哪些目录下寻找第三方模块,默认情况下,只会去 node_modules 下寻找,如果你我们项目中某个文件夹下的模块经常被导入,不希望写很长的路径,那么就可以通过配置 resolve.modules 来简化
这样配置之后,我们 import Dialog from 'dialog',会去寻找 ./src/components/dialog,不再需要使用相对路径导入。如果在 ./src/components 下找不到的话,就会到 node_modules 下寻找
alias
resolve.alias 配置项通过别名把原导入路径映射成一个新的导入路径
extensions
适配多端的项目
缺省文件后缀
在导入语句没带文件后缀时,会自动带上extensions 中配置的后缀后,去尝试访问文件是否存在,因此要将高频的后缀放在前面,并且数组不要太长,减少尝试次数。如果没有配置 extensions,默认只会找对对应的js文件
enforceExtension
如果配置了 resolve.enforceExtension 为 true,那么导入语句不能缺省文件后缀。
mainFields
有一些第三方模块会提供多份代码,引入模块时,去找哪个文件以及查找的顺序
区分不同的环境
webpack.base.js 定义公共的配置webpack.dev.js:定义开发环境的配置webpack.prod.js:定义生产环境的配置
webpack-merge 专为 webpack 设计,提供了一个 merge 函数,用于连接数组,合并对象
定义环境变量
使用 webpack 内置插件 DefinePlugin 来定义环境变量
使用
利用webpack解决跨域问题
假设前端在3000端口,服务端在4000端口,我们通过 webpack 配置的方式去实现跨域
配置代理
量化
speed-measure-webpack-plugin
speed-measure-webpack-plugin 插件可以测量各个插件和loader所花费的时间
1. exclude/include
确保转译尽可能少的文件。顾名思义,exclude 指定要排除的文件,include 指定要包含的文件
2. cache-loader
在一些性能开销较大的 loader 之前添加 cache-loader,将结果缓存中磁盘中
只打算给 babel-loader 配置 cache 的话,也可以不使用 cache-loader,给 babel-loader 增加选项 cacheDirectory
cacheDirectory:默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 Webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程。设置空值或者 true 的话,使用默认缓存目录:node_modules/.cache/babel-loader
3. happypack
把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程
当你的项目不是很复杂时,不需要配置 happypack,因为进程的分配和管理也需要时间,并不能有效提升构建速度,甚至会变慢
4.thread-loader
除了使用 Happypack 外,我们也可以使用 thread-loader ,把 thread-loader 放置在其它 loader 之前
thread-loader 和 Happypack 我对比了一下,构建时间基本没什么差别。不过 thread-loader 配置起来为简单
5.开启 JS 多进程压缩
虽然很多 webpack 优化的文章上会提及多进程压缩的优化,不管是 webpack-parallel-uglify-plugin 或者是 uglifyjs-webpack-plugin 配置 parallel。不过这里我要说一句,没必要单独安装这些插件,它们并不会让你的 Webpack 构建速度提升
当前 Webpack 默认使用的是 TerserWebpackPlugin,默认就开启了多进程和缓存
6. HardSourceWebpackPlugin
HardSourceWebpackPlugin 为模块提供中间缓存,缓存默认的存放路径是: node_modules/.cache/hard-source
首次构建时间变化不大,第二次开始,构建时间降低效果很明显
7. noParse
如果一些第三方模块没有AMD/CommonJS规范版本,可以使用 noParse 来标识这个模块,这样 Webpack 会引入这些模块,但是不进行转化和解析,从而提升 Webpack 的构建性能 ,例如:jquery 、lodash
如果你使用到了不需要解析的第三方依赖,那么配置 noParse 很显然是一定会起到优化作用的
8. resolve
resolve 配置 webpack 如何寻找模块所对应的文件
resolve.moudles
将频率最高的后缀放在第一位,并且控制列表的长度,以减少尝试次数
项目较小时,优化效果不明显
9. IgnorePlugin
webpack 的内置插件,作用是忽略第三方包指定目录
减小包体积
10. externals
可以将一些JS文件存储在 CDN 上(减少 Webpack打包出来的 js 体积),在 index.html 中通过 <script> 标签引入
希望在使用时,仍然可以通过 import 的方式去引用(如 import $ from 'jquery'),并且希望 webpack 不会对其进行打包,此时就可以配置 externals
11. DllPlugin
有些时候,如果所有的JS文件都打成一个JS文件,会导致最终生成的JS文件很大,这个时候,我们就要考虑拆分 bundles
DllPlugin 和 DLLReferencePlugin 可以实现拆分 bundles,并且可以大大提升构建速度,DllPlugin 和 DLLReferencePlugin 都是 webpack 的内置模块
使用 DllPlugin 将不会频繁更新的库进行编译,当这些依赖的版本没有变化时,就不需要重新编译
在一个新建的配置文件中,把第三方库单独打包成一个动态链接库
打包后,生成动态链接库
dist└── dll ├── manifest.json └── react.dll.9dcd9d.js
manifest.json 用于让 DLLReferencePlugin 映射到相关依赖上
在主配置文件中
在html文件中引入dll文件
<script src=\"/dll/react.dll.9dcd9d.js\"></script>
效果
构建速度
构建时间缩短,尤其是modules with no loaders,这项构建时间缩短较为明显
体积
打包后的bundle.js体积减少
12. optimization.splitChunks 抽离公共代码
抽离公共代码是对于多页应用来说的,如果多个页面引入了一些公共模块,那么可以把这些公共的模块抽离出来,单独打包。公共代码只需要下载一次就缓存起来了,避免了重复下载
抽离公共代码对于单页应用和多页应该在配置上没有什么区别,都是配置在 optimization.splitChunks 中
即使是单页应用,同样可以使用这个配置,例如,打包出来的 bundle.js 体积过大,我们可以将一些依赖打包成动态链接库,然后将剩下的第三方依赖拆出来。这样可以有效减小 bundle.js 的体积大小当然,你还可以继续提取业务代码的公共模块
配置DllPlugin 和 SplitChunks 后,General output time有所减少(不包含dll打包时间)
runtimeChunk
runtimeChunk 的作用是将包含 chunk 映射关系的列表从 main.js 中抽离出来,在配置了 splitChunk 时,记得配置 runtimeChunk.
optimization: { runtimeChunk: { name: 'manifest' } }
终构建出来的文件中会生成一个 manifest.js
借助 webpack-bundle-analyzer 进一步优化
在做 webpack 构建优化的时候,vendor 打出来超过了1M,react 和 react-dom 已经打包成了DLL。因此需要借助 webpack-bundle-analyzer 查看一下是哪些包的体积较大
npm run build 构建,会默认打开: http://127.0.0.1:8888/,可以看到各个包的体积
打包后的 vendor 太大了
使用 splitChunks 进行拆分
13.webpack自身的优化
tree-shaking
如果使用ES6的import 语法,那么在生产环境下,会自动移除没有使用到的代码
scope hosting 作用域提升
变量提升,可以减少一些变量声明。在生产环境下,默认开启
babel 配置的优化
0 条评论
回复 删除
下一页