玛雅,我也会写vite插件了!

起因

最近学习如何编写vite插件,于是便想着写一个用于上传项目产物到oss的vite插件。支持自定义实现上传方法。

话不多说,干就干完了。

源码仓库

github

创建项目

mkdir vite-plugin-oss
cd vite-plugin-oss
yarn init -y

安装项目所需依赖

  • 安装tsup(用于构建项目)
yarn add tsup -D

运行时依赖

依赖 描述
@baiducloud/sdk 百度云sdk
ali-oss 阿里云oss-sdk
glob
picocolors

构建时依赖

  "devDependencies": {
    "@types/ali-oss": "^6.16.11",
    "@types/glob": "^8.1.0",
    "@types/node": "^20.11.27",
    "tsup": "^8.0.2",
    "typescript": "^5.4.2",
    "vite": "^5.1.6"
  },

最终package.json文件

{
  "name": "@isfive/vite-plugin-oss",
  "version": "1.0.2",
  "main": "dist/index.mjs",
  "module": "dist/index.mjs",
  "types": "dist/index.d.mts",
  "files": [
    "dist/*"
  ],
  "license": "MIT",
  "scripts": {
    "build": "tsup"
  },
  "devDependencies": {
    "@types/ali-oss": "^6.16.11",
    "@types/glob": "^8.1.0",
    "@types/node": "^20.11.27",
    "tsup": "^8.0.2",
    "typescript": "^5.4.2",
    "vite": "^5.1.6"
  },
  "dependencies": {
    "@baiducloud/sdk": "^1.0.0-rc.42",
    "ali-oss": "^6.20.0",
    "glob": "^10.3.10",
    "path": "^0.12.7",
    "picocolors": "^1.0.0"
  },
  "peerDependencies": {
    "vite": "^5.0.0"
  }
}

tsup.config.ts(打包配置文件)

import { defineConfig } from 'tsup'

// 导出默认配置
export default defineConfig({
	// 生成d.ts文件
	dts: true,
	// 清理输出目录
	clean: true,
	// 最小化输出
	minify: true,
	// 将项目拆分为单个文件
	splitting: true,
	// 输出目录
	outDir: 'dist',
	// 格式
	format: [ 'esm'],
	// 入口文件
	entry: ['src/index.ts'],
	// 排除vite
	external:['vite'],
	// 保留注释
	keepNames: true,
	sourcemap: true,
})

编写插件代码

src/index.ts

