30分钟搞懂Rollup+Typescript工程构建(一)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 最近在研究一个ngptcommit命令行工具,然后想通过Rollup+Typescript去编译的时候,发现对Rollup和Typescript的编译配置有点陌生,所以希望通过本文能够对其有个系统的认知。

背景


最近在研究一个ngptcommit命令行工具,然后想通过Rollup+Typescript去编译的时候,发现对RollupTypescript的编译配置有点陌生,所以希望通过本文能够对其有个系统的认知。

本文主要是项目编译基础知识,明白其为什么要这么配置,同时能够将项目完整跑起来。

参考项目地址为:node-gptcommit

为什么不用WebpackVite呢?因为这两个对于一个命令行工具来说有点过于重了,有点啥像杀鸡焉用宰牛刀的感觉。

Rollup


Rollup是JavaScript模块打包工具,可以将现代化代码编译成更加复杂的代码,如:库或应用。默认使用 JavaScript ES6 修订版中包含的代码模块的新标准化格式,而不是以前的特殊解决方案,如 CommonJS 和 AMD等。 —— Rollup 官网

了解Rollup的打包核心思想:主要是将代码编译成符合ES模块规范的代码包,当然也可以用其相关的插件实现CommonJS规范。


接下来我们将通过下面几个步骤去完整了解Rollup:

  • Rollup组成部分
  • Rollup执行构建原理流程
  • 结合Typescript实战

组成部分


一般我们实际使用场景是不会通过命令行去编译某个文件,而是针对整个项目去编译构建,因此一个完整Rollup构建项目主要有以下几个部分组成:

  • rollup npm包,用于执行构建命令源头,可以安装本地项目,也可以安装全局命令,但是一般是跟着项目走
  • rollup.config.js roll的配置文件,是所有命令的入口,也是学习Rollup的核心基础之一
  • 插件部分,rollup有丰富的插件生态,如:Babel 编译代码,运行 JSON 文件等,可以让rollup完成更多复杂构建功能
  • 输出插件,在rollup代码分析完成之后,才可以修改代码相关事项


这基本上就是Rollup项目构建所组成的部分了,接下来我们进行一一学习。

Rollup命令


在项目中,我们常用的命令有以下几种:

  • rollup -c 使用配置文件(如果使用参数但是值没有 指定, 默认就是 rollup.config.js)执行构建
  • rollup -c -w 监听入口文件并在文件改变时重新构建
  • rollup -c --environment BUILD:production 可以设置环境变量,会设置process.env.BUILD = 'production'

通过我们会在package.json中的scripts设置如下命令:

{
    "scripts": {
        "dev": "rollup -c -w",
        "build": "rollup -c --environment BUILD:production"
    }
}

基本上就已经满足绝大部分项目需求了,如果有些项目需要更多命令配置,可以到官网查看命令行参数:Rollup命令行参数说明

非命令行使用Rollup


还有一种Rollup的使用,就是通过代码引用Rollup实现,比如在scripts/build.js,去引用rollup,然后深度参与Rollup构建前后的一些事项,满足项目构建的自定义功能。

一般代码如下:

const rollup = require('rollup');
// 有关选项的详细信息,请参见下文
const inputOptions = {...};
const outputOptions = {...};
async function build() {
  // 创建一个 bundle
  const bundle = await rollup.rollup(inputOptions);
  console.log(bundle.watchFiles); // 该 bundle 依赖的文件名数组
  // 在内存中生成输出特定的代码
  // 您可以在同一个 bundle 对象上多次调用此函数
  const { output } = await bundle.generate(outputOptions);
  for (const chunkOrAsset of output) {
    if (chunkOrAsset.type === 'asset') {
      // 对于assets,包含
      // {
      //   fileName: string,              // asset 文件名
      //   source: string | Uint8Array    // asset 资源
      //   type: 'asset'                  // 表示这是一个 asset
      // }
      console.log('Asset', chunkOrAsset);
    } else {
      // 对于chunks, 包含
      // {
      //   code: string,                  // 生成的JS代码
      //   dynamicImports: string[],      // chunk 动态导入的外部模块
      //   exports: string[],             // 导出的变量名
      //   facadeModuleId: string | null, // 该chunk对应的模块的ID
      //   fileName: string,              // chunk的文件名
      //   imports: string[],             // chunk 静态导入的外部模块
      //   isDynamicEntry: boolean,       // 该 chunk 是否是动态入口点
      //   isEntry: boolean,              // 该 chunk 是否是静态入口点
      //   map: string | null,            // sourcemaps(如果存在)
      //   modules: {                     // 此 chunk 中模块的信息
      //     [id: string]: {
      //       renderedExports: string[]; // 导出的已包含变量名
      //       removedExports: string[];  // 导出的已删除变量名
      //       renderedLength: number;    // 模块中剩余代码的长度
      //       originalLength: number;    // 模块中代码的原始长度
      //     };
      //   },
      //   name: string                   // 命名模式中使用的 chunk 的名称
      //   type: 'chunk',                 // 表示这是一个chunk
      // }
      console.log('Chunk', chunkOrAsset.modules);
    }
  }
  // 或者将bundle写入磁盘
  await bundle.write(outputOptions);
}
build();

