webpack的⾯试题
问题⼀览
1. webpack与grunt、gulp的不同?
2. 与webpack类似的⼯具还有哪些?谈谈你为什么最终选择(或放弃)使⽤webpack?
3. 有哪些常见的Loader?他们是解决什么问题的?
4. 有哪些常见的Plugin?他们是解决什么问题的?
5. Loader和Plugin的不同?
6. webpack的构建流程是什么?从读取配置到输出⽂件这个过程尽量说全
7. 是否写过Loader和Plugin?描述⼀下编写loader或plugin的思路?
8. webpack的热更新是如何做到的?说明其原理?
9. 如何利⽤webpack来优化前端性能?(提⾼性能和体验)
10. 如何提⾼webpack的构建速度?
11. 怎么配置单页应⽤?怎么配置多页应⽤?
12. npm打包时需要注意哪些?如何利⽤webpack来更好的构建?
13. 如何在vue项⽬中实现按需加载?
问题解答
1. 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做何种解析和加⼯
jquery框架面试题对于知识背景来说
gulp更像后端开发者的思路,需要对于整个流程了如指掌 webpack更倾向于前端开发者的思路
2. 与webpack类似的⼯具还有哪些?谈谈你为什么最终选择(或放弃)使⽤webpack?
同样是基于⼊⼝的打包⼯具还有以下⼏个主流的:
webpack
rollup
parcel
从应⽤场景上来看:
webpack适⽤于⼤型复杂的前端站点构建
rollup适⽤于基础库的打包,如vue、react
parcel适⽤于简单的实验性项⽬,他可以满⾜低门槛的快速看到效果
由于parcel在打包过程中给出的调试信息⼗分有限,所以⼀旦打包出错难以调试,所以不建议复杂的项⽬使⽤parcel
3.有哪些常见的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 代码
4.有哪些常见的Plugin?他们是解决什么问题的?
define-plugin:定义环境变量
commons-chunk-plugin:提取公共代码
uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
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 的运⾏结果。
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 改变输出结果。
8.webpack的热更新是如何做到的?说明其原理?
webpack的热更新⼜称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不⽤刷新浏览器⽽将新变更的模块替换掉旧的模块。
原理:
⾸先要知道server端和client端都做了处理⼯作
1. 第⼀步,在 webpack 的 watch 模式下,⽂件系统中某⼀个⽂件发⽣修改,webpack 监听到⽂件变化,根据配置⽂件对模块重新编
译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。
2. 第⼆步是 webpack-dev-server 和 webpack 之间的接⼝交互,⽽在这⼀步,主要是 dev-server 的中间件 webpack-dev-
middleware 和 webpack 之间的交互,webpack-dev-middleware 调⽤ webpack 暴露的 API对代码变化进⾏监控,并且告诉webpack,将代码打包到内存中。
3. 第三步是 webpack-dev-server 对⽂件变化的⼀个监控,这⼀步不同于第⼀步,并不是监控代码变化重新打包。当我们在配置⽂件中
配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置⽂件夹中静态⽂件的变化,变化后会通知浏览器端对应⽤进⾏ live reload。注意,这⼉是浏览器刷新,和 HMR 是两个概念。
4. 第四步也是 webpack-dev-server 代码的⼯作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之
间建⽴⼀个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态⽂件变化的信息。浏览器端根据这些 socket 消息进⾏不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后⾯的步骤根据这⼀ hash 值来进⾏模块热替换。
5. webpack-dev-server/client 端并不能够请求更新的代码,也不会执⾏热更模块操作,⽽把这些⼯作⼜交回给了
webpack,webpack/hot/dev-server 的⼯作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进⾏模块热更新。当然如果仅仅是刷新浏览器,也就没有后⾯那些步骤了。
6. HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上⼀步传递给他的新模块的 hash 值,它通过
JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回⼀个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。这就是上图中 7、8、9 步骤。
7. ⽽第 10 步是决定 HMR 成功与否的关键步骤,在该步骤中,HotModulePlugin 将会对新旧模块进⾏对⽐,决定是否更新模块,在
决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引⽤。
8. 最后⼀步,当 HMR 失败后,回退到 live reload 操作,也就是进⾏浏览器刷新来获取最新打包代码。
9.如何利⽤webpack来优化前端性能?(提⾼性能和体验)
⽤webpack优化前端性能是指优化webpack的输出结果,让打包的最终结果在浏览器运⾏快速⾼效。
压缩代码。删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的UglifyJsPlugin和ParallelUglifyPlugin来压缩JS ⽂件, 利⽤cssnano(css-loader?minimize)来压缩css
利⽤加速。在构建过程中,将引⽤的静态资源路径修改为CDN上对应的路径。可以利⽤webpack对于output参数和各loader的publicPath参数来修改资源路径
删除死代码(Tree Shaking)。将代码中永远不会⾛到的⽚段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
提取公共代码。
10.如何提⾼webpack的构建速度?
1. 多⼊⼝情况下,使⽤CommonsChunkPlugin来提取公共代码
2. 通过externals配置来提取常⽤库
3. 利⽤DllPlugin和DllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引⽤但是绝对不会修改的npm包来进⾏预编译,再
通过DllReferencePlugin将预编译的模块加载进来。
4. 使⽤Happypack 实现多线程加速编译
5. 使⽤webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采⽤了多核并⾏压缩来提升压缩速度
6. 使⽤Tree-shaking和Scope Hoisting来剔除多余代码
11.怎么配置单页应⽤?怎么配置多页应⽤?
单页应⽤可以理解为webpack的标准模式,直接在entry中指定单页应⽤的⼊⼝即可,这⾥不再赘述
多页应⽤的话,可以使⽤webpack的 AutoWebPlugin来完成简单⾃动化的构建,但是前提是项⽬的⽬
录结构必须遵守他预设的规范。 多页应⽤中要注意的是:
每个页⾯都有公共的代码,可以将这些代码抽离出来,避免重复的加载。⽐如,每个页⾯都引⽤了同⼀套css样式表
随着业务的不断扩展,页⾯可能会不断的追加,所以⼀定要让⼊⼝的配置⾜够灵活,避免每次添加新页⾯还需要修改构建配置
12.npm打包时需要注意哪些?如何利⽤webpack来更好的构建?
Npm是⽬前最⼤的 JavaScript 模块仓库,⾥⾯有来⾃全世界开发者上传的可复⽤模块。你可能只是JS模块的使⽤者,但是有些情况你也会去选择上传⾃⼰开发的模块。 关于NPM模块上传的⽅法可以去官⽹上进⾏学习,这⾥只讲解如何利⽤webpack来构建。
NPM模块需要注意以下问题:
1. 要⽀持CommonJS模块化规范,所以要求打包后的最后结果也遵守该规则。
2. Npm模块使⽤者的环境是不确定的,很有可能并不⽀持ES6,所以打包的最后结果应该是采⽤ES5编写的。并且如果ES5是经过转换
的,请最好连同SourceMap⼀同上传。
3. Npm包⼤⼩应该是尽量⼩(有些仓库会限制包⼤⼩)
4. 发布的模块不能将依赖的模块也⼀同打包,应该让⽤户选择性的去⾃⾏安装。这样可以避免模块应⽤者再次打包时出现底层模块被重
复打包的情况。
5. UI组件类的模块应该将依赖的其它资源⽂件,例如.css⽂件也需要包含在发布的模块⾥。
基于以上需要注意的问题,我们可以对于webpack配置做以下扩展和优化:
1. CommonJS模块化规范的解决⽅案: 设置output.libraryTarget='commonjs2'使输出的代码符合CommonJS2 模块化规范,以供
给其它模块导⼊使⽤
2. 输出ES5代码的解决⽅案:使⽤babel-loader把 ES6 代码转换成 ES5 的代码。再通过开启devtool: 'source-map'输出SourceMap
以发布调试。
3. Npm包⼤⼩尽量⼩的解决⽅案:Babel 在把 ES6 代码转换成 ES5 代码时会注⼊⼀些辅助函数,最终导致每个输出的⽂件中都包含这
段辅助函数的代码,造成了代码的冗余。解决⽅法是修改.babelrc⽂件,为其加⼊transform-runtime插件
4. 不能将依赖模块打包到NPM模块中的解决⽅案:使⽤externals配置项来告诉webpack哪些模块不需要打包。
5. 对于依赖的资源⽂件打包的解决⽅案:通过css-loader和extract-text-webpack-plugin来实现,配置如下:
13.如何在vue项⽬中实现按需加载?
Vue UI组件库的按需加载 为了快速开发前端项⽬,经常会引⼊现成的UI组件库如ElementUI、iView等,但是他们的体积和他们所提供的功能⼀样,是很庞⼤的。 ⽽通常情况下,我们仅仅需要少量的⼏个组件就⾜够了,但是我们却将庞⼤的组件库打包到我们的源码中,造成了不必要的开销。
不过很多组件库已经提供了现成的解决⽅案,如Element出品的babel-plugin-component和AntDesign出品的babel-plugin-import 安装以上插件后,在.babelrc配置中或babel-loader的参数中进⾏设置,即可实现组件按需加载了。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论