1)模块打包。可以将不同模块的文件打包整合在一起,并且保证它们之间的引用正确,执行有序。利用打包我们就可以在开发的时候根据我们自己的业务自由划分文件模块,保证项目结构的清晰和可读性。
2)编译兼容。在前端的“上古时期”,手写一堆浏览器兼容代码一直是令前端工程师头皮发麻的事情,而在今天这个问题被大大的弱化了,通过webpack的Loader机制,不仅仅可以帮助我们对代码做polyfill,还可以编译转换诸如.less, .vue, .jsx这类在浏览器无法识别的格式文件,让我们在开发的时候可以使用新特性和新语法做开发,提高开发效率。
3)能力扩展。通过webpack的Plugin机制,我们在实现模块化打包和编译兼容的基础上,可以进一步实现诸如按需加载,代码压缩等一系列功能,帮助我们进一步提高自动化程度,工程效率以及打包输出的质量。
2、webpack打包的过程(必考)
构建流程
webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
1、初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
2、开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
3、确定入口:根据配置中的 entry 找出所有的入口文件
4、编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
5、完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
6、输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
7、输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。 在以上过程中,webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 webpack 提供的 API 改变 webpack 的运行结果。
****3、****有哪些常见的Loader?你用过哪些Loader?
1.raw-loader:加载文件原始内容(utf-8)
2.file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件 (处理图片和字体)
3.source-map-loader:加载额外的 Source Map 文件,以方便断点调试
4.svg-inline-loader:将压缩后的 SVG 内容注入代码中
5.image-loader:加载并且压缩图片文件
6.json-loader 加载 JSON 文件(默认包含)
7.babel-loader:把 ES6 转换成 ES5
8.sass-loader:将SCSS/SASS代码转换成CSS
9.eslint-loader:通过 ESLint 检查 JavaScript 代码
10.vue-loader:加载 Vue.js 单文件组件
11.cache-loader: 可以在一些性能开销较大的 Loader 之前添加,目的是将结果缓存到磁盘里
****4、****有哪些常见的Plugin?你用过哪些Plugin?
(这大兄弟好像听上瘾了,继续开启常规操作)
define-plugin:定义环境变量 (Webpack4 之后指定 mode 会自动配置)
ignore-plugin:忽略部分文件
web-webpack-plugin:可方便地为单页应用输出 HTML,比 html-webpack-plugin 好用
terser-webpack-plugin: 支持压缩 ES6 (Webpack4)
webpack-parallel-uglify-plugin: 多进程执行代码压缩,提升构建速度
mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载
clean-webpack-plugin: 目录清理
speed-measure-webpack-plugin: 可以看到每个 Loader 和 Plugin 执行耗时 (整个打包耗时、每个 Plugin 和 Loader 耗时)
webpack-bundle-analyzer: 可视化 Webpack 输出文件的体积 (业务组件、依赖第三方模块)
**5、**是否写过Loader?简单描述一下编写loader的思路?
从上面的打包代码我们其实可以知道,Webpack最后打包出来的成果是一份Javascript代码,实际上在Webpack内部默认也只能够处理JS模块代码,在打包过程中,会默认把所有遇到的文件都当作 JavaScript代码进行解析,因此当项目存在非JS类型文件时,我们需要先对其进行必要的转换,才能继续执行打包任务,这也是Loader机制存在的意义。
Loader的配置使用我们应该已经非常的熟悉:
// webpack.config.js
module.exports = {
// …other config
module: {
rules: [
{
test: /^your-regExp$/,
use: [{ loader: “loader-name-A” }, { loader: “loader-name-B” }],
},
],
},
};
/ webpack
通过配置可以看出,针对每个文件类型,loader是支持以数组的形式配置多个的,因此当Webpack在转换该文件类型的时候,会按顺序链式调用每一个loader,前一个loader返回的内容会作为下一个loader的入参。因此loader的开发需要遵循一些规范,比如返回值必须是标准的JS代码字符串,以保证下一个loader能够正常工作,同时在开发上需要严格遵循“单一职责”,只关心loader的输出以及对应的输出。
loader函数中的this上下文由webpack提供,可以通过this对象提供的相关属性,获取当前loader需要的各种信息数据,事实上,这个this指向了一个叫loaderContext的loader-runner特有对象。有兴趣的小伙伴可以自行阅读源码。
module.expor
module.exports = function (source) {
const content = doSomeThing2JsString(source);
// 如果 loader 配置了 options 对象,那么this.query将指向 options
const options = this.query;
// 可以用作解析其他模块路径的上下文
console.log(“this.context”);
/*
* * this.callback 参数:
* * error:Error | null,当 loader 出错时向外抛出一个 error
* * content:String | Buffer,经过 loader 编译后需要导出的内容
* * sourceMap:为方便调试生成的编译后内容的 source map
* * ast:本次编译生成的 AST 静态语法树,之后执行的 loader 可以直接使用这个 AST,进而省去重复生成 AST 的过程
* */
this.callback(null, content); // or return content;
};
ts = function (sourceoader 配置了 options 对象,那
**6、**是否写过Plugin?简单描述一下编写plugin的思路?
如果说Loader负责文件转换,那么Plugin便是负责功能扩展。Loader和Plugin作为Webpack的两个重要组成部分,承担着两部分不同的职责。
上文已经说过,webpack基于发布订阅模式,在运行的生命周期中会广播出许多事件,插件通过监听这些事件,就可以在特定的阶段执行自己的插件任务,从而实现自己想要的功能。
既然基于发布订阅模式,那么知道Webpack到底提供了哪些事件钩子供插件开发者使用是非常重要的,上文提到过compiler和compilation是Webpack两个非常核心的对象,其中compiler暴露了和 Webpack整个生命周期相关的钩子(compiler-hooks[2]),而compilation则暴露了与模块和依赖有关的粒度更小的事件钩子(Compilation Hooks[3])。
Webpack的事件机制基于webpack自己实现的一套Tapable事件流方案
// Tapab = r
// Tapable的简单使用
const { SyncHook } = require(“tapable”);
class Car {
constructor() {
// 在this.hooks中定义所有的钩子事件
this.hooks = {
accelerate: new SyncHook([“newSpeed”]),
brake: new SyncHook(),
calculateRoutes: new AsyncParallelHook([
“source”,
“target”,
“routesList”,
]),
};
} /* … */
}
const myCar = new Car(); // 通过调用tap方法即可增加一个消费者,订阅对应的钩子事件了
myCar.hooks.brake.tap(“WarningLampPlugin”, () => warningLamp.on());
construc
Plugin的开发和开发Loader一样,需要遵循一些开发上的规范和原则:
插件必须是一个函数或者是一个包含 apply 方法的对象,这样才能访问compiler实例;
传给每个插件的 compiler 和 compilation 对象都是同一个引用,若在一个插件中修改了它们身上的属性,会影响后面的插件;
异步的事件需要在插件处理完任务时调用回调函数通知 Webpack 进入下一个流程,不然会卡住;
了解了以上这些内容,想要开发一个 Webpack Plugin,其实也并不困难。
class MyPlugin {
apply(compile
class MyPlugin {
apply(compiler) {
// 找到合适的事件钩子,实现自己的插件功能
compiler.hooks.emit.tap(“MyPlugin”, (compilation) => {
// compilation: 当前打包构建流程的上下文
console.log(compilation); // do something…
});
}
}
的事件钩子,实现自己的插件
7、webpack中loader和plugins的区别
Loader:
Loader让webpack能够处理不同的文件。loader可以将所有类型的文件转换为webpack能够处理的有效模块,然后利用webpack的打包能力,对他们进行处理。本质上,webpack loader将所有类型的文件,转换为应用程序的依赖图可以直接引用的模块。例如:loader可以将sass,less文件的写法转换成css,而不在使用其他转换工具。可以将ES6或者ES7的代码,转换成大多数浏览器兼容的JS代码。可以将React中的JSX转换成JavaScript代码。
Plugins:
loader被用于转换某些类型的模块,而插件则可以用于执行广泛的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。
想要使用一个插件,你只需要 require()它,然后将它添加到plugins数组中。多数插件可以通过选项自定义。你也可以在一个配置中因为不同目的而多次使用同一个插件,这时需要通过使用new操作符来创建它的一个实例。例如js压缩、html打包打包HTML,要将 HTML 文件从 src 目录下打包到 dist 目录下,需要借助 html-webpack-plugin 插件。该插件需要安装。
****8、****source map是什么?生产环境怎么用?
source map 是将编译、打包、压缩后的代码映射回源代码的过程。打包压缩后的代码不具备良好的可读性,想要调试源码就需要 soucre map。
map文件只要不打开开发者工具,浏览器是不会加载的。
线上环境一般有三种处理方案:
hidden-source-map:借助第三方错误监控平台 Sentry 使用
nosources-source-map:只会显示具体行数以及查看源代码的错误栈。安全性比 sourcemap 高
sourcemap:通过 nginx 设置将 .map 文件只对白名单开放(公司内网)
注意:避免在生产中使用 inline- 和 eval-,因为它们会增加 bundle 体积大小,并降低整体性能。
9、webpack打包如何减少体积
①去除不必要的插件
②提取第三方库,使用cdn在index.html中外部引入,cdn+webpakc的externals
③Gzip加速
④代码压缩
⑤异步加载模块
10、加快webpack打包速度
①通过babel-loader的cache配置来缓存babel的编译结果。
②推荐采用 webpack-parallel-uglify-plugin 插件,她可以并行运行 UglifyJS 插件,更加充分而合理的使用 CPU 资源,这可以大大减少的构建时间
③Happypack 的处理思路是:将原有的 webpack 对 loader 的执行过程,从单一进程的形式扩展多进程模式,从而加速代码构建;原本的流程保持不变,这样可以在不修改原有配置的基础上,来完成对编译过程的优化,
11、如何优化webpack构建的性能
一、减少代码体积 1.使用CommonsChunksPlugin 提取多个chunk之间的通用模块,减少总体代码体积
2.把部分依赖转移到CDN上,避免每次编译过程都由Webpack处理
3.对一些组件库采用按需加载,避免无用的代码
二、减少目录检索范围
·在使用loader的时候,通过制定exclude和include选项,减少loader遍历的目录范围,从而加快webpack编译速度
三、减少检索路经:resolve.alias可以配置webpack模块解析的别名,对于比较深的解析路经,可以对其配置alias
四、我们把开发中的所有资源(图片,js、css文件)都看成模块,通过loader和plugins来对资源进行处理,打包成符合生产环节部署的前端资源。
****12、****文件监听原理呢?
在发现源码发生变化时,自动重新构建出新的输出文件。
Webpack开启监听模式,有两种方式:
启动 webpack 命令时,带上 --watch 参数
在配置 webpack.config.js 中设置 watch:true
缺点:每次需要手动刷新浏览器
原理:轮询判断文件的最后编辑时间是否变化,如果某个文件发生了变化,并不会立刻告诉监听者,而是先缓存起来,等 aggregateTimeout 后再执行。
module.export = {
// 默认false,也就是不开启
watch: true,
// 只有开启监听模式时,watchOptions才有意义
watchOptions: {
// 默认为空,不监听的文件或者文件夹,支持正则匹配
ignored: /node_modules/,
// 监听到变化发生后会等300ms再去执行,默认300ms
aggregateTimeout:300,
// 判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次
poll:1000
}
}
复制代码
****13、****说一下 Webpack 的热更新原理吧
最后
前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
watch: true,
// 只有开启监听模式时,watchOptions才有意义
watchOptions: {
// 默认为空,不监听的文件或者文件夹,支持正则匹配
ignored: /node_modules/,
// 监听到变化发生后会等300ms再去执行,默认300ms
aggregateTimeout:300,
// 判断文件是否发生变化是通过不停询问系统指定文件有没有变化实现的,默认每秒问1000次
poll:1000
}
}
复制代码
****13、****说一下 Webpack 的热更新原理吧
最后
前端CSS面试题文档,JavaScript面试题文档,Vue面试题文档,大厂面试题文档
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
[外链图片转存中…(img-ZZRarZ0v-1714570190481)]
[外链图片转存中…(img-NZJSL6Bt-1714570190483)]