然后package.json中的scripts设置如下命令:

{
    "scripts": {
        "build": "node scripts/build.js"
    }
}

配置文件


rollup.config.js一般会放在项目根目录下,如果放在其他目录下,需要在命令行上指定对应路径,如:rollup -c xxx/rollup.config.js

Rollup配置文件中,我们需要关心的配置项主要有以下几个:

  • input 入口文件,Rollup将从这里去扫描解析代码,生成代码依赖树,支持多个
  • output 输出配置项,主要是指的Rollup编译输出什么格式的代码,这里涉及多个配置项
  • plugins 依赖的插件列表,需要哪些插件去扩展Rollup构建能力,不同插件配置内容也不同,后面会讲述常用的几个插件
  • external 忽略打包模块列表,如:有些公共库,我们不需要构建进来
  • cache 构建缓存,是否开启构建缓存,提高构建速度,依据配置内容可以才去不同缓存策略

需要注意的一个点,Rollup是支持多套配置,比如:用来生成umd规范的代码文件用来支持浏览器,再就生成cjs给node使用的js文件。

针对上面几个常用的配置项,我们来一一分析它们的用法,其他的配置项可以到官网查看:Rollup完整配置项

input


input最常见的问题就是多个入口文件,毕竟单一入口文件就很简单解决了,那么遇到有多个入口文件的时候,input该如何配置:

export default {
  ...,
  input: {
    a: 'src/main-a.js',
    'b/index': 'src/main-b.js'
  },
  output: {
    ...,
    entryFileNames: 'entry-[name].js'
  }
};

结果:

  • input配置的key值会作为output中配置entryFileNamesname的值
  • 最终会输出两个文件entry-a.jsentry-b/index.js

output


