前端面试题之——webpack会问到的点
大家好,这是一个小白前端的更文挑战。(写错的各位看官包容) 这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战。
前言
面试会问到的🈶️以下点,常用的loader和plugin这个应该大家都是知道,就不做赘述,其中比较重要的应该是最后一点,就是关于提高性能和体验的
- webpack与grunt、gulp的不同?
- 与webpack类似的工具还有哪些?谈谈你为什么最终选择(或放弃)使用webpack?
- 有哪些常见的Loader?他们是解决什么问题的?
- 有哪些常见的Plugin?他们是解决什么问题的?
- Loader和Plugin的不同?
- 是否写过Loader和Plugin?描述一下编写loader或plugin的思路?
- 如何利用webpack来优化前端性能?(提高性能和体验)
下面是我整理的关于以上问题的答案:
webpack与之相似的
rollup
- 适用于基础库的打包,如vue、react
parcel
- 适用于简单的实验性项目,他可以满足低门槛的快速看到效果
Loader
配置
- Loader在module.rules中配置
常见的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
Plugin在plugins中单独配置
常见的Plugin
-
define-plugin
- 定义环境变量
-
commons-chunk-plugin
- 提取公共代码
-
uglifyjs-webpack-plugin
- 通过UglifyES压缩ES6代码
-
...
Loader和Plugin的不同
loader
- webpack原生是只能解析js文件,如果想将其他文件也打包的话,就会用到loader。 所以Loader的作用是让webpack拥有了加载和解析非JavaScript文件的能力
plugin
- Plugin可以扩展webpack的功能,让webpack具有更多的灵活性
文件指纹是?
文件指纹是打包后输出的文件名的后缀
使用
-
Hash
- 和整个项目的构建相关,只要项目文件有修改,整个项目构建的 hash 值就会更改
-
Chunkhash
- 和 Webpack 打包的 chunk 有关,不同的 entry 会生出不同的 chunkhash
-
Contenthash
- 根据文件内容来定义 hash,文件内容不变,则 contenthash 不变
在实际工程中,配置文件上百行乃是常事,如何保证各个loader按照预想方式工作
使用 enforce 强制执行 loader 的作用顺序
pre 代表在所有正常 loader 之前执行
post 是所有 loader 之后执行
如何优化 Webpack 的构建速度
使用高版本的 Webpack 和 Node.js
多进程/多实例构建:HappyPack(不维护了)、thread-loader
压缩代码
-
多进程并行压缩
- webpack-paralle-uglify-plugin
- uglifyjs-webpack-plugin 开启 parallel 参数 (不支持ES6)
- terser-webpack-plugin 开启 parallel 参数
-
通过 mini-css-extract-plugin 提取 Chunk 中的 CSS 代码到单独文件,通过 css-loader 的 minimize 选项开启 cssnano 压缩 CSS。
图片压缩
- 使用基于 Node 库的 imagemin (很多定制选项、可以处理多种图片格式)
- 配置 image-webpack-loader
缩小打包作用域
- exclude/include (确定 loader 规则范围)
- resolve.modules 指明第三方模块的绝对路径 (减少不必要的查找)
- resolve.mainFields 只采用 main 字段作为入口文件描述字段 (减少搜索步骤,需要考虑到所有运行时依赖的第三方模块的入口文件描述字段)
- resolve.extensions 尽可能减少后缀尝试的可能性
- noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)
- IgnorePlugin (完全排除模块)
- 合理使用alias
提取页面公共资源
-
基础包分离
- 使用 html-webpack-externals-plugin,将基础包通过 CDN 引入,不打入 bundle
-
使用 SplitChunksPlugin 进行(公共脚本、基础包、页面公共文件)分离(Webpack4内置) ,替代了 CommonsChunkPlugin 插件
DLL
- 使用 DllPlugin 进行分包,使用 DllReferencePlugin(索引链接) 对 manifest.json 引用,让一些基本不会改动的代码先打包成静态资源,避免反复编译浪费时间。
- HashedModuleIdsPlugin 可以解决模块数字id问题
充分利用缓存提升二次构建速度
- babel-loader 开启缓存
- terser-webpack-plugin 开启缓存
- 使用 cache-loader 或者 hard-source-webpack-plugin
Tree shaking
Scope hoisting
- 构建后的代码会存在大量闭包,造成体积增大,运行代码时创建的函数作用域变多,内存开销变大。Scope hoisting 将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突
- 必须是ES6的语法,因为有很多第三方库仍采用 CommonJS 语法,为了充分发挥 Scope hoisting 的作用,需要配置 mainFields 对第三方模块优先采用 jsnext:main 中指向的ES6模块化语法
动态Polyfill
- 建议采用 polyfill-service 只给用户返回需要的polyfill,社区维护
是否写过Plugin?简单描述一下编写Plugin的思路?
compiler 暴露了和 Webpack 整个生命周期相关的钩子
compilation 暴露了与模块和依赖有关的粒度更小的事件钩子
插件需要在其原型上绑定apply方法,才能访问 compiler 实例
Babel原理
解析:将代码转换成 AST
- 语法分析:分析token流(上面生成的数组)并生成 AST
- 词法分析:将代码(字符串)分割为token流,即语法单元成的数组
转换:访问 AST 的节点进行变换操作生产新的 AST
- Taro就是利用 babel 完成的小程序语法转换
生成:以新的 AST 为基础生成代码
转载自:https://juejin.cn/post/6995098632147959815