前言

在上一篇给大家带来了《如何入门去写一个 vite 插件》,能看的出来同学们也甚是喜欢,如果大家对 vite 感兴趣,可以关注我的专栏《vite 从入门到精通》;

因为公司目前大部分项目都已转为 vite,为了给社区添加一份贡献,故开发了一个已经应用到项目中的vite 前端打包进度条 的插件 vite-plugin-progress;

介绍

vite-plugin-progress 插件是一个在打包时展示进度条的插件,如果您觉得该插件对您的项目有帮助,欢迎 star ⭐️ 支持一下,感谢!

用法

安装

 # npm
 npm i vite-plugin-progress -D 
 # yarn 
 yarn add vite-plugin-progress -D
 # pnpm 
 pnpm i vite-plugin-progress -D

使用(不带参数):在 vite.config.js / vite.config.ts 中配置

import progress from 'vite-plugin-progress'
 export default {
   plugins: [
     progress()
   ]
 }

参数 0ptions

  • format :自定义进度条的格式;
  • width :进度条在终端中的宽度;
  • complete :完成后的默认字符 \u2588 ;
  • incomplete :未完成的默认字符 \u2591 ;
  • renderThrottle :间隔更新时间默认16(毫秒);
  • clear :完成后是否清除终端,默认 false;
  • callback :完成后执行的回调函数;
  • stream 终端中的输出格式流,默认 stderr ;
  • head :进度条的头字符;

参数 options 中的 format 中各个标记含义:

  • :bar :代表进度条;
  • :current :当前执行到哪的刻度;
  • :total :总刻度;
  • :elapsed :所用时间(秒);
  • :percent :完成的百分比;
  • :eta :预计完成时间(秒);
  • :rate :每一秒的速率;

使用(带参数)

// vite.config.js / vite.config.ts
import progress from 'vite-plugin-progress'
export default {
  plugins: [
    progress({
        format: 'building [:bar] :percent',
        total: 200,
        width: 60,
        complete: '=',
        incomplete: '',
    })
  ]
}

给自定义进度条加点颜色

安装 picocolors :

 pnpm i picocolors -D

使用:

 // vite.config.js / vite.config.ts
 import progress from 'vite-plugin-progress'
 import colors from 'picocolors'
 export default {
   plugins: [
     progress({
         format:  `${colors.green(colors.bold('Bouilding'))} ${colors.cyan('[:bar]')} :percent`
     })
   ]
 }

如果您只想使用该插件的话,那么现在就去安装使用吧!

如果您对实现思路感兴趣的话,那么您可以继续向下滚动查阅哟 ~

实现思路

其实实现这个功能,我们最主要的考虑就是当前 vite 打包的进度到哪里了,那么我们思考两个问题:

  • 考量当前 vite 打包的进度是到哪里了?
  • 如何知道当前打包的进度?

熟悉 webpack 的朋友,肯定对 webpack 的打包进度条也不陌生;会发现在 webpack 中,webpack 暴露了一个 webpack.ProgressPlugin 的事件钩子,这样就导致在 webpack 中实现进度条会很容易,直接通过该钩子去封装即可;

但是在 vite 中由于是基于 Rollup 来构建打包代码,所以我们是没法知道当前 vite 打包进度的;

借用 vite 其中某位作者的原话:

简单理解意思就是说在 vite 打包时,是没法知道进度条的 0%-100%,因为您必须先确定要构建的模块的总数

虽然我们不知道模块总数,但是我们可以在第一次打包时模拟一个;

并且在第一次打包的时候,我们 记录下对应的模块数量,然后 缓存起来,这样我们不就可以知道对应的模块数量了吗?

说干就干 ~

第一次打包(模拟模块总数)

因为我们可以知道 src 目录 下所有的文件总数,所以就可以假设在第一次打包时用该总数来代替模块总数;

那么简单公式:进度条百分比 = 当前转换的模块 / 模拟的模块总数

