从0搭建Vue3组件库:使用gulp自动化处理打包与发布

简介: 从0搭建Vue3组件库:使用gulp自动化处理打包与发布

image.png


前言



这一篇文章我们将实现组件库打包与发布的自动化,将我们从繁琐的打包和发布流程中解救出来(如:打包前要删除打包文件,发布前要增加版本号、修改package.json、每个包都要执行打包命令等等等等)。同时我们的组件样式的打包也将采用gulp流程化控制。 如果你不太了解gulp,你可以先花十分钟时间看下前端自动化工具gulp急速入门这篇文章。如果你对vue3组件库开发感兴趣的话,你也可以先阅读前置文章使用Vite和TypeScript带你从零打造一个属于自己的Vue3组件库。当然你也可以直接阅读本篇文章,每一步我都会有详细的解释说明。相信看完这篇文章你就会发现使用gulp自动化流程带来的魅力。


我们要用gulp做什么?



我们要用gulp处理我们的打包流程和发布流程,如下图所示

image.png


让gulp支持ts和es6


我们需要使用ts以及新的es6语法,而gulp是不支持的,所以我们需要安装一些依赖使得gulp支持这些

  • gulp
  • @types/gulp  gulp的ts声明文件
  • sucras 让我们执行gulp可以使用最新语法并且支持ts
pnpm i gulp @types/gulp sucrase -D -w


测试gulp


我们在根目录的package.json添加打包组件库脚本命令

...
 "scripts": {
    "build:kitty": "gulp -f packages/components/script/build/gulpfile.ts"
  }
...

根目录执行npm run build:kittygulp去执行packages/components/script/build/gulpfile.ts,所以在packages/components下新建gulpfile.ts并写个测试的脚本,看下我们的gulp是否生效

export default () => {
    console.log('任务完成')
    return Promise.resolve()
}

然后我们执行npm run build:kitty就会发现gulpfile.ts被执行了

image.png

接下来我们就可以使用gulp来实现我们的自动化流程了


删除dist文件夹



在打包之前我们首先是要删除我们上一次打包留下的dist文件的.我们通常删除一个文件夹只需要在cmd中输入rm -rf xxx即可,所以说我们需要写一个执行脚本命令的任务,而Node.js的子进程 child_process模块下有一 spawn 函数,可以用于调用系统上的命令。所以我们可以利用它来实现这个任务函数。因为我们是在项目根目录下执行的,所以在实现这个函数前我们还需要维护一些路径信息。


script下新建utils/paths.ts,以后的路径信息都放在这

import { resolve } from 'path'
//组件库根目录
export const componentPath = resolve(__dirname, '../../')
script/build/gulpfile.ts
import { spawn } from 'child_process'
import { series } from 'gulp'
import { componentPath } from '../utils/paths'
console.log(componentPath);
const run = async (command: string) => {
    //cmd表示命令,args代表参数,如 rm -rf  rm就是命令,-rf就为参数
    const [cmd, ...args] = command.split(' ')
    return new Promise((resolve, reject) => {
        const app = spawn(cmd, args, {
            cwd: componentPath,//执行命令的路径
            stdio: 'inherit', //输出共享给父进程
            shell: true
        })
        //执行完毕关闭并resolve
        app.on('close', resolve)
    })
}
export default series(async () => run(`rm -rf ${componentPath}/dist`))

因为我们还没有打包所以组件库目录下还没有dist文件夹,所以我们先暂时新建个dist文件夹模拟一下,我们的目录结构如下

image.png

然后在根目录下执行pnpm run build:kitty我们就会发现dist目录被删除了,此时我们的删除dist命令便完成了


注意

如果你的vscode终端没有配置git base是无法执行rm -rf这种命令的,所以你需要给你的vscode终端配置git base,配置方法如图示:

image.png

image.png

image.png

"terminal.integrated.profiles.windows": {
    "bash": {
      "path": "D:\\Git\\bin\\bash.exe", // 找到自己git安装目录所在xxx\bash.exe
      "args": []
    }
  }


