谈谈webpack - (打包、编译)笔记

101 阅读5分钟

本文已参与「 新人创作礼 」活动,一起开启掘金创作之路

关于如何进行图片优化 - 适合的才是最好的

webpack打包原理

webpack只是一个打包模块的机制,只是把依赖的模块转化成可以代表这些包的静态文件。并不是什么commonjs或者amd之类的模块化规范。webpack就是识别你的 入口文件。识别你的模块依赖,来打包你的代码。至于你的代码使用的是commonjs还是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。webpack做的就是分析代码,转换代码,编译代码,输出代码。webpack本身是一个node的模块,所以webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)

webpack中每个模块有一个唯一的id,是从0开始递增的。整个打包后的bundle.js是一个匿名函数自执行。参数则为一个数组。数组的每一项都为个function。function的内容则为每个模块的内容,并按照require的顺序排列。

如何实现一个简单的webpack
  1. 读取文件分析模块依赖
  2. 对模块进行解析执行(深度遍历)
  3. 针对不同的模块使用相应的loader
  4. 编译模块,生成抽象语法树AST。
  5. 循环遍历AST树,拼接输出js。
loader原理

在解析对于文件,会自动去调用响应的loader。**loader 本质上是一个函数,输入参数是一个字符串,输出参数也是一个字符串。当然,输出的参数会被当成是 JS 代码,从而被 esprima 解析成 AST,触发进一步的依赖解析。**webpack会按照从右到左的顺序执行loader。

Babel原理及其使用
核心包
  • babel-core:babel转译器本身,提供了babel的转译API,如babel.transform等,用于对代码进行转译。像webpack的babel-loader就是调用这些API来完成转译过程的。

  • babylon:js的词法解析器

  • babel-traverse:用于对AST(抽象语法树,想了解的请自行查询编译原理)的遍历,主要给plugin用

  • babel-generator:根据AST生成代码

功能包
  • babel-types:用于检验、构建和改变AST树的节点

  • babel-template:辅助函数,用于从字符串形式的代码来构建AST树节点

  • babel-helpers:一系列预制的babel-template函数,用于提供给一些plugins使用

  • babel-code-frames:用于生成错误信息,打印出错误点源代码帧以及指出出错位置

  • babel-plugin-xxx:babel转译过程中使用到的插件,其中babel-plugin-transform-xxx是transform步骤使用的

  • babel-preset-xxx:transform阶段使用到的一系列的plugin

  • babel-polyfill:JS标准新增的原生对象和API的shim,实现上仅仅是core-js和regenerator-runtime两个包的封装

  • babel-runtime:功能类似babel-polyfill,一般用于library或plugin中,因为它不会污染全局作用域

工具包

babel-cli:babel的命令行工具,通过命令行对js代码进行转译

babel-register:通过绑定node.js的require来自动转译require引用的js代码文件

使用形式

如果是以命令行方式使用babel,那么babel的设置就以命令行参数的形式带过去;

还可以在package.json里在babel字段添加设置;

但是建议还是使用一个单独的.babelrc文件,把babel的设置都放置在这里,所有babel API的options(除了回调函数之外)都能够支持,具体的options见babel的API options文档

常用options字段说明
  • env:指定在不同环境下使用的配置。比如production和development两个环境使用不同的配置,就可以通过这个字段来配置。env字段的从process.env.BABEL_ENV获取,如果BABEL_ENV不存在,则从process.env.NODE_ENV获取,如果NODE_ENV还是不存在,则取默认值"development"

  • plugins:要加载和使用的插件列表,插件名前的babel-plugin-可省略;plugin列表按从头到尾的顺序运行

  • presets:要加载和使用的preset列表,preset名前的babel-preset-可省略;presets列表的preset按从尾到头的逆序运行(为了兼容用户使用习惯)

  • 同时设置了presets和plugins,那么plugins的先运行;每个preset和plugin都可以再配置自己的option

babel会从当前转译的文件所在目录下查找配置文件,如果没有找到,就顺着文档目录树一层层往上查找,一直到.babelrc文件存在或者带babel字段的package.json文件存在为止。

babel是一个转译器,感觉相对于编译器compiler,叫转译器transpiler更准确,因为它只是把同种语言的高版本规则翻译成低版本规则,而不像编译器那样,输出的是另一种更低级的语言代码。

但是和编译器类似,babel的转译过程也分为三个阶段:parsing、transforming、generating,以ES6代码转译为ES5代码为例,babel转译的具体过程如下:

ES6代码输入 ==》 babylon进行解析 ==》 得到AST

==》 plugin用babel-traverse对AST树进行遍历转译 ==》 得到新的AST树

==》 用babel-generator通过AST树生成ES5代码

此外,还要注意很重要的一点就是,babel只是转译新标准引入的语法,比如ES6的箭头函数转译成ES5的函数;而新标准引入的新的原生对象,部分原生对象新增的原型方法,新增的API等(如Proxy、Set等),这些babel是不会转译的。需要用户自行引入polyfill来解决

plugins

插件应用于babel的转译过程,尤其是第二个阶段transforming,如果这个阶段不使用任何插件,那么babel会原样输出代码。

我们主要关注transforming阶段使用的插件,因为transform插件会自动使用对应的词法插件,所以parsing阶段的插件不需要配置。