import type { PluginOption } from 'vite';
import rd from 'rd';
export default function viteProgressBar(options?: PluginOptions): PluginOption {
    // 文件类型总数
    let fileCount = 0
    let transformCount = 0
    let transformed = 0 // 当前已转换的数量
    retun {
        ...	
	config(config, { command }) {			
            if (command === 'build') {
		const readDir = rd.readSync('src');
                const reg = /\.(vue|ts|js|jsx|tsx|css|scss||sass|styl|less)$/gi;
                readDir.forEach((item) => reg.test(item) && fileCount  );
            }
	},
	transform(code, id) {			
            transformCount  
            const reg = /node_modules/gi;
            if (!reg.test(id){
		percent =  (transformed / fileCount).toFixed(2)			
            }
	}
    }
}

与进度条配合

那么既然我们已经算出了基本的进度条,也知道了基本思路,那么我们就把进度条加进去

import type { PluginOption } from 'vite';
import progress from 'progress';
import rd from 'rd';
export default function viteProgressBar(options?: PluginOptions): PluginOption {
    let fileCount = 0	// 文件类型总数
    let transformCount = 0 // 转换的模块总数
    let transformed = 0 // 当前已转换的数量
    let lastPercent = 0; // 记录上一次进度条百分比
    const bar: progress;
    retun {
	...			
        config(config, { command }) {		
            if (command === 'build') {		
		// 初始化进度条
		options = {
                    width: 40,
                    complete: '\u2588',
                    incomplete: '\u2591',
                    ...options
                };
                options.total = options?.total || 100;
                const transforming = isExists ? `${colors.magenta('Transforms:')} :transformCur/:transformTotal | ` : ''
                const chunks = isExists ? `${colors.magenta('Chunks:')} :chunkCur/:chunkTotal | ` : ''
                const barText = `${colors.cyan(`[:bar]`)}`
                const barFormat =
                    options.format ||
                   `${colors.green('Bouilding')} ${barText} :percent | ${transforming}${chunks}Time: :elapseds`
                delete options.format;
                bar = new progress(barFormat, options as ProgressBar.ProgressBarOptions);				
                                    // 统计目录下的文件总数
                const readDir = rd.readSync('src');
                const reg = /\.(vue|ts|js|jsx|tsx|css|scss||sass|styl|less)$/gi;
                readDir.forEach((item) => reg.test(item) && fileCount  );
            }	
        },
	transform(code, id) {
            transformCount  
            const reg = /node_modules/gi;
            if (!reg.test(id){
                 lastPercent = percent =  (transformed / fileCount).toFixed(2)
            }
            // 更新进度条
            bar.update(lastPercent, {
		transformTotal: cacheTransformCount,
                transformCur: transformCount,
                chunkTotal: cacheChunkCount,
                chunkCur: 0,
            })	
        },
	closeBundle() {		
            // close progress
            bar.update(1)
            bar.terminate()
	}
    }
}

添加缓存

为了更准确的知道打包的进度,那么我们在第一次模拟了总数的时候,也要同时把真实的模块总数给缓存起来,这样在下一次打包时才能更加准确的计算出进度条;

新增缓存文件 cache.ts

import fs from 'fs';
import path from 'path';
const dirPath = path.join(process.cwd(), 'node_modules', '.progress');
const filePath = path.join(dirPath, 'index.json');
export interface ICacheData {
    /**
     * 转换的模块总数
     */
    cacheTransformCount: number;
    /**
     * chunk 的总数
     */
    cacheChunkCount: number
}
/**
 * 判断是否有缓存
 * @return boolean
 */
export const isExists = fs.existsSync(filePath) || false;
/**
 * 获取缓存数据
 * @returns ICacheData
 */
export const getCacheData = (): ICacheData => {
    if (!isExists) return {
        cacheTransformCount: 0,
        cacheChunkCount: 0
    };
    return JSON.parse(fs.readFileSync(filePath, 'utf8'));
};
/**
 * 设置缓存数据
 * @returns 
 */
export const setCacheData = (data: ICacheData) => {
    !isExists && fs.mkdirSync(dirPath);
    fs.writeFileSync(filePath, JSON.stringify(data));
};

使用缓存

// 缓存进度条计算
function runCachedData() {
    if (transformCount === 1) {
        stream.write('\n');
        bar.tick({
            transformTotal: cacheTransformCount,
            transformCur: transformCount,
            chunkTotal: cacheChunkCount,
            chunkCur: 0,
        })
    }
    transformed          
    percent = lastPercent =  (transformed / (cacheTransformCount   cacheChunkCount)).toFixed(2)
}

实现架构图

最后

该系列会是一个持续更新系列,关于整个专栏 《Vite 从入门到精通》,我主要会从如下图几个方面讲解,请大家拭目以待吧!!!

更多关于vite打包插件的资料请关注Devmax其它相关文章!

vite插件打包更顺畅使用技巧示例的更多相关文章

  1. 使用pyinstaller打包.exe文件的详细教程

    PyInstaller是一个跨平台的Python应用打包工具,能够把 Python 脚本及其所在的 Python 解释器打包成可执行文件,下面这篇文章主要给大家介绍了关于使用pyinstaller打包.exe文件的相关资料,需要的朋友可以参考下

  2. jQuery插件HighCharts绘制的基本折线图效果示例【附demo源码下载】

    这篇文章主要介绍了jQuery插件HighCharts绘制的基本折线图效果,结合实例形式分析了jQuery基于HighCharts插件绘制图形的具体实现步骤与相关操作技巧,并附带demo源码供读者下载参考,需要的朋友可以参考下

  3. jQuery插件实现的日历功能示例【附源码下载】

    这篇文章主要介绍了jQuery插件实现的日历功能,结合完整实例形式分析了jQuery datepicker插件实现日历功能的相关操作技巧,需要的朋友可以参考下

  4. vue打包chunk-vendors.js文件过大导致页面加载缓慢的解决

    这篇文章主要介绍了vue打包chunk-vendors.js文件过大导致页面加载缓慢的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  5. 使用JQuery自动完成插件Auto Complete详解

    这篇文章主要介绍了使用JQuery自动完成插件Auto Complete详解,使用JQuery自动完成插件,更新现有图书列表页面上的搜索,当用户键入的时候立即显示结果。,需要的朋友可以参考下

  6. 使用Vite+Vue3+Vant全家桶快速构建项目步骤详解

    这篇文章主要为大家介绍了使用Vite+Vue3+Vant全家桶快速构建项目步骤详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  7. jQuery插件JWPlayer视频播放器用法实例分析

    这篇文章主要介绍了jQuery插件JWPlayer视频播放器用法,结合实例形式分析了JWPlayer插件播放视频的相关操作技巧,需要的朋友可以参考下

  8. jQuery插件FusionCharts绘制的3D双柱状图效果示例【附demo源码】

    这篇文章主要介绍了jQuery插件FusionCharts绘制的3D双柱状图效果,涉及jQuery使用插件FusionCharts结合xml数据绘制的3D双柱状图的相关操作技巧,需要的朋友可以参考下

  9. jQuery自动完成插件completer附源码下载

    这篇文章主要介绍了jQuery自动完成插件completer的相关资料,需要的朋友可以参考下

  10. php打包网站并在线压缩为zip

    这篇文章主要介绍了php打包网站并在线压缩为zip的相关资料,具有一定的参考价值,需要的朋友可以参考下

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部