打包流程控制



接下来我们开始我们的组件库打包流程控制,打包组件库我们分为打包组件和打包样式两部分


处理样式


处理样式之前我们要安装一些依赖


pnpm i gulp-less @types/gulp-less gulp-autoprefixer @types/gulp-autoprefixer -D -w

其中gulp-less是gulp中处理less的插件,gulp-autoprefixer是为css加前缀的的插件。@types/xxx则是它们的声明文件

然后写一个处理样式的任务buildStyle

//script/build/gulpfile.ts
import { spawn } from 'child_process'
import { series, src, dest, task } from 'gulp'
import { componentPath } from '../utils/paths'
import less from "gulp-less"
import autoprefixer from 'gulp-autoprefixer'
const run = async (command: string) => {
    //cmd表示命令,args代表参数,如 rm -rf  rm就是命令,-rf就为参数
    const [cmd, ...args] = command.split(' ')
    return new Promise((resolve, reject) => {
        const app = spawn(cmd, args, {
            cwd: componentPath,//目录指的是组件库根目录
            stdio: 'inherit', //输出共享给父进程
            shell: true //mac不需要开启,windows下git base需要开启支持
        })
        //执行完毕关闭并resolve
        app.on('close', resolve)
    })
}
//处理样式
const buildStyle = () => {
    return src(`${componentPath}/src/**/style/**.less`)
        .pipe(less())
        .pipe(
            autoprefixer()
        )
        .pipe(dest(`${componentPath}/dist/lib/src`))
        .pipe(dest(`${componentPath}/dist/es/src`));
};
export default series(async () => run(`rm -rf ${componentPath}/dist`), async () => buildStyle())

我们会发现gulp处理方式是以流的方式一层一层往下走,最后走到我们打包后的目录下。

然后我们根目录执行pnpm run build:kitty,就会发现样式被打包进了dist文件中

控制台输出如下

image.png

dist文件如下

image.png


组件打包


接下来就是组件的打包,详细的流程可以参考使用Vite和TypeScript带你从零打造一个属于自己的Vue3组件库,这里对原来的components/vite.config.ts做一个条调整

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue"
import dts from 'vite-plugin-dts'
import { resolve } from 'path'
export default defineConfig(
    {
        build: {
            target: 'modules',
            //打包文件目录
            outDir: "es",
            //压缩
            minify: true,
            //css分离
            //cssCodeSplit: true,
            rollupOptions: {
                //忽略打包vue文件
                external: ['vue', /\.less/],
                input: ['src/index.ts'],
                output: [
                    {
                        format: 'es',
                        //不用打包成.es.js,这里我们想把它打包成.js
                        entryFileNames: '[name].js',
                        //让打包目录和我们目录对应
                        preserveModules: true,
                        //配置打包根目录
                        dir: resolve(__dirname, './dist/es'),
                        preserveModulesRoot: 'dist'
                    },
                    {
                        format: 'cjs',
                        //不用打包成.mjs
                        entryFileNames: '[name].js',
                        //让打包目录和我们目录对应
                        preserveModules: true,
                        //配置打包根目录
                        dir: resolve(__dirname, './dist/lib'),
                        preserveModulesRoot: 'src'
                    }
                ]
            },
            lib: {
                entry: './index.ts',
                formats: ['es', 'cjs']
            }
        },
        plugins: [
            vue(),
            dts({
                outputDir: resolve(__dirname, './dist/es/src'),
                //指定使用的tsconfig.json为我们整个项目根目录下掉,如果不配置,你也可以在components下新建tsconfig.json
                tsConfigFilePath: '../../tsconfig.json'
            }),
            //因为这个插件默认打包到es下,我们想让lib目录下也生成声明文件需要再配置一个
            dts({
                outputDir: resolve(__dirname, './dist/lib/src'),
                tsConfigFilePath: '../../tsconfig.json'
            }),
            {
                name: 'style',
                generateBundle(config, bundle) {
                    //这里可以获取打包后的文件目录以及代码code
                    const keys = Object.keys(bundle)
                    for (const key of keys) {
                        const bundler: any = bundle[key as any]
                        //rollup内置方法,将所有输出文件code中的.less换成.css,因为我们当时没有打包less文件
                        this.emitFile({
                            type: 'asset',
                            fileName: key,//文件名名不变
                            source: bundler.code.replace(/\.less/g, '.css')
                        })
                    }
                }
            }
        ],
        resolve: {
            alias: {
                '@': resolve(__dirname, 'src'),
            },
        }
    }
)