// 导入 normalizePath 函数,用于处理路径
import { normalizePath } from "vite";
// 导入 picocolors 库,用于生成随机颜色
import color from "picocolors";
// 导入 globSync 函数,用于处理 glob 匹配
import { globSync } from "glob";
// 导入 path 库,用于处理文件路径
import path from "path";
// 导入 Options 类型,用于声明配置参数
import type { Options } from "./types";
import  IClient from "./IClient";
// 导出 resolver 中的所有内容
export * from "./resolver"
// 导出 types 中的所有内容
export * from "./types"
export type CustomClient = IClient & {}
// 定义 handleIgnore 函数,用于处理忽略规则
const handleIgnore = (
ignore: string[],
ssrServer: string,
ssrClient: string
) => {
if (ignore === undefined) return "";
if (ignore) {
if (ssrClient) {
return ["**/ssr-manifest.json", "**/*.html", ...ignore];
}
if (ssrServer) {
return ["**"];
}
}
};
// 定义 VitePluginOSS 函数,用于处理上传OSS操作
/**
* 
* @param config 插件入参
* @param {IClient} config.client - 上传客户端类
* @param {boolean} config.enabled - 是否开启上传
* @param {string[]} [config.ignore] - 忽略文件数组
* @param {boolean} [config.overwrite] - 是否覆盖原文件
* @param {string} [config.base] - 对象存储路径前缀
* @returns 
*/
const VitePluginOSS = (config: Options) => {
// 获取配置参数
const { client, enabled, ignore = [], overwrite = true,base="/" } = config;
// 打印配置参数
console.log("进入插件", config);
// 设置基础路径
let baseConfig = base;
// 设置构建路径
let buildConfig = {} as any;
// 返回插件对象
return {
name: "vite-plugin-oss",
enforce: "post",
apply: "build",
configResolved(resolvedConfig: any) {
// 存储最终解析的配置
buildConfig = resolvedConfig.build;
},
async closeBundle() {
//   console.log("VitePluginOSS", config);
//   console.log("resolvedConfig", buildConfig);
//   console.log("baseConfig", baseConfig);
// 获取输出目录路径
const outDirPath = normalizePath(
path.resolve(normalizePath(buildConfig.outDir))
);
// 获取 ssr 客户端路径
const ssrClient = buildConfig.ssrManifest;
// 获取 ssr 服务端路径
const ssrServer = buildConfig.ssr;
// 获取输出目录下的所有文件
const files = globSync(`${outDirPath}/**/*`, {
// 忽略 nodir
nodir: true,
// 忽略 dot
dot: true,
// 处理忽略规则
ignore: handleIgnore(ignore, ssrServer, ssrClient),
});
let statTime = new Date().getTime();
// 遍历所有文件
for (const fileFullPath of files) {
// 获取文件路径
const filePath = normalizePath(fileFullPath).split(`${outDirPath}/`)[1];
// 获取 oss 存储路径
const ossFilePath = baseConfig + filePath;
// 获取完成时路径
const completePath = ossFilePath;
// 打印输出路径
const output = `${buildConfig.outDir + filePath} => ${color.green(
completePath
)}`;
// 上传文件
await client.upload({
fileFullPath,
filePath,
ossFilePath,
completePath,
output,
overwrite,
});
console.log(fileFullPath);
}
// 计算耗时
const duration = (new Date().getTime() - statTime) / 1000;
console.log("");
console.log(
color.green(`文件已全部上传完毕 ^_^, 耗时 ${duration.toFixed(2)}秒`)
);
console.log("");
},
} as any;
}
// 导出 VitePluginOSS
export default VitePluginOSS;

src/types.ts

import OSS from "ali-oss";
import IClient from "./IClient";
//   阿里云oss配置类型
export type AliOSSConfig = OSS.Options;
//百度云bos配置
export type BOSConfig = {
endpoint: string; //传入Bucket所在区域域名
bucket: string;
credentials: {
ak: string; //您的AccessKey
sk: string; //您的SecretAccessKey
};
};
export type Config = AliOSSConfig | BOSConfig;
export type Options = {
base?:string; //对象存储路径前缀
overwrite?:boolean;//是否覆盖原文件
ignore?: string[]; //忽略文件数组
client: IClient;//上传客户端类
enabled: boolean;//是否开启上传
};
export type UploadParameter = {
// 是否覆盖
overwrite:boolean;
// 文件完整路径
fileFullPath:string;
// 文件相对路径
filePath:string;
// oss路径
ossFilePath:string;
// 完成时路径
completePath:string
// 输出路径
output:string
}

src/resolver.ts

// 导入IClient类型
import IClient from "./IClient";
// 导入AliOSSClient类型
import AliOSSClient from "./alioss";
// 导入BOSClint类型
import BOSClint from "./bos";
// 导入AliOSSConfig和BOSConfig类型
import type { AliOSSConfig,BOSConfig } from "./types";
// 导出AliOSSResolve函数,参数为AliOSSConfig类型,返回值为IClient类型
/**
* 
* @param config 
* @returns 
*/
export const AliOSSResolve = (config: AliOSSConfig): IClient => {
// 返回一个新的AliOSSClient实例
return new AliOSSClient(config);
};
/**
* 
* @param config 
* @returns 
*/
// 导出BaiduResolve函数,参数为BOSConfig类型,返回值为IClient类型
export const BaiduResolve = (config: BOSConfig): IClient => {
// 返回一个新的BOSClint实例
return new BOSClint(config);
};

