小笔记:使用 unbuild 搭建 JavaScript 构建系统笔记

简介: 小笔记:使用 unbuild 搭建 JavaScript 构建系统笔记

使用 unbuild 搭建 JavaScript 构建系统


jcLee95

已入驻阿里云博客

邮箱 :291148484@163.com

简介:

本文是笔者阅读分析 elementPlus 项目时记录的。该项目用到了一个完全没有文档和资料的工具 unbuild

为了搞清楚其功能,在github上看了一下该模块的源代码并记录之。

unbuild 是一个快速实现编译ts生成 mjscjs 以及相应的类型声明.d.ts等的工具,它内部调用了 rollup 等其他工具,似乎只是简化了配置。

本文地址https://blog.csdn.net/qq_28550263/article/details/129016838

目 录


1. unbuild 简介与安装

2. unbuild 的用法

3. 该模块用到的一些模块

3.3 Node.js 的运行时Typescript 和 ESM 支持:jiti

3.4 将字节转换为人类可读的字符串:pretty-bytes

附录


1. unbuild 简介与安装

搭建前端项目时,我们经常需要寻找一些构建工具来辅助我们自动化地进行打包和完成其它工作,如 webpack、rollup、gulp、vite 等等。对于一些功能的需求比较简单的场景,我们用不着使用 webpack 之类的重型构建工具,完全可以考虑使用一些小工具完成。本文介绍的 unbuild 就是这样一个小工具,它是一个强大的基于 rollup 的捆绑器,支持 TypeScript 并生成 commonjs 和 module 格式 + 类型声明。它能够能够自动推断 package.json 中的 build 配置和条目。

你可以通过如下方式安装到项目依赖中:

npm i unbuild -D
# 或
yarn add unbuild -D
# 或
pnpm i unbuild -D

2. unbuild 的用法

2.1 配置文件 build.config.ts

unbuild CLI 基于配置文件 build.config.ts 工作,你应该手动在项目根目录中创建它,以便于执行脚本的时候被 unbuild 所找到。一个该文件的实例为:

import { defineBuildConfig } from 'unbuild'
export default defineBuildConfig({
    // 如果没有提供entries,将从package.json中自动推断出
    entries: [
        // 指定源文件入口,默认值为 './src/index'
        './src/index',
        // mkdist builder 传输文件到文件,保持原始源结构
        {
            builder: 'mkdist',
            input: './src/package/components/',
            outDir: './build/components'
        },
    ],
    // 指定输出目录,默认为 'dist'
    outDir: 'build',
    // 指定是否生成 .d.ts 声明文件
    declaration: true,
})

其中 defineBuildConfig 传入的对象可以配置以下内容:

配置项 类型 描述
rootDir string 指定根目录起始位置
entries BuildEntry[]; 指定源文件入口,默认值为 './src/index'
clean boolean 输出前是否清空输出目录
declaration boolean 指定是否生成 .d.ts 声明文件
outDir string 指定输出目录,默认为 'dist'
stub boolean 跳过 stub 的剩余部分
externals string[] 是否所有依赖项作为外部项添加(dependencies、peerDependencies)
dependencies string[] 从pkg推断的依赖关系
peerDependencies string[] 从pkg推断的依赖关系
devDependencies string[] 从pkg推断的依赖关系
alias { [find: string]: string;} 由 unbuild 传给 rollup 的 alias 配置项
replace { [find: string]: string;} 由 unbuild 传给 rollup 的 replace 配置项
rollup RollupBuildOptions 由 unbuild 传给 rollup 的构建选项
hooks Partial<BuildHooks>
preset string | BuildPreset

2.2 pacakage.json 与 cli 用法

{
  "main": "./dist/index.cjs",
  "module": "./dist/index.mjs",
  "types": "./dist/index.d.ts",
  "scripts": {
    "build": "unbuild",
    "dev": "pnpm run stub",
    "stub": "unbuild --stub"
  },
  "devDependencies": {
    "unbuild": "^0.7.4"
  }
}

3. 该模块用到的一些模块

这个部分是在分析 unbuild 模块源码中用到的一些库。

3.1 一款 await 的挂钩系统:hookable