主要将原来的打包路径以及声明文件路径指向了dist目录下。

然后我们写个打包组件函数buildComponent

//打包组件
const buildComponent = async () => {
    await run(`cd ${componentPath}`)
    run('pnpm run build')
}

其实步骤很简单,就是在组件库根目录(packages/componmets/)执行pnpm run build,所以components/package.json的脚本命令改为

"scripts": {
    "build": "vite build"
  }

由于我们打包组件和打包样式是互不影响的,所以这两部分可以并行执行parallel,此时我们的gulpfile.ts可以这样写

import { series, src, dest, parallel } from 'gulp'
...
//打包组件
const buildComponent = async () => {
    await run(`cd ${componentPath}`)
    run('pnpm run build')
}
export default series(
    async () => run(`rm -rf ${componentPath}/dist`),
    parallel(
        async () => buildStyle(),
        async () => buildComponent()
    )
)

根目录执行pnpm run build:kitty便可完成我们的组件与样式的打包,打包后的目录如图所示

image.png


发布流程



接下来我们要做的就是发布了,因为我们需要在dist下进行发布,所以我们要做的就是先往dist下塞一个用于发布的package.json,这个package.json我们要在组件库下新建个中转的package.json复制到dist下。

在components下新建transitpkg/package.json

{
  "name": "kitty-ui",
  "version": "1.0.0",
  "main": "lib/index.js",
  "module": "es/index.js",
  "files": ["es", "lib"],
  "keywords": ["kitty-ui", "vue3组件库"],
  "author": "小月",
  "license": "MIT",
  "description": "",
  "typings": "lib/index.d.ts"
}

根目录下package.json新增命令pnpm run publish:kitty

"scripts": {
    "build:kitty": "gulp -f packages/components/script/build/gulpfile.ts",
    "publish:kitty": "gulp -f packages/components/script/publish/gulpfile.ts"
  }

同样的我们新建packages/components/script/publish/gulpfile.ts,(相关注释写在代码中)

import { spawn } from 'child_process'
import { series, src, dest } from 'gulp'
import { componentPath } from '../utils/paths'
const run = async (command: string, path: string) => {
    //cmd表示命令,args代表参数,如 rm -rf  rm就是命令,-rf就为参数
    const [cmd, ...args] = command.split(' ')
    return new Promise((resolve, reject) => {
        const app = spawn(cmd, args, {
            cwd: path,//执行命令的路径
            stdio: 'inherit', //输出共享给父进程
            shell: true //mac不需要开启,windows下git base需要开启支持
        })
        //执行完毕关闭并resolve
        app.on('close', resolve)
    })
}
//复制
const copypackage = async () => {
    return src(`${componentPath}/transitpkg/**`).pipe(dest(`${componentPath}/dist/`));
};
//发布任务
const publish = async () => {
    //先给transitpkg升个版本
    await run('pnpm version patch', `${componentPath}/transitpkg`)
    //复制到dist目录
    await copypackage()
    //在dist下执行发布命令
    await run('npm publish', `${componentPath}/dist`)
    // run('pnpm publish')
}
export default series(
    async () => publish()
)

最后我们执行pnpm run publish:kitty便可发布我们的组件库。其实这里的一些方法可以提取到utils中,gulp的任务函数也可以写到其它地方,gulpfile.ts只作为一个集中执行的地方。具体 目录如下

image.png

