rollup打包工具

rollup打包工具

在学习vite和vue3源码的时候,接触到了rollup,所以过来学习一下

什么是rollup

rollup是一个模块化的打包工具,会将javascript文件进行合并。比起webpack,webpack在打包的时候会进行代码注入(保障兼容性),如果对于一些项目,特别是类库,没有其他的静态资源文件,就可以使用rollup。rollup支持es6模块,支持tree-shaking,不支持code-splitting,模块热更新

  • tree shaking优化: tree shaking是一种优化技术,用于剔除未使用的代码,减少最终的文件大小
  • ES6模块支持: rollup专注es6模块的打包,有助于避免commonjs模块的一些问题,比如命名空间
  • 代码拆分与懒加载:Rollup 支持代码拆分和懒加载,允许将代码拆分成多个文件,只在需要时加载。这有助于减少初始加载时间,并提供更好的性能。
  • 可插拔的插件系统:允许使用现有插件和编写自定义插件
  • 输出格式多样性:支持多种输出格式,包含ES6模块,commonjs,umd等

rollup的工作流程

acorn: JavaScript的此法解析器,可以将JavaScript字符串解析成为语法抽象树AST。
提供一个入口文件,rollup通过acorn读取解析文件,返回一种ast的抽象语法树。一个文件就是一个模块,每个模块都会根据文件的代码生成一个ast抽象语法树
分析AST节点,就是看这个节点有没有调用函数的方法,有没有读到变量,有,就查看是否在当前的作用域,不在就往上找,直到找到模块顶层作用域为止,如果本模块没有找到,则说明依赖于别的模块,需要从其他模块中去导出。直到没有依赖的模块为止。

│  bundle.js // Bundle 打包器,在打包过程中会生成一个 bundle 实例,用于收集其他模块的代码,最后再将收集的代码打包到一起。
│  external-module.js // ExternalModule 外部模块,例如引入了 'path' 模块,就会生成一个 ExternalModule 实例。
│  module.js // Module 模块,module 实例。
│  rollup.js // rollup 函数,一切的开始,调用它进行打包。
│
├─ast // ast 目录,包含了和 AST 相关的类和函数
│      analyse.js // 主要用于分析 AST 节点的作用域和依赖项。
│      Scope.js // 在分析 AST 节点时为每一个节点生成对应的 Scope 实例,主要是记录每个 AST 节点对应的作用域。
│      walk.js // walk 就是递归调用 AST 节点进行分析。
│
├─finalisers
│      cjs.js
│      index.js
│
└─utils // 一些帮助函数
        map-helpers.js
        object.js
        promise.js
        replaceIdentifiers.js
生成一个new Bundle(),然后执行build()打包
```javascript
let Bundle = require('./bundle')
function rollup(entry, outputFileName) {
    const bundle = new Bundle({ entry })
    bundle.build(outputFileName)
}
module.export = rollup
```
```javascript
class Bundle {
    constructor() {}
    build(outputFileName) {}
    fetchModule(importee, importer) {
       ...
        if(route) {
            let code = fs.readFileSync(route, 'utf8'),
            let module = new Module({
                code, // 模块的源代码
                path: route, // 模块的绝对路径
                bundle: this // 属于那个bundle
            })
            return module;
        }     
    }
}
```
new Module()

每个文件都是一个模块,每个模块都会有一个module实例,在module实例中,会调用acorn库的parse()方法将代码解析称为AST