output相比较input会复杂很多,但是我们所关心的主要配置项如下:

  • output.dir, 构建好的代码文件放d到哪个文件夹中
  • output.file,针对单一入口,指定生成的文件名,如:index.esm.jsindex.cjs.js
  • output.format,按照哪个代码规范去生成,目前主要有:
  • cjs 为CommonJS规范,适用于 Node 环境和其他打包工具
  • es 为ES规范,适用于其他打包工具以及支持 </code> 标签的浏览器</li><li><code>umd</code> 通用模块定义,生成的包同时支持 <code>amd</code>、<code>cjs</code> 和 <code>iife</code> 三种格式</li><li>其他有<code>amd</code> <code>iife</code>等</li></ul><ul><li><code>output.globals</code>,用来忽略打包(umd 或 iife 规范)后的代码的代码依赖,比如:代码中依赖<code>jquery</code>,且<code>jquery</code>在代码使用<code>$</code>标识,则可以配置:</li></ul><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22%7B%5Cn...%5Cnoutput%3A%7B%5Cn%20%20%20%20globals%3A%20%7B%5Cn%20%20%20%20%20%20%20%20jquery%3A%20'%24'%5Cn%20%20%20%20%7D%5Cn%7D%5Cn...%5Cn%7D%22%2C%22heightLimit%22%3Atrue%2C%22margin%22%3Atrue%2C%22id%22%3A%22jSOyo%22%2C%22autoWrap%22%3Atrue%7D"></div><ul><li><code>output.name</code>, 以umd 或 iife 规范打包后的代码,需要注册在全局对象中的名字</li><li><code>output.plugins</code>,针对输出后的代码需要进行插件扩展,如:压缩代码</li><li><code>output.chunkFileNames</code>,对代码分割中产生的 chunk 文件自定义命名,默认值是:<code>[name]-[hash].js</code></li><li><code>output.exports</code>,指定导出模式,有几个值:</li></ul><ul data-lake-indent="1"><li><code>default</code>,等于最终导出等于<code>export default xxx</code>,这里适用于单个文件入口</li><li><code>named</code>,等于<code>export default {xxx1, xxx2}</code>,适用于多个入口文件</li><li><code>none</code>,没有<code>export</code>,适用于打包web应用,不需要对外抛出对象</li></ul><ul><li><code>output.externalLiveBindings</code>, 是否给外部依赖生成动态绑定代码,简单的说就是是否需要将外部依赖的npm包通过转义来引入</li><li><code>output.freeze</code> 指定是否使用 Object.freeze() 冻结动态访问的引入对象的命名空间,禁止修改外部的依赖对象属性</li><li><code>output.sourcemap</code> 是否生成sourcemap文件</li></ul><div>Typescript主要知识点:</div><h3 id="ZKp5i">plugins</h3><div data-card-type="block" data-ready-card="hr"></div><div>Rollup plugin有很多,这里我们分成两块去学习,一个是如何配置plugin,另外一个是如何开发一个plugin。</div><h4 id="2198z">配置plugin</h4><div data-card-type="block" data-ready-card="hr"></div><div>这块内容就相对比较简单了,主要在于如何找到适合的plugin,以及它们的配置项是怎么样的。</div><div>第一个问题,到哪里找插件,Rollup官网提供一个地方去找对应plugin,<a href="https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Frollup%2Fawesome" target="_blank" ref="nofollow noopener noreferrer">awesome Rollup插件</a></div><div>第二个文件,如何配置plugin,具体如下:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22import%20typescript%20from%20'rollup-plugin-typescript2'%3B%5Cnexport%20default%20%5B%7B%5Cn%20%20%20%20plugins%3A%5B%5Cn%20%20%20%20%20%20%20%20typescript()%5Cn%20%20%20%20%5D%5Cn%7D%5D%22%2C%22heightLimit%22%3Atrue%2C%22margin%22%3Atrue%2C%22id%22%3A%22qKLMw%22%2C%22autoWrap%22%3Atrue%7D"></div><h4 id="0G4Lk">开发plugin</h4><div data-card-type="block" data-ready-card="hr"></div><div>在官网有详细的教程,这里我们简单学会如何快速完成一个插件。</div><div>首先,我们需要对Rollup执行流程有一个完整的理解,如下图生命周期钩子函数所示:</div><div><span style="color: #181818;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2Fm7fto6cqrsdsy_ad7379dee4c246219133e1191d895c96.png%22%2C%22originWidth%22%3A1224%2C%22originHeight%22%3A1524%2C%22name%22%3A%22f16f56c2b2f9bd6bc0d8e92ffe55407.png%22%2C%22size%22%3A313649%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A1224%2C%22height%22%3A1524%7D"></span></span></div><div>Rollup对外提供的生命周期钩子函数:</div><ul><li>读取配置项 options</li><li>开始构建 buildStart</li><li>解析代码 resolveId,这里可以自定义一个解析代码器</li><li>加载代码 load</li><li>加载缓存模块 shouldTransformCacheModule</li><li>转义代码中 transform</li><li>将代码解析ES模块化后 modulePared</li><li>解析异步加载,如:import(()=> xxx) resolveDynamicImport</li><li>构建结束 buildEnd</li><li>监听改变中 watchChange</li><li>关闭监听后 closeWatcher</li></ul><div><br /></div><div>接下来我们来完成一个插件,就是在代码构建前,将<code>__helloworld__</code>换成<code>"hello qborfy!"</code>,避免代码解析出错,代码如下:</div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22%5Cn%2F%2F%20replaceHelloWorld.js%5Cnexport%20default%20function%20replaceHelloWorld()%7B%5Cn%20%20%20%20return%20%7B%5Cn%20%20%20%20%20%20%20%20name%3A%20'replace-helloworld'%2C%20%2F%2F%20%E6%8F%92%E4%BB%B6%E5%90%8D%E7%A7%B0%5Cn%20%20%20%20%20%20%20%20transform%20(%20code%2C%20id%20)%20%7B%20%2F%2F%20%E5%BD%93%E8%BF%9B%E5%85%A5%E8%BD%AC%E6%8D%A2%E7%9A%84%E6%97%B6%E5%80%99%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20(id%20%3D%3D%3D%20'replace-helloworld')%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20code%20%3D%20code.replace(%2F__helloworld__%2Fg%2C%20%60%5C%22hello%20qborfy!%5C%22%60)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20code%2C%20%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20map%3A%20null%2C%20%2F%2F%20%E8%BF%99%E9%87%8C%E4%B8%8D%E5%BD%B1%E5%93%8Dsourcemap%E7%94%9F%E6%88%90%20%E5%85%B7%E4%BD%93%E5%8F%AF%E4%BB%A5%E7%9C%8Bhttps%3A%2F%2Frollupjs.org%2Fplugin-development%2F%23source-code-transformations%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%3B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%20%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%7D%5Cn%7D%5Cn%5Cn%2F%2F%20%E6%8E%A5%E4%B8%8B%E6%9D%A5%E5%9C%A8rollup.config.js%E4%B8%AD%E5%BC%95%E7%94%A8%5Cn%5Cnimport%20replaceHelloWorld%20from%20'.%2FreplaceHelloWorld.js'%3B%5Cnexport%20default%20(%7B%5Cn%20%20input%3A%20'virtual-module'%2C%20%2F%2F%20resolved%20by%20our%20plugin%5Cn%20%20plugins%3A%20%5BreplaceHelloWorld()%5D%2C%5Cn%20%20output%3A%20%5B%7B%5Cn%20%20%20%20file%3A%20'bundle.js'%2C%5Cn%20%20%20%20format%3A%20'es'%5Cn%20%20%7D%5D%5Cn%7D)%3B%5Cn%22%2C%22heightLimit%22%3Atrue%2C%22margin%22%3Atrue%2C%22id%22%3A%22p9VCi%22%2C%22autoWrap%22%3Atrue%7D"></div><div>有两种方式去管理插件,一个是在项目直接管理维护,另外一种是通过发布npm包管理,这个取决插件是否有公用性即可。</div>
