第一题:谈谈你对webpack的理解?
webpack是一个打包模块化js的工具,在webpack里一切文件皆模块,通过loader转换文件,通过plugin注入钩子,最后输出由多个模块组合成的文件,webpack专注构建模块化项目。WebPack可以看做是模块的打包机器:它做的事情是,分析你的项目结构,找到js模块以及其它的一些浏览器不能直接运行的拓展语言,例如:Scss,TS等,并将其打包为合适的格式以供浏览器使用。
第二题:说说webpack与grunt、gulp的不同?
三者都是前端构建工具,grunt和gulp在早期比较流行,现在webpack相对来说比较主流,不过一些轻量化的任务还是会用gulp来处理,比如单独打包CSS文件等。
grunt和gulp是基于任务和流(Task、Stream)的。类似jQuery,找到一个(或一类)文件,对其做一系列链式操作,更新流上的数据, 整条链式操作构成了一个任务,多个任务就构成了整个web的构建流程。
webpack是基于入口的。webpack会自动地递归解析入口所需要加载的所有资源文件,然后用不同的Loader来处理不同的文件,用Plugin来扩展webpack功能。
所以,从构建思路来说,gulp和grunt需要开发者将整个前端构建过程拆分成多个Task
,并合理控制所有Task
的调用关系;webpack需要开发者找到入口,并需要清楚对于不同的资源应该使用什么Loader做何种解析和加工
对于知识背景来说,gulp更像后端开发者的思路,需要对于整个流程了如指掌 webpack更倾向于前端开发者的思路
第三题:什么是bundle,什么是chunk,什么是module?
bundle:是由webpack打包出来的文件
chunk:代码块,一个chunk由多个模块组合而成,用于代码的合并和分割
module:是开发中的单个模块,在webpack的世界,一切皆模块,一个模块对应一个文件,webpack会从配置的entry中递归开始找出所有依赖的模块
第四题:什么是Loader?什么是Plugin?
1)Loaders是用来告诉webpack如何转化处理某一类型的文件,并且引入到打包出的文件中
2)Plugin是用来自定义webpack打包过程的方式,一个插件是含有apply方法的一个对象,通过这个方法可以参与到整个webpack打包的各个流程(生命周期)。
第五题:有哪些常见的Loader?他们是解决什么问题的?
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
source-map-loader:加载额外的 Source Map 文件,以方便断点调试
image-loader:加载并且压缩图片文件
babel-loader:把 ES6 转换成 ES5
css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
eslint-loader:通过 ESLint 检查 JavaScript 代码
第六题:有哪些常见的Plugin?他们是解决什么问题的?
define-plugin:定义环境变量
commons-chunk-plugin:提取公共代码
uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
第七题: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的实例,参数都通过构造函数传入。
第八题:webpack的构建流程是什么?
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
确定入口:根据配置中的 entry 找出所有的入口文件;
编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。
第九题:描述一下编写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 改变输出结果。
第十题:如何利用webpack来优化前端性能?
用webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运行快速高效。
压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
提取公共代码。
第十一题:如何提高webpack的构建速度?
多入口情况下,使用CommonsChunkPlugin来提取公共代码
通过externals配置来提取常用库
利用DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
使用Happypack 实现多线程加速编译
使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
使用Tree-shaking和Scope Hoisting来剔除多余代码
第十二题:怎么配置单页应用?怎么配置多页应用?
单页应用可以理解为webpack的标准模式,直接在entry中指定单页应用的入口即可,这里不再赘述
多页应用的话,可以使用webpack的 AutoWebPlugin来完成简单自动化的构建,但是前提是项目的目录结构必须遵守他预设的规范。 多页应用中要注意的是:
每个页面都有公共的代码,可以将这些代码抽离出来,避免重复的加载。比如,每个页面都引用了同一套css样式表
随着业务的不断扩展,页面可能会不断的追加,所以一定要让入口的配置足够灵活,避免每次添加新页面还需要修改构建配置
第十三题:如何在vue项目中实现按需加载?
Vue UI组件库的按需加载 为了快速开发前端项目,经常会引入现成的UI组件库如ElementUI、iView等,但是他们的体积和他们所提供的功能一样,是很庞大的。 而通常情况下,我们仅仅需要少量的几个组件就足够了,但是我们却将庞大的组件库打包到我们的源码中,造成了不必要的开销。
不过很多组件库已经提供了现成的解决方案,如Element出品的babel-plugin-component和AntDesign出品的babel-plugin-import 安装以上插件后,在.babelrc配置中或babel-loader的参数中进行设置,即可实现组件按需加载了。
单页应用的按需加载 现在很多前端项目都是通过单页应用的方式开发的,但是随着业务的不断扩展,会面临一个严峻的问题——首次加载的代码量会越来越多,影响用户的体验。
通过import()语句来控制加载时机,webpack内置了对于import()的解析,会将import()中引入的模块作为一个新的入口在生成一个chunk。 当代码执行到import()语句时,会去加载Chunk对应生成的文件。import()会返回一个Promise对象,所以为了让浏览器支持,需要事先注入Promise polyfill
webpack 基本使用
module.exports = {
entry : './src/js/entry.js',
output : {
filename : 'index.js',
path : __dirname + '/out'
},
module : {
rules: [
{test: /.js$/, use: ['babel-loader']},
{test: /.css$/, use: ['style-loader', 'css-loader']},/*解析css, 并把css添加到html的style标签里*/
//{test: /.css$/, use: ExtractTextPlugin.extract({fallback: 'style-loader',use: 'css-loader'})},/*解析css, 并把css变成文件通过link标签引入*/
{test: /.(jpg|png|gif|svg)$/, use: ['url-loader?limit=8192&name=./[name].[ext]']},/*解析图片*/
{test: /.less$/, use: ['style-loader', 'css-loader', 'less-loader']}/*解析less, 把less解析成浏览器可以识别的css语言*/
]
}
}
常见的Webpack Plugin?
plugin:本质是插件,基于事件流框架 Tapable
,插件可以扩展 Webpack
的功能,在 Webpack
运行的生命周期中会广播出许多事件,Plugin
可以监听这些事件,在合适的时机通过 Webpack
提供的 API
改变输出结果。
html-webpack-plugin
:简化 HTML
文件创建 (依赖于 html-loader
)
uglifyjs-webpack-plugin
:压缩js文件
clean-webpack-plugin
:目录清除
mini-css-extract-plugin
:分离样式文件,CSS 提取为独立文件,支持按需加载 (替代extract-text-webpack-plugin
)
loader和plugin对比?
Loader
在module.rules
中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object
,内部包含了test(类型文件)、loader、options
(参数)等属性。Plugin
在plugins
中单独配置,类型为数组,每一项是一个Plugin
的实例,参数都通过构造函数传入。
前端模块化,CMD、AMD、CommonJS
CommonJS
CommonJS
是服务器端模块的规范,由Node推广使用,webpack也采用这种规范编写
commonJs规范:
CommonJS
模块规范主要分为三部分:模块定义、模块标识、模块引用
。
- 模块定义:
module
对象:在每一个模块中,module
对象代表该模块自身。export
属性:module
对象的一个属性,它向外提供接口。输出模块变量的最好方法是使用module.exports
对象。一个单独的文件就是一个模块。每一个模块都是一个单独的作用域,也就是说,在该模块内部定义的变量,无法被其他模块读取,除非定义为global
对象的属性。 - 模块标识:传递给
require
方法的参数,必须是符合小驼峰命名的字符串,或者以 . 、.. 、开头的相对路径,或者绝对路径。 - 模块引用:加载模块使用
require
(同步加载),该方法读取一个文件并执行,返回文件内部的module.exports
对象。
优势:
在后端,JavaScript
的规范远远落后并且有很多缺陷,这使得难以使用JavaScript
开发大型应用。比如:没有模块系统、标准库较少、没有标准接口、缺乏包管理系统、列表内容
- CommonJS模块规范很好地解决变量污染问题,每个模块具有独立空间,互不干扰,命名空间相比之下就不太好。
- CommonJS规范定义模块十分简单,接口十分简洁。
- CommonJS模块规范支持引入和导出功能,这样可以顺畅地连接各个模块,实现彼此间的依赖关系
- CommonJS规范的提出,主要是为了弥补JavaScript没有标准的缺陷,已达到像Python、Ruby和Java那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段
缺点:
没有并行加载机制
由于CommonJS
是同步加载模块,这对于服务器端是很不好的,因为所有的模块都放在本地硬盘。等待模块时间就是硬盘读取文件时间,很小。但是,对于浏览器而言,它需要从服务器加载模块,涉及到网速,代理等原因,一旦等待时间过长,浏览器处于”假死”状态。
所以浏览器端不是很适合Common.Js
,出现另一种规范AMD
AMD
AMD
是运行在浏览器环境的一个异步模块定义规范 ,是RequireJS
在推广过程中对模块定义的规范化产出。
AMD规范
AMD
推崇依赖前置,在定义模块的时候就要声明其依赖的模块
优点
用户体验好,因为没有延迟,依赖模块提前执行了。
CMD
CMD
是一个通用模块定义规范;是SeaJs推广过程中对模块定义的规范化产出
CMD规范
CMD
推崇依赖就近,只有在用到某个模块的时候才会去require
优点
性能好,因为只有用户需要的时候才执行。
webpack 作用
- 模块打包
- 编译兼容
- 能力扩展
打包原理
首先我们应该简单了解一下webpack
的整个打包流程:
- 1、读取
webpack
的配置参数; - 2、启动
webpack
,创建Compiler
对象并开始解析项目; - 3、从入口文件(
entry
)开始解析,并且找到其导入的依赖模块,递归遍历分析,形成依赖关系树; - 4、对不同文件类型的依赖模块文件使用对应的
Loader
进行编译,最终转为Javascript
文件; - 5、整个过程中
webpack
会通过发布订阅模式,向外抛出一些hooks
,而webpack
的插件即可通过监听这些关键的事件节点,执行插件任务进而达到干预输出结果的目的。
其中文件的解析与构建是一个比较复杂的过程,在webpack
源码中主要依赖于compiler
和compilation
两个核心对象实现。
compiler
对象是一个全局单例,他负责把控整个webpack
打包的构建流程。 compilation
对象是每一次构建的上下文对象,它包含了当次构建所需要的所有信息,每次热更新和重新构建,compiler
都会重新生成一个新的compilation
对象,负责此次更新的构建过程。
而每个模块间的依赖关系,则依赖于AST
语法树。每个模块文件在通过Loader
解析完成之后,会通过acorn
库生成模块代码的AST
语法树,通过语法树就可以分析这个模块是否还有依赖的模块,进而继续循环执行下一个模块的编译解析。
最终Webpack
打包出来的bundle
文件是一个IIFE
的执行函数。
// webpack 5 打包的bundle文件内容
(() => { // webpackBootstrap
var __webpack_modules__ = ({
'file-A-path': ((modules) => { // ... })
'index-file-path': ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => { // ... })
})
// The module cache
var __webpack_module_cache__ = {};
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = __webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {}
};
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
// Return the exports of the module
return module.exports;
}
// startup
// Load entry module and return exports
// This entry module can't be inlined because the eval devtool is used.
var __webpack_exports__ = __webpack_require__("./src/index.js");
})
sourceMap 是什么
提到sourceMap
,很多小伙伴可能会立刻想到Webpack
配置里边的devtool
参数,以及对应的eval
,eval-cheap-source-map
等等可选值以及它们的含义。除了知道不同参数之间的区别以及性能上的差异外,我们也可以一起了解一下sourceMap
的实现方式。
sourceMap
是一项将编译、打包、压缩后的代码映射回源代码的技术,由于打包压缩后的代码并没有阅读性可言,一旦在开发中报错或者遇到问题,直接在混淆代码中debug
问题会带来非常糟糕的体验,sourceMap
可以帮助我们快速定位到源代码的位置,提高我们的开发效率。sourceMap
其实并不是Webpack
特有的功能,而是Webpack
支持sourceMap
,像JQuery
也支持souceMap
。
既然是一种源码的映射,那必然就需要有一份映射的文件,来标记混淆代码里对应的源码的位置,通常这份映射文件以.map
结尾,里边的数据结构大概长这样:
{
"version" : 3, // Source Map版本
"file": "out.js", // 输出文件(可选)
"sourceRoot": "", // 源文件根目录(可选)
"sources": ["foo.js", "bar.js"], // 源文件列表
"sourcesContent": [null, null], // 源内容列表(可选,和源文件列表顺序一致)
"names": ["src", "maps", "are", "fun"], // mappings使用的符号名称列表
"mappings": "A,AAAB;;ABCDE;" // 带有编码映射数据的字符串
}
//# sourceURL=/path/to/file.js.map
有了这段注释后,浏览器就会通过sourceURL
去获取这份映射文件,通过解释器解析后,实现源码和混淆代码之间的映射。因此sourceMap其实也是一项需要浏览器支持的技术。
loader
Webpack
最后打包出来的成果是一份Javascript
代码,实际上在Webpack
内部默认也只能够处理JS
模块代码,在打包过程中,会默认把所有遇到的文件都当作 JavaScript
代码进行解析,因此当项目存在非JS
类型文件时,我们需要先对其进行必要的转换,才能继续执行打包任务,这也是Loader
机制存在的意义
// webpack.config.js
module.exports = {
// ...other config
module: {
rules: [
{
test: /^your-regExp$/,
use: [
{
loader: 'loader-name-A',
},
{
loader: 'loader-name-B',
}
]
},
]
}
}
loader
是支持以数组的形式配置多个的,因此当Webpack
在转换该文件类型的时候,会按顺序链式调用每一个loader
,前一个loader
返回的内容会作为下一个loader
的入参。因此loader
的开发需要遵循一些规范,比如返回值必须是标准的JS
代码字符串,以保证下一个loader
能够正常工作,同时在开发上需要严格遵循“单一职责”,只关心loader
的输出以及对应的输出。
plugin
如果说Loader
负责文件转换,那么Plugin
便是负责功能扩展。Loader
和Plugin
作为Webpack
的两个重要组成部分,承担着两部分不同的职责。
上文已经说过,webpack
基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的插件任务,从而实现自己想要的功能。
既然基于发布订阅模式,那么知道Webpack
到底提供了哪些事件钩子供插件开发者使用是非常重要的,上文提到过compiler
和compilation
是Webpack
两个非常核心的对象,其中compiler
暴露了和 Webpack
整个生命周期相关的钩子(compiler-hooks),而compilation
则暴露了与模块和依赖有关的粒度更小的事件钩子(Compilation Hooks)
十连问
webpack 的构建流程是什么
初始化参数
:解析webpack配置参数,合并shell传入和webpack.config.js文件配置的参数,形成最后的配置结果;开始编译
:上一步得到的参数初始化compiler对象,注册所有配置的插件,插件 监听webpack构建生命周期的事件节点,做出相应的反应,执行对象的run方法开始执行编译;确定入口
:从配置的entry入口,开始解析文件构建AST语法树,找出依赖,递归下去;编译模块
:递归中根据文件类型和loader配置,调用所有配置的loader对文件进行转换,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;完成模块编译并输出
:递归完事后,得到每个文件结果,包含每个模块以及他们之间的依赖关系,根据entry或分包配置生成代码块chunk;输出完成
:输出所有的chunk到文件系统;
webpack 的热更新原理
其实是自己开启了express应用
,添加了对webpack编译的监听,添加了和浏览器的websocket长连接,当文件变化触发webpack进行编译并完成后,会通过sokcet消息告诉浏览器准备刷新
。而为了减少刷新的代价,就是不用刷新网页
,而是刷新某个模块
,webpack-dev-server可以支持热更新,通过生成 文件的hash值来比对需要更新的模块,浏览器再进行热替换
服务端
- 启动 webpack-dev-server服务器
- 创建webpack实例
- 创建server服务器
- 添加webpack的done事件回调
- 编译完成向客户端发送消息
- 创建express应用app
- 设置文件系统为内存文件系统
- 添加 webpack-dev-middleware 中间件
- 中间件负责返回生成的文件
- 启动webpack编译
- 创建http服务器并启动服务
- 使用sockjs在浏览器端和服务端之间建立一个websocket长连接
- 创建socket服务器
客户端
- webpack-dev-server/client端会监听到此hash消息
- 客户端收到ok消息后会执行reloadApp方法进行更新
- 在reloadApp中会进行判断,是否支持热更新,如果支持的话发生 webpackHotUpdate事件,如果不支持就直接刷新浏览器
- 在 webpack/hot/dev-server.js 会监听 webpackHotUpdate 事件
- 在check方法里会调用module.hot.check方法
- HotModuleReplacement.runtime请求Manifest
- 通过调用 JsonpMainTemplate.runtime 的 hotDownloadManifest方法
- 调用JsonpMainTemplate.runtime的hotDownloadUpdateChunk方法通过JSONP请求获取最新的模块代码
- 补丁js取回来或会调用 JsonpMainTemplate.runtime.js 的 webpackHotUpdate 方法
- 然后会调用 HotModuleReplacement.runtime.js 的 hotAddUpdateChunk方法动态更新 模块代码
- 然后调用hotApply方法进行热更
webpack 打包是hash码是如何生成的
1.webpack生态中存在多种计算hash的方式
hash
chunkhash
contenthash
hash代表每次webpack编译中生成的hash值,所有使用这种方式的文件hash都相同。每次构建都会使webpack计算新的hash。chunkhash基于入口文件及其关联的chunk形成,某个文件的改动只会影响与它有关联的chunk的hash值,不会影响其他文件contenthash根据文件内容创建。当文件内容发生变化时,contenthash发生变化
2.避免相同随机值
- webpack在
计算hash后分割chunk
。产生相同随机值可能是因为这些文件属于同一个chunk,可以将某个文件提到独立的chunk(如放入entry)
webpack 离线缓存静态资源如何实现
- 在配置webpack时,我们可以使用html-webpack-plugin来注入到和html一段脚本来实现将第三方或者共用资源进行 静态化存储在html中注入一段标识,例如
<% HtmlWebpackPlugin.options.loading.html %>
,在 html-webpack-plugin 中即可通过配置html属性,将script注入进去 - 利用 webpack-manifest-plugin 并通过配置 webpack-manifest-plugin ,生成 manifestjson 文件,用来对比js资源的差异,做到是否替换,当然,也要写缓存script
- 在我们做Cl以及CD的时候,也可以通过编辑文件流来实现静态化脚本的注入,来降低服务器的压力,提高性能
- 可以通过自定义plugin或者html-webpack-plugin等周期函数,动态注入前端静态化存储script
webpack 常见的plugin有哪些
ProvidePlugin
:自动加载模块,代替require和importhtml-webpack-plugin
可以根据模板自动生成html代码,并自动引用css和js文件extract-text-webpack-plugin
将js文件中引用的样式单独抽离成css文件DefinePlugin
编译时配置全局变量,这对开发模式和发布模式的构建允许不同的行为非常有用。HotModuleReplacementPlugin
热更新optimize-css-assets-webpack-plugin
不同组件中重复的css可以快速去重webpack-bundle-analyzer
一个webpack的bundle文件分析工具,将bundle文件以可交互缩放的treemap的形式展示。compression-webpack-plugin
生产环境可采用gzip压缩JS和CSShappypack
:通过多进程模型,来加速代码构建clean-wenpack-plugin
清理每次打包下没有使用的文件speed-measure-webpack-plugin
:可以看至U每个Loader和Plugin执行耗时(整个扌丁包耗时、每个Plugin和 Loader 耗时)webpack-bundle-analyzer
:可视化Webpack输出文件的体积(业务组件、依赖第三方模块
webpack 插件如何实现
- webpack本质是一个事件流机制,核心模块:tabable(Sync + Async)Hooks 构造出 === Compiler(编译) + Compiletion(创建bundles)
- compiler对象代表了完整的webpack环境配置。这个对象在启动webpack时被一次性建立,并配置好所有可操作的设置,包括options、loader和plugin。当在webpack环境中应用一插件时,插件将收到此compiler对象的引用。可以使用它来访问webpack的主环境
- compilation对象代表了一次资源版本构建。当运行webpack开发环境中间件时,每当检测到一个文件变化,就会创建一个新的compilation,从而生成一个新的编译资源。一个compilation对象表现了当前的模块资源、编译生成资源、变化的文件、以及被跟踪依赖的状态的信息。compilation对象也提供了很多关键时机的回调,以供插件做自定义处理时选择使用
- 创建一个插件函数,在其prototype上定义apply方法,指定一个webpack自身的事件钩子
- 函数内部处理webpack内部实例的特定数据
- 处理完成后,调用webpack提供的回调函数
function MyWebpackPlugin()(
};
// prototype 上定义 apply 方法
MyWebpackPlugin.prototype.apply=function(){
// 指定一个事件函数挂载到webpack
compiler.pluginCwebpacksEventHook"funcion (compiler)( console. log(“这是一个插件”);
// 功能完成调用后webpack提供的回调函数
callback()
})
复制代码
webpack有哪些常⻅的Loader
file-loader
:把⽂件输出到⼀个⽂件夹中,在代码中通过相对 URL 去引⽤输出的⽂件url-loader
:和 file-loader 类似,但是能在⽂件很⼩的情况下以 base64 的⽅式把⽂件内容注⼊到代码中去source-map-loader
:加载额外的 Source Map ⽂件,以⽅便断点调试image-loader
:加载并且压缩图⽚⽂件babel-loader
:把 ES6 转换成 ES5css-loader
:加载 CSS,⽀持模块化、压缩、⽂件导⼊等特性style-loader
:把 CSS 代码注⼊到 JavaScript 中,通过 DOM 操作去加载 CSS。eslint-loader
:通过 ESLint 检查 JavaScript 代码
webpack如何实现持久化缓存
服务端设置http缓存头
(cache-control)- 打包依赖和运行时到不同的chunk,
即作为splitChunk,因为他们几乎是不变的
延迟加载
:使用import()方式
,可以动态加载的文件分到独立的chunk,以得到自己的chunkhash保持hash值的稳定
:编译过程和文件内通的更改尽量不影响其他文件hash的计算,对于低版本webpack生成的增量数字id不稳定问题,可用hashedModuleIdsPlugin基于文件路径生成解决
如何⽤webpack来优化前端性能?
⽤webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运⾏快速⾼效。
压缩代码
:删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的 UglifyJsPlugin 和 ParallelUglifyPlugin 来压缩JS⽂件, 利⽤ cssnano (css-loader?minimize)来压缩css利⽤CDN加速
: 在构建过程中,将引⽤的静态资源路径修改为CDN上对应的路径。可以利⽤webpack对于 output 参数和各loader的 publicPath 参数来修改资源路径Tree Shaking
: 将代码中永远不会⾛到的⽚段删除掉。可以通过在启动webpack时追加参数 --optimize-minimize 来实现Code Splitting
: 将代码按路由维度或者组件分块(chunk),这样做到按需加载,同时可以充分利⽤浏览器缓存提取公共第三⽅库
: SplitChunksPlugin插件来进⾏公共模块抽取,利⽤浏览器缓存可以⻓期缓存这些⽆需频繁变动的公共代码
webpack treeShaking机制的原理
-
treeShaking 也叫
摇树优化
,是一种通过移除多于代码,来优化打包体积的,生产环境默认开启
。 -
可以在
代码不运行
的状态下,分析出不需要的代码
; -
利用
es6模块
的规范- ES6 Module引入进行
静态分析
,故而编译的时候正确判断到底加载了那些模块
- 静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码
- ES6 Module引入进行