AST抽象语法树是什么?
树形语法结构,会对代码里的函数、变量声明、逻辑操作进行一些校验。
为什么要用AST?
手写了一段代码之后,编译器需要对不同风格的代码按照商定好的规则统一处理,处理成为规则能够顺利执行的语言,才可以发布执行。
编译逻辑:
将代码字符串解析数据单元,再解析成一段token数组,解析时比如方法里还有别的变量,逻辑等,就会形成嵌套,最终就是一个树形结构,然后再生成一个AST树,是一个JSON的数据格式,遍历AST,对内容进行一些变更,生成新的AST再根据它生成一套新的代码。
比如babel 编译一个es6的箭头函数时,会通过编译,输出为一个普通的function,转换成为兼容es6之下的语法。
1. parsing:解析过程,词法分析、语法分析、构建AST;
1) 词法分析:读取代码字符串,识别每一个单词及其种类,移除空格注释,按照预定规则合并成一个个的标识(token),产出一个token数组(令牌)
2) 语法分析:读取令牌流,在语法约束下生成树形的中间表示,便于描述逻辑结构,给出了令牌流的结构表示,同时验证语法,如果有错误抛出语法错误
2. transformation:转化过程,将上一步的解析后的内容按照编译器指定的规则进行处理,形成一个新的表现形式,即改写AST,根据当前AST生成一个新的AST,可以是相同语言或其他语言;
3. code generation:代码生成,将上一步处理好的内容(AST)转化为新的代码
使用工具查看AST代码: astexplorer.net 网址
用一段代码逻辑表示就是:
function compiler(input){
let tokens = tokenizer(input); // 生成tokens
/**
* 遍历代码字符,匹配到括号、字符、数字、引号等,分类push到tokens数组里 键值对push { type: 'number', value: '123' }
*/
let ast = parser(tokens); // 生成ast
/**
* 遍历type === 'number'时,ast.body就push一条 NumberLiteral 类型的键值对, 如果是方法调用,就会继续遍历调用的params传入
*/
let newAst = transformer(ast); // 拿到新的ast
/**
* 核心功能,新逻辑注入,AST的转换过程
*/
let output = codeGenerator(nevAst); // 生成新代码
return output;
}
1. 代码优化 eg: 简易版esliint,禁止console
2. 代码检查 代码风格检查,安全检查
3. 代码编辑器和插件 语法高亮 代码提示
1. 性能优化: Webpack 5引入了许多性能优化,包括持久性缓存、更好的树摇(Tree Shaking)和模块联邦(Module Federation)等特性,以提高构建速度和应用程序性能。
2. 持久性缓存: Webpack 5引入了持久性缓存,通过为构建生成的文件添加哈希值,可以更好地利用浏览器缓存, 从而减少用户下载更新后的代码量。减少了打包的工作量,可以在.cache里取chunk。
3. 模块联邦(Module Federation):这是Webpack 5的一个重要特性,允许多个独立的Webpack构建共享模块从而提高应用程序之间的代码共享和分发。避免重复打包,优化打包时间。
webpack5 在默认情况下,cache 属性值在开发环境中为true,等同于cache:{type:'memory'},而在生产环境下则是被禁用。
对于持久化缓存,则需要指定 cache.type 的属性值为 filesystem,开启这个配置之后,默认会在node_modules 下创建一个.cache 的目录,用来存放生成的缓存文件。
1. 构建时间优化
2. 打包体积优化
如何知道webpack编译速度?
progress-bar-webpack-plugin: 查看编译进度; 安装插件,获取进度条
speed-measure-webpack-plugin: 查看编译速度; 安装插件,获取细粒度的时间
webpack-bundle-analyzer: 打包体积分析(这个插件需要跑起来项目,看到各个部分占据的体积,将较大占用的功能按需引用,可减小体积。)
npm run eject 可以拿到所有的webpack配置,包含默认配置,安装插件之后,在webpack.config.js中引用,按照文档说明使用插件,在下次build时就能在控制台看到对应的编译数据,这样就可以在优化时有一个比较明确的针对方向。
sourceMap 是将编译后的代码映射回开发时的代码的工具,根据map文件里的变量映射。
在devtool 的地方修改,可以改变编译map方式。
关键字: eval、inline、hidden、nosources、cheap
[inline- | hidden- | eval-][nosources-][cheap-[module-]]source-map
其中:
inline- | hidden- | eval- : 三个值时三选一
cheap 可选值,并且可以跟随 module的值;
nosources: 可选值。
常用配置推荐
开发环境推荐使用
1. eval-cheap-source-map
2. eval-cheap-module-source-map
生产环境推荐使用
1. hidden-source-map
2. nosources-source-map
1. 忽略不必要的文件
可以通过配置watchOptions中的ignore选项,忽略不必要监视的文件或目录,这样可以避免监听一些不相关的文件,减少不必要的构建触发,从而提高性能。
2. 增加检查延迟
默认情况下,webpack会立即响应文件的变化并触发重新构建。通过调整watchOptions中的aggregateTimeout选项,增加检查的延迟时间,以减少频繁的构建触发,这样可以在一段时间内收集文件变化,然后一次性触发构建,避免过于频繁的构建。
3. 使用增量编程
webpack5中引入了增量编译功能,它只会重新编译发生变化的模块及其依赖,而不是整个项目,这样可以提高构建的速度,特别是在大型项目中。
配置include、exclude,缩小loader对文件的搜索范围。
比如配置解析范围在 src目录下的 ts、tsx、js、jsx文件,不包含node_modules
按照tsx、ts、jsx、js的顺序匹配,若没匹配到则报错
将thread-loader放在构建耗时较大的loader之前,会开启多进程进行打包,如果是轻量的loader,有可能会加重负担。
把不变的第三方库、一些公共模块 比如 util.js 这些单独拆成一个chunk,在访问页面的时候就可以一直使用浏览器中缓存的资源。
webpack5 默认开启tree-shaking
image-webpack-loader 使用 type: 'asset' 选项可以将图片文件或其他资源文件类型视为Asset Module类型,这个类型是webpack5引入的新特性,允许将资源文件直接作为模块导入到代码中,无需手动配置额外的loader。
1. 初始化: 启动构建,读取与合并配置参数,加载Plugin,实例化Compiler
2. 编译: 从 entry出发,针对每个 Module 串行调用对应的 loader 去翻译文件的内容,再找到该 Module 依赖的Module,递归地进行编译处理
3. 输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中
1. 识别依赖关系: Tree-shaking 从应用程序的入口点开始,识别所有的依赖关系,包括ES6的import 和 require 语句。
2. 构建依赖图: 根据识别到的依赖关系,建立一个依赖图。这个图用于表示模块之间的依赖关系和引用关系。
3. 静态分析:在 Tree-shaking 过程中,不执行实际的代码,而是进行静态分析。这意味着它在编译时期(而不是运行时期)就能够确定哪些代码被引用,哪些代码未被引用。
4. 标记未使用代码: 通过静态分析,Tree-shaking 标记所有未被引用的代码,包括模块、函数、变量等。
5. 移除未使用代码: 最后,标记的未使用代码将从最终的打包文件中剔除,以减小文件大小。
实现 Tree-shaking 的前提是,应用程序必须使用 ES6 模块系统,这通常意味着在开发中避免使用CommonJs 的require 语句,因为它们不具备静态特性,无法被Tree-shaking 移除。
更多【node.js-AST抽象语法树&webpack逻辑解析】相关视频教程:www.yxfzedu.com