目录
相关文章
|
6月前
|
自然语言处理 JavaScript 前端开发
Vue3 + Vite + TypeScript + Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(1)
Vue3 + Vite + TypeScript + Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(1)
|
5月前
|
缓存 JavaScript 前端开发
探讨如何通过一系列优化策略来提升TypeScript与Webpack的构建性能。
【6月更文挑战第11天】本文探讨了优化TypeScript与Webpack构建性能的策略。理解Webpack的解析、构建和生成阶段是关键。优化包括:调整tsconfig.json(如关闭不必要的类型检查)和webpack.config.js选项,启用Webpack缓存,实现增量构建,代码拆分和懒加载。通过这些方法,可以提升构建速度,提高开发效率。
76 0
|
6月前
|
JavaScript 前端开发 应用服务中间件
Vue3 + Vite + TypeScript + Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(2)
Vue3 + Vite + TypeScript + Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(2)
|
6月前
|
监控 JavaScript 数据挖掘
TypeScript代码示例:构建灵活可扩展的员工上网管控平台
使用TypeScript构建的员工上网监控平台示例展示了如何通过`InternetMonitoringPlatform`类实现实时监控、数据分析和数据自动提交。类包含`monitorInternetActivity`用于监控行为,`analyzeData`用于分析数据,`autoSubmitToWebsite`借助axios库将数据POST到网站。此平台旨在提高企业安全性和效率。
159 3
|
6月前
|
缓存 JavaScript 前端开发
【TypeScript技术专栏】TypeScript与Webpack构建优化
【4月更文挑战第30天】本文探讨了优化TypeScript与Webpack构建性能的策略。理解Webpack的解析、构建和生成阶段是关键。优化包括:调整tsconfig.json(关闭不必要的类型检查,适配目标环境)和webpack.config.js(配置entry、output、resolve,使用压缩插件)。启用Webpack缓存和增量构建,利用代码拆分与懒加载,能有效提升构建速度和开发效率。
83 0
|
6月前
|
JavaScript 前端开发 编译器
TypeScript的编译器、编辑器支持与工具链:构建高效开发环境的秘密武器
【4月更文挑战第23天】TypeScript的强大力量源于其编译器、编辑器支持和工具链,它们打造了高效的开发环境。编译器`tsc`进行类型检查、语法分析和代码转换;编辑器如VS Code提供智能提示、错误检查和格式化;工具链包括Webpack、Rollup等构建工具,Jest、Mocha等测试框架,以及代码质量和性能分析工具。这些组合使用能提升开发效率、保证代码质量和优化项目性能。
|
6月前
|
前端开发 JavaScript 安全
使用React、TypeScript和Ant Design构建现代化前端应用
使用React、TypeScript和Ant Design构建现代化前端应用
172 0
|
6月前
|
JavaScript 安全 容器
Vue3 + setup + TypeScript: 构建现代、类型安全的Vue应用的关键技巧总结
当使用 setup 的时候,组件直接引入就可以了,不需要再自己手动注册
|
6月前
|
JavaScript 前端开发 开发工具
Vue3 + Vite + TypeScript + Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(3)
Vue3 + Vite + TypeScript + Element-Plus:从零到一构建企业级后台管理系统(前后端开源)(3)
|
JavaScript 前端开发 编译器
🎖️使用 esbuild 简化 TypeScript 构建并跳过 tsc/tsx
JavaScript 生态系统一直在不断创新,最近的一位游戏规则改变者是 esbuild,这是一个极速的 JavaScript 和 TypeScript 打包器。
1003 0