面试题
[toc]
工程化
如何简述对Webpack的认识?
WebPack是一个模块打包工具,可以使用 WebPack管理模块依赖,并编译输岀模块所需的静态文件。它能够很好地管理与打包Web开发中所用到的HTML、 JavaScript 、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源, WebPack有对应的模块加载器。Web Pack模块打包器会分析模块间的依赖关系,最后生成优化且合并后的静态资源。
WebPack的两大特色如下:
- 代码切割( code splitting)
- loader可以处理各种类型的静态文件,并且支持串行操作WebPack以 CommonJS规范来书写代码,但对 AMD/CMD的支持也很全面,方便对项目进行代码迁移。
WebPack具有 require.js和 browserify的功能,但也有很多自己的新特性:
- 对 CommonJS、AMD、ES6的语法实现了兼容。
- 对 JavaScript、CSS、图片等资源文件都支持打包
- 串联式模块加载器和插件机制,让其具有更好的灵活性和扩展性,例如提供对CoffeeScript、 EMAScript 6的支持
- 有独立的配置文件 webpack.config. js。
- 可以将代码切割成不同的块,实现按需加载,缩短了初始化时间。
- 支持 SourceUrls和 SourceMaps,易于调试。
- 具有强大的 Plugin接口,大多是内部插件,使用起来比较灵活
- 使用异步I/O,并具有多级缓存,这使得 WebPack速度很快且在增量编译上更加快。
webpack 中 loader 和 plugin 的区别是什么?
loader 实现对不同格式文件的处理,比如将Scss转换为CSS,或将 TypeScript转化为Javascript。可以编译文件,从而使其能够添加到依赖关系中。loader是 Webpack最重要的部分之一。通过使用不同的 loader,我们能够调用外部的脚本或者工具,实现对不同格式文件的处理。loader需要在 webpack.config.js里单独用 module进行配置。
plugin 是一个扩展器,它丰富了 webpack 本身,针对是 loader 结束后,webpack 打包的整个过程,它并不直接操作文件,而是基于事件机制工作,会监听 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 代码
webpack 热更新原理是如何做到在不刷新浏览器的前提下更新页面的?
webpack的热更新又称热替换(Hot Module Replacement),缩写为HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。首先要知道server端和client端都做了处理工作
- 在 webpack 的 watch 模式下,文件系统中某一个文件发生修改,webpack 监听到文件变化,根据配置文件对模块重新编译打包,并将打包后的代码通过简单的 JavaScript 对象保存在内存中。
- webpack-dev-server 和 webpack 之间的接口交互,而在这一步,主要是 dev-server 的中间件 webpack-dev-middleware 和 webpack 之间的交互,webpack-dev-middleware 调用 webpack 暴露的 API对代码变化进行监控,并且告诉 webpack,将代码打包到内存中。
- webpack-dev-server 对文件变化的一个监控,这一步不同于第一步,并不是监控代码变化重新打包。当我们在配置文件中配置了devServer.watchContentBase 为 true 的时候,Server 会监听这些配置文件夹中静态文件的变化,变化后会通知浏览器端对应用进行 live reload。注意,这儿是浏览器刷新,和 HMR 是两个概念。
- webpack-dev-server 代码的工作,该步骤主要是通过 sockjs(webpack-dev-server 的依赖)在浏览器端和服务端之间建立一个 websocket 长连接,将 webpack 编译打包的各个阶段的状态信息告知浏览器端,同时也包括第三步中 Server 监听静态文件变化的信息。浏览器端根据这些 socket 消息进行不同的操作。当然服务端传递的最主要信息还是新模块的 hash 值,后面的步骤根据这一 hash 值来进行模块热替换。
- webpack-dev-server/client 端并不能够请求更新的代码,也不会执行热更模块操作,而把这些工作又交回给了 webpack,webpack/hot/dev-server 的工作就是根据 webpack-dev-server/client 传给它的信息以及 dev-server 的配置决定是刷新浏览器呢还是进行模块热更新。当然如果仅仅是刷新浏览器,也就没有后面那些步骤了。
- HotModuleReplacement.runtime 是客户端 HMR 的中枢,它接收到上一步传递给他的新模块的 hash 值,它通过 JsonpMainTemplate.runtime 向 server 端发送 Ajax 请求,服务端返回一个 json,该 json 包含了所有要更新的模块的 hash 值,获取到更新列表后,该模块再次通过 jsonp 请求,获取到最新的模块代码。
- HotModulePlugin 将会对新旧模块进行对比,决定是否更新模块,在决定更新模块后,检查模块之间的依赖关系,更新模块的同时更新模块间的依赖引用。
- 当 HMR 失败后,回退到 live reload 操作,也就是进行浏览器刷新来获取最新打包代码。
webpack的构建流程是什么?
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数;
- 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译;
- 确定入口:根据配置中的 entry 找出所有的入口文件;
- 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理;
- 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系;
- 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会;
- 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
如何用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的打包速度?
- happypack: 利用进程并行编译loader,利用缓存来使得 rebuild 更快,遗憾的是作者表示已经不会继续开发此项目,类似的替代者是thread-loader
- 外部扩展(externals): 将不怎么需要更新的第三方库脱离webpack打包,不被打入bundle中,从而减少打包时间,比如jQuery用script标签引入
- dll: 采用webpack的 DllPlugin 和 DllReferencePlugin 引入dll,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间
- 利用缓存: webpack.cache、babel-loader.cacheDirectory、HappyPack.cache都可以利用缓存提高rebuild效率
- 缩小文件搜索范围: 比如babel-loader插件,如果你的文件仅存在于src中,那么可以
include: path.resolve(__dirname, 'src'),当然绝大多数情况下这种操作的提升有限,除非不小心build了node_modules文件
说说webpack proxy工作原理?为什么能解决跨域?
webpack proxy,即webpack提供的代理服务,基本行为就是接收客户端发送的请求后转发给其他服务器,其目的是为了便于开发者在开发模式下解决跨域问题(浏览器安全策略限制,想要实现代理首先需要一个中间服务器,webpack中提供服务器的工具为webpack-dev-server
webpack-dev-server是 webpack 官方推出的一款开发工具,将自动编译和自动刷新浏览器等一系列对开发友好的功能全部集成在了一起,目的是为了提高开发者日常的开发效率,只适用在开发阶段
跨域原理是服务器与服务器之间不存在跨域行为
tree shaking的原理是什么?
- ES6 Module引入进行静态分析,故而编译的时候正确判断到底加载了那些模块
- 静态分析程序流,判断那些模块和变量未被使用或者引用,进而删除对应代码