3.1.1 简介与安装

该模块用于管理注册和调用钩子,你可以通过以下方式进行安装:

npm install hookable
# or
yarn add hookable
# or
pnpm install hookable

3.1.2 通过直接创建 hookable 实例使用

import { createHooks } from 'hookable'
// 创建一个 hookable 实例
const hooks = createHooks()
// 挂上 'hello'
hooks.hook('hello', () => { console.log('Hello World' )})
// 调用 'hello' 钩子
hooks.callHook('hello')

3.1.3 通过从 Hookable 扩展你的基类使用

import { Hookable } from 'hookable'
export default class FooLib extends Hookable {
  constructor() {
    // 调用父类进行初始化
    super()
    // 用自定义 logger 初始化可挂钩
    // super(consola)
  }
  async someFunction() {
    // 按顺序调用并等待“hook1”挂钩(如果有)
    await this.callHook('hook1')
  }
}

在插件中,注册任意钩子:

const lib = new FooLib()
// 注册“hook2”的处理程序
lib.hook('hook2', async () => { /* ... */ })
// 一次注册多个处理程序
lib.addHooks({
  hook1: async () => { /* ... */ },
  hook2: [ /* 也可以是一个数组 */ ]
})

3.1.4 Hookable 类的接口解析

3.2 轻量级文件到文件转换器:mkdist

3.2.1 简介与安装

Bundling 库有时候不是最佳的选择,因为通过捆绑传输丢失现代语法,也让我们丢失原有的文件结构,也有时通过将css提取到全局dist(vue) 而丢失关键css。

即使不使用,依赖项也将总是从 bundle 绑包中导入(第二个捆绑步骤可能会解决这个问题,但这通常不会发生在开发中和有副作用的依赖项中)。

虽然有像 tsc@babel/cli 这样的工具,但它们大多专注于传输文件,而不是保持源代码级别的质量。此外,它们缺乏对处理自定义扩展的支持,如。vue和复制assets

mkdist可以:

  • 复制所有的 assets;
  • 支持 Vue 单文件组件;
  • 通过 esbuild 实现快速和最小的转换;
  • .为 .ts, .js.vue 文件生成 d.ts 类型声明文件

你可以通过以下方式进行安装:

npm install mkdist
# or
yarn add mkdist
# or
pnpm install mkdist

3.2.2 mkdist 的用法

CLI 的语法格式

mkdist [rootDir] [--src=src] [--dist=dist] [--pattern=glob [--pattern=more-glob]] [--format=cjs|esm] [-d|--declaration] [--ext=mjs|js|ts]

3.3 Node.js 的运行时 Typescript 和 ESM 支持:jiti

3.3.1 简介与安装

jiti 是一款Node.js 的运行时 Typescript 和 ESM 支持模块,也就是说它可以让 Nodejs 运行时运行 Typescript 和 ESM 代码,该模块可以:

  • 无缝typescript和ESM语法支持;
  • ESM和CommonJS之间的无缝互操作性;
  • 替换 require 的同步API;
  • 超级苗条和零依赖;
  • 智能语法检测,避免额外的转换;
  • CommonJS缓存集成;
  • 文件系统传输文件硬缓存;
  • V8编译缓存;
  • 自定义解析别名

你可以通过如下之一的方式进行安装:

npm install jiti
# or
yarn add jiti
# or
pnpm install jiti

3.3.2 jiti 的用法

1. 编程用法

const jiti = require("jiti")(__filename);
jiti("./path/to/file.ts");

2. 命令行用法

若是全局安装了 jiti ,可以直接使用 jiti 命:

jiti index.ts

若只在当前项目中安装了 jiti ,可用 npx jiti 运行脚本文件

npx jiti index.ts

3.4 将字节转换为人类可读的字符串:pretty-bytes

3.4.1 简介与安装

该模块可用于打包时在终端更好地输出文件大小,可以通过如下方式进行安装:

npm install pretty-bytes
# or
yarn add pretty-bytes
# or
pnpm install pretty-bytes

3.4.2 用法