class Module {
    constructor({code, path, bundle}) {
        this.code = new MagicString(code, {filename: path})
        this.path = path 
        this.bundle = bundle
        this.ast = parse(code, { // 把源代码转换为抽象语法树
            ecmaVersion: 7,
            sourceType: 'module'
        })
        this.analyse()
    }
}
  1. 词法分析 this.analyse()
    • 分析当前模块导入import和导出exports模块,将引入的模块和导出的模块存储起来的this.imports={} // 存放当前模块所有的导入
    • this.exports = {} 存放着当前模块所有的导出
    this.imports = {};//存放着当前模块所有的导入
    this.exports = {};//存放着当前模块所有的导出
    this.ast.body.forEach(node => {
      if (node.type === 'ImportDeclaration') {// 说明这是一个 import 语句
        let source = node.source.value; // 从哪个模块导入的
        let specifiers = node.specifiers; // 导入标识符
        specifiers.forEach(specifier => {
          const name = specifier.imported.name; //name
          const localName = specifier.local.name; //name
          //本地的哪个变量,是从哪个模块的的哪个变量导出的
          this.imports[localName] = { name, localName, source }
        });
        //}else if(/^Export/.test(node.type)){ // 导出方法有很多
      } else if (node.type === 'ExportNamedDeclaration') { // 说明这是一个 exports 语句
        let declaration = node.declaration;//VariableDeclaration
        if (declaration.type === 'VariableDeclaration') {
          let name = declaration.declarations[0].id.name;
          this.exports[name] = {
            node, localName: name, expression: declaration
          }
        }
      }
    });
    analyse(this.ast, this.code, this);//找到了_defines 和 _dependsOn
    
  2. analyse(this.ast, this.code, this)
    • _defines: { value: {} },//存放当前模块定义的所有的全局变量
    • _dependsOn: { value: {} },//当前模块没有定义但是使用到的变量,也就是依赖的外部变量
    • _included: { value: false, writable: true },//此语句是否已经被包含到打包结果中,防止重复打包
    • _source: { value: magicString.snip(statement.start, statement.end) } //magicString.snip 返回的还是 magicString 实例 clone
    function analyse(ast, magicString, module) {
        let scope = new Scope();//先创建一个模块内的全局作用域
        //遍历当前的所有的语法树的所有的顶级节点
        ast.body.forEach(statement => {
        
            //给作用域添加变量 var function const let 变量声明
            function addToScope(declaration) {
                var name = declaration.id.name;//获得这个声明的变量
                scope.add(name);
                if (!scope.parent) {//如果当前是全局作用域的话
                    statement._defines[name] = true;
                }
            }
    
            Object.defineProperties(statement, {
                _defines: { value: {} },//存放当前模块定义的所有的全局变量
                _dependsOn: { value: {} },//当前模块没有定义但是使用到的变量,也就是依赖的外部变量
                _included: { value: false, writable: true },//此语句是否已经 被包含到打包结果中了
                //start 指的是此节点在源代码中的起始索引,end 就是结束索引
                //magicString.snip 返回的还是 magicString 实例 clone
                _source: { value: magicString.snip(statement.start, statement.end) }
            });
            
            //这一步在构建我们的作用域链
            walk(statement, {
                enter(node) {
                    let newScope;
                    if (!node) return
                    switch (node.type) {
                        case 'FunctionDeclaration':
                            const params = node.params.map(x => x.name);
                            if (node.type === 'FunctionDeclaration') {
                                addToScope(node);
                            }
                            //如果遍历到的是一个函数声明,我会创建一个新的作用域对象
                            newScope = new Scope({
                                parent: scope,//父作用域就是当前的作用域
                                params
                            });
                            break;
                        case 'VariableDeclaration': //并不会生成一个新的作用域
                            node.declarations.forEach(addToScope);
                            break;
                    }
                    if (newScope) {//当前节点声明一个新的作用域
                        //如果此节点生成一个新的作用域,那么会在这个节点放一个_scope,指向新的作用域
                        Object.defineProperty(node, '_scope', { value: newScope });
                        scope = newScope;
                    }
                },
                leave(node) {
                    if (node._scope) {//如果此节点产出了一个新的作用域,那等离开这个节点,scope 回到父作用法域
                        scope = scope.parent;
                    }
                }
            });
        });
        ast._scope = scope;
        //找出外部依赖_dependsOn
        ast.body.forEach(statement => {
            walk(statement, {
                enter(node) {
                    if (node._scope) {
                        scope = node._scope;
                    } //如果这个节点放有一个 scope 属性,说明这个节点产生了一个新的作用域
                    if (node.type === 'Identifier') {
                        //从当前的作用域向上递归,找这个变量在哪个作用域中定义
                        const definingScope = scope.findDefiningScope(node.name);
                        if (!definingScope) {
                            statement._dependsOn[node.name] = true;//表示这是一个外部依赖的变量
                        }
                    }
    
                },
                leave(node) {
                    if (node._scope) {
                        scope = scope.parent;
                    }
    
                }
            });
        });
    }
    
  3. this.definitions = {} 全局变量的定义语句存放到definitions里
    // module.js
    this.definitions = {};//存放着所有的全局变量的定义语句
    this.ast.body.forEach(statement => {
        Object.keys(statement._defines).forEach(name => {
        //key 是全局变量名,值是定义这个全局变量的语句
        this.definitions[name] = statement;
        });
    });
    
  4. 展开语法,展开当前模块的所有语法,把这些语句中定义的变量的语句放到结果里