src/alioss.ts(阿里云客户端实现类)

// 导入阿里OSS模块
import OSS from "ali-oss";
// 导入picocolors模块
import color from "picocolors";
// 导入IClient接口
import type IClient from "./IClient";
// 导入VitePluginOSS中的AliOSSConfig类型
import type { UploadParameter ,AliOSSConfig} from "./types";
// 实现IClient接口的AliOSSClient类
class AliOSSClient implements IClient {
// 定义OSS客户端
client: any;
constructor(createOssOption:AliOSSConfig) {
// 实例化OSS客户端
this.client = new OSS(createOssOption);
}
// 打印"AliOSSClient"
info() {
console.log("AliOSSClient");
}
// 上传文件
async upload({
overwrite,
filePath,
ossFilePath,
fileFullPath,
output ,
}:UploadParameter) {
// 如果options.overwrite为true,则直接上传
try {
await this.client.put(ossFilePath, fileFullPath, {
headers: {},
});
} catch (error) {
console.error("上传失败:",error)
}
}
}
export default AliOSSClient;

src/IClient.ts(上传客户端接口)

import { UploadParameter } from "./types";
interface IClient {
client: any;
upload: (uploadParameter:UploadParameter)=>Promise<unknown>;
info?: Function;
beforeUpload?: Function;
afterUpload?: Function;
}
export default IClient;

直接去仓库看代码吧

抄近路:github

vite-plugin-oss

vite插件:将打包后的文件上传到oss

安装

npm install @isfive/vite-plugin-oss --save-dev

使用方法

  • 百度云对象存储
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VitePluginOSS,{BaiduResolve} from "@isfive/vite-plugin-oss"
import type {BOSConfig} from "@isfive/vite-plugin-oss"
const config: BOSConfig = {
endpoint: "https://xxx.bcebos.com", //传入Bucket所在区域域名
bucket: "stoneku",
credentials: {
ak: "xxxxxxxxxxxxxxxx", //您的AccessKey
sk: "xxxxxxxxxxxxxxxxxxxxx" //您的SecretAccessKey
}
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),VitePluginOSS({
base:"/" //oss路径前缀
overwrite:true,
enabled:true,
client:BaiduResolve(config),
ignore:["xxx.xxx"] //忽略文件列表
})],
})
  • 阿里云对象存储
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VitePluginOSS,{AliOSSResolve} from "@isfive/vite-plugin-oss"
import type {AliOSSConfig} from "@isfive/vite-plugin-oss"
console.log(typeof VitePluginOSS)
const config: AliOSSConfig = {
accessKeyId: 'XXXXXXXXXXXXXXXX',
accessKeySecret: 'XXXXXXXXXXXXXXXXXXXX'
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),VitePluginOSS({
overwrite:true,
enabled:true,
client:AliOSSResolve(config),
})],
})
  • 自定义上传
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VitePluginOSS from "@isfive/vite-plugin-oss"
import type {CustomClient, UploadParameter} from "@isfive/vite-plugin-oss"
console.log(typeof VitePluginOSS)
/**
* 自定义resolve 需要继承 CustomClient接口
*/
class MyResolve implements CustomClient {
client: any
constructor() {}
// 需要实现upload方法
async upload (uploadParameter: UploadParameter){
console.log(uploadParameter.completePath) //完成路径
console.log(uploadParameter.fileFullPath) //文件完整路径
console.log(uploadParameter.filePath) //文件相对路径
/**
* 在这里做自定义文件上传
*/
}
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(),VitePluginOSS({
overwrite:true,
enabled:true,
client:new MyResolve(),
})],
})

原文链接:https://juejin.cn/post/7346720393414344743 作者:qiuquanwu

(0)
上一篇 2024年3月17日 上午10:17
下一篇 2024年3月17日 上午10:27

相关推荐

发表回复

登录后才能评论