第一步:创建插件项目结构
首先创建项目目录并初始化:
mkdir vue-toast-notification
cd vue-toast-notification
npm init -y
npm install vue@3
npm install -D vite @vitejs/plugin-vue
第二步:编写插件代码
创建 src/index.js 文件:
import { createVNode, render } from 'vue';
import ToastComponent from './Toast.vue';
// 创建一个容器元素
const container = document.createElement('div');
document.body.appendChild(container);
// 存储当前显示的 toast 实例
let currentToast = null;
// Toast 函数,用于显示提示
function showToast(options = {}) {
// 如果已有 toast 显示,则先移除
if (currentToast) {
render(null, container);
}
// 默认配置
const defaults = {
message: '',
type: 'info', // info, success, error, warning
duration: 3000,
position: 'top-center'
};
// 合并配置
const props = { ...defaults, ...options };
// 创建虚拟节点
const vnode = createVNode(ToastComponent, props);
// 渲染组件
render(vnode, container);
currentToast = vnode;
// 设置自动关闭
if (props.duration > 0) {
setTimeout(() => {
closeToast();
}, props.duration);
}
return {
close: closeToast
};
}
// 关闭 toast
function closeToast() {
if (currentToast) {
render(null, container);
currentToast = null;
}
}
// 插件安装函数
const ToastPlugin = {
install(app) {
// 全局注册
app.config.globalProperties.$toast = showToast;
// 提供组合式 API
app.provide('toast', showToast);
}
};
// 导出插件和工具函数
export default ToastPlugin;
export const toast = showToast;
创建 src/Toast.vue 组件文件:
<template>
<div
class="vue-toast"
:class="[typeClass, positionClass]"
:style="{ opacity: visible ? 1 : 0, transform: visible ? 'translateY(0)' : transformValue }"
>
<div class="toast-content">
<slot>{{ message }}</slot>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const props = defineProps({
message: {
type: String,
default: ''
},
type: {
type: String,
default: 'info',
validator: (value) => {
return ['info', 'success', 'error', 'warning'].includes(value);
}
},
position: {
type: String,
default: 'top-center',
validator: (value) => {
return [
'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center',
'bottom-right'].includes(value);
}
}
});
const visible = ref(false);
// 计算样式类
const typeClass = `toast-${props.type}`;
const positionClass = `toast-${props.position}`;
// 计算初始位移
const transformValue = ref('translateY(-20px)');
onMounted(() => {
// 触发过渡动画
setTimeout(() => {
visible.value = true;
}, 10);
});
</script>
<style scoped>
.vue-toast {
position: fixed;
padding: 12px 20px;
border-radius: 4px;
color: #fff;
font-size: 14px;
transition: all 0.3s ease;
z-index: 9999;
max-width: 300px;
word-break: break-word;
}
/* 位置样式 */
.toast-top-left {
top: 20px;
left: 20px;
}
.toast-top-center {
top: 20px;
left: 50%;
transform: translateX(-50%);
}
.toast-top-right {
top: 20px;
right: 20px;
}
.toast-bottom-left {
bottom: 20px;
left: 20px;
}
.toast-bottom-center {
bottom: 20px;
left: 50%;
transform: translateX(-50%);
}
.toast-bottom-right {
bottom: 20px;
right: 20px;
}
/* 类型样式 */
.toast-info {
background-color: #4285f4;
}
.toast-success {
background-color: #0f9d58;
}
.toast-error {
background-color: #d93025;
}
.toast-warning {
background-color: #f4b400;
}
</style>
第三步:配置打包文件
创建 vite.config.js 配置文件:
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: path.resolve(__dirname, 'src/index.js'),
name: 'VueToastNotification',
// format 参数:由 Vite/Rollup 自动传递,根据你配置的打包格式变化,常见值包括:
// es:ES 模块格式(对应 module 字段)
// umd:通用模块定义格式(对应 main 字段)
// 这种命名方式让使用者和工具链能清晰识别不同模块格式的文件,
// 确保在各种环境下都能正确引入你的 Vue 插件。
fileName: (format) => `vue-toast-notification.${format}.js`
},
rollupOptions: {
// 确保外部化处理那些你不想打包进库的依赖
external: ['vue'],
output: {
// 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
globals: {
vue: 'Vue'
}
}
}
}
});
修改 package.json 文件,添加必要的字段:
{
"name": "vue-toast-notification",
"version": "1.0.0",
"description": "A simple toast notification plugin for Vue 3",
"main": "dist/vue-toast-notification.umd.js",
"module": "dist/vue-toast-notification.es.js",
"exports": {
".": {
"import": "./dist/vue-toast-notification.es.js",
"require": "./dist/vue-toast-notification.umd.js"
}
},
"files": [
"dist"
],
"scripts": {
"build": "vite build"
},
"keywords": [
"vue",
"vue3",
"toast",
"notification",
"plugin"
],
"author": "",
"license": "MIT",
"dependencies": {
"vue": "^3.3.4"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"vite": "^4.4.5"
}
}
- main定义包的默认入口文件,主要供使用 CommonJS 模块系统的环境(如 Node.js 或使用 require() 导入的工具)使用。这里指向 UMD 格式的文件(umd.js),因为 UMD 格式兼容 CommonJS 和 AMD 规范。
- module定义 ES 模块入口文件,供支持 ES 模块的工具(如 Webpack、Rollup、Vite 等)使用。工具会优先读取 module 字段,加载 ES 模块格式的文件(es.js),有利于 Tree-shaking 优化(删除未使用的代码)。
-
exports这是 ES 模块规范中更现代的导出定义方式,比 main 和 module 更灵活,可根据导入方式(import 或 require)自动匹配对应的文件:
当使用 import 导入时,使用 ES 模块格式(es.js)
当使用 require 导入时,使用 UMD 格式(umd.js)
这种配置能更精确地控制不同模块系统的加载行为,是对 main 和 module 的补充和增强。 - files字段用于指定 当包发布到 npm 时,需要包含哪些文件或目录,它是控制 npm 包体积和内容的重要配置。
第四步:打包并发布到 npm
- 首先打包插件:
npm run build
- 登录 npm(如果还没有账号,先在 npm 官网注册):
npm login
- 发布插件:
npm publish