generate()
  • 移除额外代码
  • 处理ast节点上的源码,拼接字符串
  • 返回合并后的源代码
  • 输出到dist/bundle.js中
总结

在这里插入图片描述

  1. 获取入口文件的内容,包装成 module,生成抽象语法树
  2. 对入口文件抽象语法树进行依赖解析
  3. 生成最终代码
  4. 写入目标文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/799759.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

位图——哈希思想的应用

三、位图 0、位图概念 所谓位图,就是用每一个比特位来存放某种状态(0或1),是一种哈希思想的应用,适用于海量数据,整数,数据无重复的场景。通常是用来判断某个数据存不存在的。(注意…

GaussDB DWS 详解

文章目录 GaussDB DWS 详解一、简介二、DWS的分布式架构架构概述关键组件 三、分布式查询数据查询流程SQL执行的示例 批注:本文引鉴了Forlogen博主的一些内容,并加以补充,以供学习了解。 GaussDB DWS 详解 一、简介 DWS(Data Warehouse Ser…

数据库-三范式

第一范式 1 数据库所有字段都只有单一属性。 2 单一属性由基本数据类型构成。 3 数据库的表都是二维的行与列。 例如上面的例子就不满足第一范式,因为是可以继续拆分的,拆分为更多的属性。 第二范式 1 符合第一范式 2 表必须有个主建 3 其它字段可以…

《0基础》学习Python——第十一讲__时间函数

一、时间函数是Python中的内置函数和模块,用于处理日期和时间相关的操作。以下是常用的时间函数的种类和用法: 1、time.time():返回当前时间的时间戳。 时间戳(timestamp)是一种表示日期和时间的方式,它是一…

Linux--USB驱动开发(二)插入USB后的内核执行程序

一、USB总线驱动程序的作用 a)识别USB设备 1.1 分配地址 1.2 并告诉USB设备(set address) 1.3 发出命令获取描述符 b)查找并安装对应的设备驱动程序 c)提供USB读写函数 二、USB设备工作流程 由于内核自带了USB驱动,所以我们先插入一个U…

CSS-0_3 CSS和单位

文章目录 CSS的值和单位属性值长度单位CSS和绝对单位CSS和相对单位百分比em & rem视口 颜色单位 碎碎念 CSS的值和单位 我们知道,CSS是由属性和属性值所组成的表 随着CSS的发展,属性不说几千也有几百,我从来不支持去背诵所有的可能性。…

K8S系列-Kubernetes基本概念及Pod、Deployment、Service的使用

一、Kubernetes 的基本概念和术语 一、资源对象 ​ Kubernetes 的基本概念和术语大多是围绕资源对象 Resource Object 来说的,而资源对象在总体上可分为以下两类: 1、某种资源的对象 ​ 例如节点 Node) Pod 服务 (Service) 、存储卷 (Volume)。 2、…

Nginx入门到精通五(动静分离)

