每天一道面试题(13) - webpack, babel, loader等原理解析

偷懒了这么些天, 终于又开始更新啦~~

文章目录

前端代码为什么要进行构建和打包?

  • 体积更小(压缩, 合并), 加载更快
  • 编译高级语言和语法(TS,ES6,模块化,scss)
  • 兼容性和错误检查(plyfill, postcss, eslint)
  • 统一, 高效的开发环境
  • 统一的构建流程和产出标准
  • 集成公司构建规范(提测,上线等)

webpack的打包原理

参考链接: webpack原理

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程 :

  1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。
  2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译。
  3. 确定入口:根据配置中的 entry 找出所有的入口文件。
  4. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
  5. 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

每天一道面试题(13) - webpack, babel, loader等原理解析

在5完成编译阶段, 找到每个模块之间的依赖关系 这一步骤中, 是通过AST语法树来实现的

loader其实可以理解为一个函数, 输入是源代码, 经过处理后输出转换后的代码
在配置时经常通过数组的方式使用多个loader, 由于webpack内部是使用reduceRight方式进行处理, 所以loader数组的执行顺序是从右到左

loader的执行时机是在打包过程中;
在打包过程中webpack会在生命周期的不同阶段发布不同的事件, plugin插件通过监听这些事件从而在合适的时机执行

webpack的打包流程就像一条生产线,只有完成当前处理后才能交给下一个流程去处理。
Webpack通过Tapable来组织这条复杂的生产线。 Webpack在运行过程中会广播事件,插件只需要监听它所关心的事件,就能加入到这条生产线中,去改变生产线的运作。 Webpack的事件流机制保证了插件的有序性,使得整个系统扩展性很好。

loader和plugin的区别?

loader即为文件转换器,操作的是文件,将文件A通过loader转换成文件B,是一个单纯的文件转化过程。

plugin即为插件,是一个扩展器,丰富webpack本身,增强功能 ,针对的是在loader结束之后,webpack打包的整个过程,他并不直接操作文件,而是基于事件机制工作,监听webpack打包过程中的某些节点,执行广泛的任务。

编写loader和plugin

参考链接

Babel是什么, 与webpack的区别

Babel 是编译工具,把高版本语法编译成低版本语法,或者将文件按照自定义规则转换成js语法。

webpack 是打包工具,定义入口文件,将所有模块引入整理后,通过 loader 和 plugin 处理后,打包输出。

webpack 通过 babel-loader 使用 Babel

Babel的工作原理

babel在将高版本的语法编译成低版本语法时, 是先将代码转换成AST, 再对AST进行处理, 最后再转换为代码.

具体流程如下

  1. 词法分析: 将代码解析为token
  2. 语法分析: 将token解析为抽象语法树(AST)

babel-parser负责将输入代码转换为AST

  1. 变换(transforming): 对AST进行变换处理

babel-traverse负责提供深度遍历 ast 节点的能力(traverse 本身并不负责转换 ast,只是提供遍历 ast 节点的能力,具体的转换逻辑交给各种 babel 插件来实现,这也在很大程度上提高了 babel 的扩展性,日后如果新增了语言特性不用修改或很少修改核心代码,只需要编写相应的插件来转换新的语法就行了

  1. 生成目标代码(generating): 通过递归处理AST ,打印出目标代码

babel-generator 负责根据新的 ast 打印出目标代码

@babel-core这个包包含了@babel/parser, @babel/traverse, @babel/generator

babel-runtime和babel-polyfill作用及区别

Babel默认只转换新的JavaScript语法,而不转换新的API。 例如,Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。 如果想使用这些新的对象和方法,则需要为当前环境提供一个polyfill

  • babel-polyfill会”加载整个polyfill库”,针对编译的代码中新的API进行处理,并且在代码中插入一些帮助函数

  • babel-runtime用以提供编译模块的工具函数, 启用插件babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数

Babel < 7.4.0
开发类库, 选择 @babel/runtime
内部项目,@babel/polyfill

Babel >= 7.4.0
直接上 @babel/runtime

上一篇:Cobalt Strike 上线Linux主机


下一篇:buffer和cache简介