import prettyBytes from 'pretty-bytes';
prettyBytes(1337);
//=> '1.34 kB'
prettyBytes(100);
//=> '100 B'
// 以位为单位显示
prettyBytes(1337, {bits: true});
//=> '1.34 kbit'
// 显示文件大小差异
prettyBytes(42, {signed: true});
//=> '+42 B'
// 使用德语语言环境的本地化输出
prettyBytes(1337, {locale: 'de'});
//=> '1,34 kB'

附录

declare type BuildEntry = BaseBuildEntry | RollupBuildEntry | UntypedBuildEntry | MkdistBuildEntry;
interface BaseBuildEntry {
    builder?: 'untyped' | 'rollup' | 'mkdist';
    input: string;
    name?: string;
    outDir?: string;
    declaration?: Boolean;
}
interface UntypedBuildEntry extends BaseBuildEntry {
    builder: 'untyped';
    defaults?: Record<string, any>;
}
interface RollupBuildEntry extends BaseBuildEntry {
    builder: 'rollup';
}
interface MkdistBuildEntry extends BaseBuildEntry {
    builder: 'mkdist';
    format?: 'esm' | 'cjs';
    ext?: 'cjs' | 'mjs' | 'js' | 'ts';
}
import { RollupReplaceOptions } from '@rollup/plugin-replace';
import { RollupAliasOptions } from '@rollup/plugin-alias';
import { RollupNodeResolveOptions } from '@rollup/plugin-node-resolve';
import { RollupJsonOptions } from '@rollup/plugin-json';
import { Options as Options$1 } from 'rollup-plugin-dts';
declare type RollupCommonJSOptions = Parameters<typeof commonjs>[0] & {};
interface RollupBuildOptions {
    emitCJS?: boolean;
    cjsBridge?: boolean;
    inlineDependencies?: boolean;
    replace: RollupReplaceOptions | false;
    alias: RollupAliasOptions | false;
    resolve: RollupNodeResolveOptions | false;
    json: RollupJsonOptions | false;
    esbuild: Options | false;
    commonjs: RollupCommonJSOptions | false;
    dts: Options$1;
}
import { RollupOptions, RollupBuild } from 'rollup';
interface BuildHooks {
    'build:prepare': (ctx: BuildContext) => void | Promise<void>;
    'build:before': (ctx: BuildContext) => void | Promise<void>;
    'build:done': (ctx: BuildContext) => void | Promise<void>;
    'rollup:options': (ctx: BuildContext, options: RollupOptions) => void | Promise<void>;
    'rollup:build': (ctx: BuildContext, build: RollupBuild) => void | Promise<void>;
    'rollup:dts:options': (ctx: BuildContext, options: RollupOptions) => void | Promise<void>;
    'rollup:dts:build': (ctx: BuildContext, build: RollupBuild) => void | Promise<void>;
    'rollup:done': (ctx: BuildContext) => void | Promise<void>;
    'mkdist:entries': (ctx: BuildContext, entries: MkdistBuildEntry[]) => void | Promise<void>;
    'mkdist:entry:options': (ctx: BuildContext, entry: MkdistBuildEntry, options: MkdistOptions) => void | Promise<void>;
    'mkdist:entry:build': (ctx: BuildContext, entry: MkdistBuildEntry, output: {
        writtenFiles: string[];
    }) => void | Promise<void>;
    'mkdist:done': (ctx: BuildContext) => void | Promise<void>;
    'untyped:entries': (ctx: BuildContext, entries: UntypedBuildEntry[]) => void | Promise<void>;
    'untyped:entry:options': (ctx: BuildContext, entry: UntypedBuildEntry, options: any) => void | Promise<void>;
    'untyped:entry:schema': (ctx: BuildContext, entry: UntypedBuildEntry, schema: Schema) => void | Promise<void>;
    'untyped:entry:outputs': (ctx: BuildContext, entry: UntypedBuildEntry, outputs: UntypedOutputs) => void | Promise<void>;
    'untyped:done': (ctx: BuildContext) => void | Promise<void>;
}
interface BuildContext {
    options: BuildOptions;
    pkg: PackageJson;
    buildEntries: {
        path: string;
        bytes?: number;
        exports?: string[];
        chunks?: string[];
    }[];
    usedImports: Set<string>;
    hooks: Hookable<BuildHooks>;
}
declare type BuildPreset = BuildConfig | (() => BuildConfig);
interface BuildConfig extends DeepPartial<Omit<BuildOptions, 'entries'>> {
    entries?: (BuildEntry | string)[];
    preset?: string | BuildPreset;
    hooks?: Partial<BuildHooks>;
}
目录
相关文章
|
9天前
|
SQL JavaScript 安全
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
47 11
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
|
10天前
|
前端开发 JavaScript Java
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
57 13
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
|
11天前
|
人工智能 JavaScript 关系型数据库
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
57 14
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
|
13天前
|
人工智能 JavaScript 安全
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
57 13
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
|
1月前
|
中间件 API
Next.js 实战 (八):使用 Lodash 打包构建产生的“坑”?
这篇文章介绍了作者在使用Nextjs15进行项目开发时遇到的部署问题。在部署过程中,作者遇到了打包构建时的一系列报错,报错内容涉及动态代码评估在Edge运行时不被允许等问题。经过一天的尝试和调整,作者最终删除了lodash-es库,并将radash的部分源码复制到本地,解决了打包报错的问题。文章最后提供了项目的线上预览地址,并欢迎读者留言讨论更好的解决方案。
41 10
|
2月前
|
监控 算法 JavaScript
基于 Node.js Socket 算法搭建局域网屏幕监控系统
在数字化办公环境中,局域网屏幕监控系统至关重要。基于Node.js的Socket算法实现高效、稳定的实时屏幕数据传输,助力企业保障信息安全、监督工作状态和远程技术支持。通过Socket建立监控端与被监控端的数据桥梁,确保实时画面呈现。实际部署需合理分配带宽并加密传输,确保信息安全。企业在使用时应权衡利弊,遵循法规,保障员工权益。
52 7
|
2月前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
71 12
|
3月前
|
JavaScript NoSQL API
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发如同一座灯塔,指引着数据的海洋。本文将带你航行在Node.js的海域,探索如何从一张白纸到完成一个功能完备的RESTful API。我们将一起学习如何搭建开发环境、设计API结构、处理数据请求与响应,以及实现数据库交互。准备好了吗?启航吧!
|
3月前
|
JSON 缓存 JavaScript
深入浅出:使用Node.js构建RESTful API
在这个数字时代,API已成为软件开发的基石之一。本文旨在引导初学者通过Node.js和Express框架快速搭建一个功能完备的RESTful API。我们将从零开始,逐步深入,不仅涉及代码编写,还包括设计原则、最佳实践及调试技巧。无论你是初探后端开发,还是希望扩展你的技术栈,这篇文章都将是你的理想指南。
|
3月前
|
开发框架 JavaScript 前端开发
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势
TypeScript 是一种静态类型的编程语言,它扩展了 JavaScript,为 Web 开发带来了强大的类型系统、组件化开发支持、与主流框架的无缝集成、大型项目管理能力和提升开发体验等多方面优势。通过明确的类型定义,TypeScript 能够在编码阶段发现潜在错误,提高代码质量;支持组件的清晰定义与复用,增强代码的可维护性;与 React、Vue 等框架结合,提供更佳的开发体验;适用于大型项目,优化代码结构和性能。随着 Web 技术的发展,TypeScript 的应用前景广阔,将继续引领 Web 开发的新趋势。
66 2

热门文章

最新文章

  • 1
    当面试官再问我JS闭包时,我能答出来的都在这里了。
    47
  • 2
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 3
    Node.js 中实现多任务下载的并发控制策略
    34
  • 4
    【2025优雅草开源计划进行中01】-针对web前端开发初学者使用-优雅草科技官网-纯静态页面html+css+JavaScript可直接下载使用-开源-首页为优雅草吴银满工程师原创-优雅草卓伊凡发布
    26
  • 5
    【JavaScript】深入理解 let、var 和 const
    49
  • 6
    【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
    47
  • 7
    【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
    57
  • 8
    【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
    57
  • 9
    如何通过pm2以cluster模式多进程部署next.js(包括docker下的部署)
    72
  • 10
    【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
    57