下面内容整理自bilibili-尚硅谷-Nginx青铜到王者视频教程 Nginx相关文章 Nginx入门到精通一(基本概念介绍)-CSDN博客 Nginx入门到精通二(安装配置)-CSDN博客 Nginx入门到精通三(Nginx实例1:反向代理&a…

从0-1搭建一个web项目(页面布局详解)详解

本章分析页面布局详解详解 ObJack-Admin一款基于 Vue3.3、TypeScript、Vite3、Pinia、Element-Plus 开源的后台管理框架。在一定程度上节省您的开发效率。另外本项目还封装了一些常用组件、hooks、指令、动态路由、按钮级别权限控制等功能。感兴趣的小伙伴可以访问源码点个赞 地…

java数组之冒泡排序、快速排序

一、排序算法概述 1.算法定义 排序&#xff1a;假设含有n个记录的序列为{R1&#xff0c;R2&#xff0c;...,Rn},其相应的关键字序列为{K1&#xff0c;K2&#xff0c;...,Kn}。将这些记录重新排序为{Ri1,Ri2,...,Rin},使得相应的关键字值满足条Ki1<Ki2<...<Kin,这样的…

使用Keepalived实现双机热备(虚拟漂移IP地址)详细介绍

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

BI工具的AI革新:对话式分析如何引领企业智能转型?

在数据驱动的时代&#xff0c;数据分析早已成为企业决策制定的关键支撑。但是&#xff0c;很多企业在数字化转型的过程中&#xff0c;常常面临门槛高、流程复杂等问题。而AI技术的发展为解决上述问题带来了突破。 为了简化企业智能转型路径&#xff0c;帆软接入AI大模型技术&a…

Scherlokk - Mac 文件快速搜索对比工具

Scherlokk 是一款适用于 Mac 的文件内容快搜比较工具&#xff0c;在 Scherlokk 内输入关键词&#xff0c;即可在本地磁盘 / 移动硬盘 / 网络驱动器等区域内&#xff0c;查找包含该词的文件&#xff0c;快速定位所需文件&#xff0c;并提供文件比较、快速筛选过滤等功能。 两种…

SpringCloud--常用组件和服务中心

常用组件 Euroke和nacos 区别 负载均衡 负载均衡策略有哪些 自定义负载均衡策略

Power Apps使用oData访问表数据并赋值前端

在使用OData查询语法通过Xrm.WebApi.retrieveMultipleRecords方法过滤数据时&#xff0c;你可以指定一个OData $filter 参数来限制返回的记录集。 以下是一个使用Xrm.WebApi.retrieveMultipleRecords方法成功的例子&#xff0c;它使用了OData $filter 参数来查询实体的记录&am…

YOLOv5分类任务——手势识别

1. 下载YOLOv5官方代码 ONNX > CoreML > TFLite (github.com)">ultralytics/yolov5: YOLOv5 🚀 in PyTorch > ONNX > CoreML > TFLite (github.com) 2. 配置环境 打开终端,先建立名为YOLO5的环境,再将路径切换为requirements.txt文件夹所在的路径…

C#环境与数据类型

文章目录 C#环境.NET 框架集成开发环境 创建一个C#项目数据类型值类型引用类型对象类型object动态类型dynamic字符串类型string 指针类型 类型转换隐式转换显示转换&#xff08;强制转换&#xff09;C#提供的类型转换方法Convert类Parse方法TryParse方法 C#环境 .NET 框架 C#是…

Directory Opus 13 专业版(Windows 增强型文件管理器)值得购买?

在使用电脑时&#xff0c;总少不了和文件打交道。系统自带的 Explorer 资源管理器功能又非常有限&#xff0c;想要拥有一个多功能文件管理器吗&#xff1f; Directory Opus 是一款老牌多功能文件管理器&#xff0c;能很好地接管 Windows 资源管理器。 接管资源管理器 Directo…

Kotlin标准函数(语法糖)let with run also apply快速讲解

目录 1、知识储备——扩展函数 原理 定义扩展函数 调用扩展函数 2、返回值为上下文对象的标准函数 apply also 3、返回值为Lambda表达式结果 let run with 4、一表总结 1、知识储备——扩展函数 原理 Kotlin 在不继承父类或实现接口下&#xff0c;也能扩展一个类的…

硅谷甄选4(项目主体)

1.路由配置 1.1路由组件的雏形 src\views\home\index.vue&#xff08;以home组件为例&#xff09; 安装插件&#xff1a; 1.2路由配置 1.2.1路由index文件 src\router\index.ts //通过vue-router插件实现模板路由配置 import { createRouter, createWebHashHistory } fro…