相关代码可以到 kittyui 查看。

到这里我们的组件库已经可以自动打包和发布了


直接使用



  • 拉取kittyui
git clone https://gitee.com/geeksdidi/kittyui.git
  • 安装pnpm
npm i pnpm -g
  • 安装esno
npm i esno -g
  • 安装项目所有依赖
pnpm install
  • 打包组件库
pnpm run build:kitty
  • 发布组件库

发布前记得到components/transitpkg/package.json下改个包名(不然肯定会重名,因为我已经发布过了)

pnpm run publish:kitty


相关文章
|
3月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
165 10
|
7月前
|
Web App开发 Python
在ModelScope中,你可以使用Python的浏览器自动化库
在ModelScope中,你可以使用Python的浏览器自动化库
77 2
|
7月前
|
移动开发 安全 数据安全/隐私保护
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
|
7月前
|
数据可视化 测试技术 持续交付
自动化测试神器:Python之Pytest库入门使用
自动化测试神器:Python之Pytest库入门使用
195 4
|
14天前
|
安全 API 文件存储
Yagmail邮件发送库:如何用Python实现自动化邮件营销?
本文详细介绍了如何使用Yagmail库实现自动化邮件营销。Yagmail是一个简洁强大的Python库,能简化邮件发送流程,支持文本、HTML邮件及附件发送,适用于数字营销场景。文章涵盖了Yagmail的基本使用、高级功能、案例分析及最佳实践,帮助读者轻松上手。
27 4
|
2月前
|
JavaScript 前端开发 搜索推荐
Gulp:构建自动化与任务管理的强大工具
【10月更文挑战第13天】Gulp:构建自动化与任务管理的强大工具
82 0
|
7月前
|
Web App开发 IDE 测试技术
【专栏】Selenium 是一款广泛使用的自动化测试框架:深入理解 Selenium 的核心组件
【4月更文挑战第27天】Selenium 是一款广泛使用的自动化测试框架,核心组件包括 WebDriver(与浏览器交互的接口,支持多浏览器测试),IDE(可视化的测试脚本录制和编辑工具)和 Grid(分布式测试,实现多机器并行测试)。通过这些组件,开发者能高效、稳定地进行自动化测试,但需注意浏览器兼容性、脚本维护和性能问题。理解并掌握这些组件的使用,能提升测试效率和质量。
96 5
|
4月前
|
数据采集 数据可视化 数据挖掘
利用 Jupyter 实现自动化报告生成 展示如何结合 Jupyter 和 Python 库
【8月更文第29天】为了创建自动化报告,我们可以利用 Jupyter Notebook 结合 Python 的强大库如 Pandas、Matplotlib 和 Seaborn 来处理数据、制作图表,并使用 Jinja2 模板引擎来生成 HTML 报告。这种方式非常适合需要定期生成相同类型报告的情况,比如数据分析、业务报表等。
193 1
|
4月前
|
缓存 开发者 Docker
Dockerfile是Docker容器化过程中的核心组件,它允许开发者以一种可重复、可移植的方式自动化地构建Docker镜像
【8月更文挑战第19天】Dockerfile是构建Docker镜像的脚本文件,含一系列指令定义镜像构建步骤。每条大写指令后跟至少一个参数,按序执行,每执行一条指令即生成新的镜像层。常用指令包括:FROM指定基础镜像;RUN执行构建命令;EXPOSE开放端口;CMD指定容器启动行为等。优化策略涉及减少镜像层数、选择轻量基础镜像、利用缓存及清理冗余文件。示例:基于Python应用的Dockerfile包括设置工作目录、复制文件、安装依赖等步骤。掌握Dockerfile有助于高效自动化构建镜像,加速应用部署。
39 1
|
6月前
|
程序员 API 计算机视觉
技术经验解读:【python自动化】02.pywin32库自动操作键鼠(保姆级代码注释)
技术经验解读:【python自动化】02.pywin32库自动操作键鼠(保姆级代码注释)
151 0