vue3 + vite自定义封装vue+audio ,并发布到npm

前言

在平时使用工具的时一直想着拥有自己的一套npm库,但是迟迟没有动静,这次项目中使用audio,想着封装一套自定义的audio小工具,下面我会为大家做详细介绍以及在途中遇到的坑

创建项目

可以使用两种方式创建vue3项目
1.第一种方式 vite
2.第二种方式创建 @vue/cli
相比之下我更喜欢使用vite创建(1.开发环境中,无需打包操作,可快速的冷启动2.轻量快速的热重载(HMR)3.真正的按需编译,不再等待整个应用编译完成),此处不过多介绍

  npm init vite@latest

创建步骤

  1. 提示我们安装是否需要create-vite@5.2.3依赖,选择y,可以快速创建一个vite项目
    在这里插入图片描述
  2. 起一个组件名称,然后选择vue
    在这里插入图片描述
  3. 这里我们选择javaScript,项目中没有使用TS,然后按回车
    在这里插入图片描述
  4. 安装成功
    在这里插入图片描述
  5. 项目中我们需要使用到element-plus,需要手动下载
npm install element-plus --save
  • –save 等同于 -S (常用,可保存在package.json文件中),
    -S, --save 安装包信息将加入到dependencies(生产阶段的依赖,也就是项目运行时的依赖,就是程序上线后仍然需要依赖)
  • –save-dev 等同于 -D
    -D, --save-dev 安装包信息将加入到devDependencies(开发阶段的依赖,就是我们在开发过程中需要的依赖,只在开发阶段起作用。)

创建组件

  1. 首先在项目中src目录下创建一个package文件夹
    在这里插入图片描述
  2. 在package文件夹下创建一个.vue文件(自定义组件名)
    在这里插入图片描述
  3. 这里就开始封装我们需要的组件

组件我不做介绍,不懂得爱豆请联系我

<template>
  <div>
    <audio @timeupdate="updateProgress"
           controls
           autoplay
           ref="audioRef"
           id="myaudio"
           style="display: none">
      <source :src="audioUrl"
              type="audio/mpeg" />
      您的浏览器不支持音频播放
    </audio>
    <div class="audio_right"
         :style="{background:GradientBg}">
      <img class="audio_icon"
           src="../assets/images/Fastrewind.png"
           @click="Fastrewind"
           alt="快退" />
      <span class="advanceRetreat">-{{backSecond}}s</span>
      <img v-if="audioIsPlay"
           @click="playAudio"
           class="audio_icon"
           src="../assets/images/play.png"
           alt="播放" />
      <img v-if="!audioIsPlay"
           @click="playAudio"
           class="audio_icon"
           src="../assets/images/pause.png"
           alt="暂停" />
      <span class="advanceRetreat">+{{forwardSecond}}s</span>
      <img class="audio_icon"
           src="../assets/images/fastForward.png"
           @click="fastForward"
           alt="快进" />
      <el-slider class="slider_box"
                 v-model="currentProgress"
                 :show-tooltip="false"
                 @input="handleProgressChange" />
      <div class="audio_time">
        <span class="audio_current">{{ audioStart }}</span>
        &nbsp;/&nbsp;
        <span class="audio_total">{{ durationTime }}</span>
      </div>
      <div class="volume">
        <div class="volume_progress"
             v-show="audioHuds">
          <el-slider vertical
                     height="100px"
                     class="volume_bar"
                     v-model="audioVolume"
                     :show-tooltip="false"
                     @change="handleAudioVolume" />
          <span class="Volume_Level"> {{ audioVolume}}%</span>
        </div>
        <img class="volume_icon"
             v-if="audioVolume <= 0"
             @click.stop="audioHuds = !audioHuds"
             src="../assets/images/audio_mute.png"
             alt="" />
        <img class="volume_icon"
             v-if="audioVolume > 0"
             @click.stop="audioHuds = !audioHuds"
             src="../assets/images/audio_high.png"
             alt="" />
      </div>
    </div>
  </div>
</template>
<script>
export default {
  //一定要写组件名,切记切记★★★
  name: 'l-vue3-audio'
}
</script>
<script setup name='l-vue3-audio'>
import { ref, onMounted, watch, onUnmounted, computed } from "vue";
const props = defineProps({
  audioUrl: String,        //试听的链接
  isPauseTtsAudio: Boolean,    //是否暂停播放试听
  color1: {                //背景颜色
    type: String,
    default: '#9cafe8'
  },
  color2: {                //背景颜色
    type: String,
    default: '#9cebb8'
  },
    isinit: {     //是否初始化       
    type: Boolean,
    default: false
  },
  backSecond: {
    type: Number,
    default: 3
  },
  forwardSecond: {
    type: Number,
    default: 10
  }
});

const audioIsPlay = ref(true); //音频是否在播放
const audioStart = ref("0:00");
const durationTime = ref("0:00"); //音频的总时长,显示的时间格式
const duration = ref(0); //音频的总时长
const audioVolume = ref(80); //音量的默认值是0.8
const audioHuds = ref(false); //是否显示音量slider
const audioRef = ref(null);
const currentProgress = ref(0);


watch(() => props.isPauseTtsAudio, (newVal, oldVal) => {
  console.log(newVal)
  if (newVal) {
    // 如果 isPauseTtsAudio 为 true,试听暂停
    handleCloseMusic();
  } else {
    setTimeout(() => {
      audioIsPlay.value = false;
      audioRef.value && audioRef.value.click() // 【主要代码 - 解决报错】先模拟与页面进行交互,防止报错
      audioRef.value && audioRef.value.play() // 播放音频
    }, 15)
    // audioRef.value.play();
  }
}, { deep: true, immediate: true });
watch(() => props.isinit, (newVal, oldVal) => {
  console.log(newVal)
  if (newVal) {
    closeRendering()
  }
}, { deep: true, immediate: true });
//动态背景颜色
const GradientBg = computed(() => {
  return `linear-gradient(to left, ${props.color1}, ${props.color2})`
});
// 暂停
function handleCloseMusic () {
  audioRef.value.pause();
  audioIsPlay.value = true;
}
//关闭重新渲染
function closeRendering () {
  nextTick(() => {
    audioRef.value.pause();
    audioIsPlay.value = true;
    audioRef.value.currentTime = 0;
  })
}
const handleGlobalClick = (event) => {
  if (!event.target.closest('.volume_icon')) {
    console.log('检测到全局点击');
    audioHuds.value = false
  }
};
onMounted(() => {
  calculateDuration();
  document.addEventListener('click', handleGlobalClick);
});
// 获取音频时长
function calculateDuration () {
  var myVid = audioRef.value;
  myVid.loop = false;
  myVid.src = props.audioUrl;
  // 监听音频播放完毕
  myVid.addEventListener(
    "ended",
    function () {
      audioIsPlay.value = true;
      currentProgress.value = 0;
    },
    false
  );
  if (myVid != null) {
    myVid.oncanplay = function () {
      duration.value = myVid.duration; // 计算音频时长
      durationTime.value = transTime(myVid.duration); //换算成时间格式
    };
    myVid.volume = 0.8; // 设置默认音量50%
    // 进入页面默认开始播放
    // audioRef.value.play();
    // audioIsPlay.value = false;
  }
}
// 音频播放时间换算
function transTime (duration) {
  const minutes = Math.floor(duration / 60);
  const seconds = Math.floor(duration % 60);
  const formattedMinutes = String(minutes).padStart(2, "0"); //padStart(2,"0") 使用0填充使字符串长度达到2
  const formattedSeconds = String(seconds).padStart(2, "0");
  return `${formattedMinutes}:${formattedSeconds}`;
}
// 播放暂停控制
function playAudio () {
  if (audioRef.value.paused) {
    audioRef.value.play();
    audioIsPlay.value = false;
  } else {
    audioRef.value.pause();
    audioIsPlay.value = true;
  }
}
// 根据当前播放时间,实时更新进度条
function updateProgress (e) {
  var value = e.target.currentTime / e.target.duration;
  if (audioRef.value.play) {
    currentProgress.value = value * 100;
    audioStart.value = transTime(audioRef.value.currentTime);
  }
}
//调整播放进度
const handleProgressChange = (val) => {
  console.log(val);
  if (!val) {
    return;
  }
  let currentTime = duration.value * (val / 100);
  // 更新音频的当前播放时间
  audioRef.value.currentTime = currentTime;
  audioRef.value.pause();
  audioIsPlay.value = true;
};
//调整音量
const handleAudioVolume = (val) => {
  audioRef.value.volume = val / 100;
};
//鼠标移除音量控件
// const handleMouseLeave = () => {
//   audioHuds.value = false
// }
//快退
const Fastrewind = () => {
  //当前时间
  if (audioRef.value.currentTime > props.backSecond) {
    audioRef.value.currentTime = audioRef.value.currentTime - props.backSecond
    audioRef.value.pause();
    audioIsPlay.value = true;
  }
}
//快进
const fastForward = () => {
  if (audioRef.value.duration - audioRef.value.currentTime > props.forwardSecond) {
    audioRef.value.currentTime = audioRef.value.currentTime + 10
    audioRef.value.pause();
    audioIsPlay.value = true;
  }
}
onUnmounted(() => {
  document.removeEventListener('click', handleGlobalClick);
});
</script>


<style lang="scss" scoped>
.audio_right {
  width: 300px;
  height: 40px;
  display: flex;
  align-items: center;
  // background: linear-gradient(to left, #9cafe8 0%, #9cebb8 100%);
  border-radius: 20px;
  padding: 0 10px;
  box-sizing: border-box;
  position: relative;
  .slider_box {
    width: 160px;
    height: 4px;
    border-radius: 5px;
    background-color: #f1f1f1;
    flex: 1;
    margin: 0 8px 4px;
  }
  .audio_icon {
    width: 20px;
    height: 20px;
    margin-bottom: 4px;
    cursor: pointer;
  }
  .audio_time {
    color: #f1f1f1;
    overflow: hidden;
    font-size: 12px;
    position: absolute;
    bottom: -1px;
    left: 110px;
    .audio_total {
      float: right;
    }
    .audio_current {
      float: left;
    }
  }
}
.volume {
  position: relative;
  .volume_progress {
    width: 32px;
    height: 140px;
    position: absolute;
    top: -146px;
    right: -4px;
  }
  .volume_bar {
    background: #276bf982;
    border-radius: 4px;
  }
  .volume_icon {
    width: 24px;
    height: 24px;
    cursor: pointer;
  }
}
</style>
<style lang="scss">
.el-slider__button-wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
}
.slider_box,
.volume_bar {
  .el-slider__button {
    width: 8px;
    height: 8px;
    border: none;
  }
  .el-slider__bar {
    background: #00db15;
  }
}
.slider_box {
  .el-slider__button-wrapper {
    width: 8px;
  }
}
.volume_bar {
  .el-slider__runway {
    margin: 0 14px !important;
  }
}
.Volume_Level {
  font-size: 12px;
  text-align: center;
  width: 100%;
  color: #fff;
  position: absolute;
  bottom: 1px;
}
.advanceRetreat {
  font-size: 12px;
  color: #ffffff;
  width: 20px;
  height: 20px;
  margin: 0 4px;
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>
  1. 封装的组件可以在app.vue中引用下是否能正常使用

导出组件

  1. 在src下创建index.js
import lVue3Audio from './package/laudio.vue' // 引入封装好的组件
export { lVue3Audio } //实现按需引入*
const components = [lVue3Audio] // 将来如果有其它组件,都可以写到这个数组里
//批量注册组件
const install = function (App, options) {
  components.forEach((item) => {
    console.log(item)//这里可以输出下组件是否有name,组件中的name至关重要
    App.component(item.name, item)
  })
}
export default { install } // 批量的引入*

这里因为前期组件中我没有写 name: ‘l-vue3-audio’,导致后续下载一直引入不进去
在这里插入图片描述
2. 使用vite构建
编辑vite.config.js文件,新增build属性 vite中文文档

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import viteCompression from 'vite-plugin-compression'//打包压缩
export default (mode, command) => {
  console.log('🚀 ~ command:', command)
  console.log('🚀 ~ mode:', mode)
  return defineConfig({
    plugins: [
      vue(),
      dts(),
      viteCompression({
        verbose: true,
        disable: false, // 不禁用压缩
        deleteOriginFile: false, // 压缩后是否删除原文件
        threshold: 10240, // 压缩前最小文件大小
        algorithm: 'gzip', // 压缩算法
        ext: '.gz', // 文件类型
      }),
    ],
    resolve: {
      alias: {
        '@': resolve(__dirname, 'src'),
      },
    },
    build: {
      minify: 'esbuild',//boolean | 'terser' | 'esbuild'//压缩方式
      sourcemap: true,//boolean | 'inline' | 'hidden' 默认: false;构建后是否生成 source map 文件,下面会说一下
      //库编译模式配置
      lib: {
        entry: resolve(__dirname, 'src/index.js'), //指定组件编译入口文件,是必须的因为库不能使用 HTML 作为入口
        name: 'l-vue3-audio',// 则是暴露的全局变量,在 formats 包含 'umd' 或 'iife' 时是必须的
        fileName: (format) => `l-vue3-audio.${format}.js`,//是输出的包文件名
      },
      // rollup打包配置
      rollupOptions: {
        // 确保外部化处理那些你不想打包进库的依赖
        external: ['vue'],
        output: {
          // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
          globals: {
            vue: 'Vue',
          },
        },
      },
    },
  })
}


上面所说的build.sourcemap
前端工程打包后代码会跟项目源码不一致,当代码运行出错时控制台上定位出错代码的位置跟项目源码上不对应。这时候我们很难定位错误代码的位置。SourceMap的用途是可以将转换后的代码映射回源码,如果设置了js文件对应的map资源,那么就可以在控制台进行调试时直接定位到源码位置。

boolean: true | false
默认为false,不生成map文件。当设置成true时,会生成单独的map文件,并且在对应的bundle文件中生成相应注释指明map文件。
在这里插入图片描述

build.minify 是指在构建(Build)过程中对代码进行压缩(Minify)的操作。在 Web 开发中,通常会将 JavaScript、CSS 和 HTML 文件进行压缩,以减小文件大小并提高加载速度。
代码压缩(Minification)是指去除代码中的多余空格、注释和换行符等,将代码压缩成一行或几行,以减小文件体积。这样做的好处包括:
减小文件大小:压缩代码可以显著减小文件大小,从而加快文件的下载速度。
提高加载速度:减小文件大小可以减少网络传输时间,加快网页加载速度。
减少带宽消耗:压缩代码可以减少服务器和客户端之间的数据传输量,节省带宽成本。
保护代码:压缩后的代码更难阅读和理解,可以一定程度上保护源代码不被轻易泄露。代码压缩(Minification)是指去除代码中的多余空格、注释和换行符等,将代码压缩成一行或几行,以减小文件体积。这样做的好处包括:
减小文件大小:压缩代码可以显著减小文件大小,从而加快文件的下载速度。
提高加载速度:减小文件大小可以减少网络传输时间,加快网页加载速度。
减少带宽消耗:压缩代码可以减少服务器和客户端之间的数据传输量,节省带宽成本。
保护代码:压缩后的代码更难阅读和理解,可以一定程度上保护源代码不被轻易泄露。
在这里插入图片描述


回到正滚,上面简单的了解一下vite的build属性

  1. 修改package.json文件
{
  "name": "l-vue3-audio", //包名字
  "private": true,  //是否私有
  "version": "0.0.1", //版本
  "type": "module",//type 字段用于指定模块的类型,module表示该模块遵循ECMAScript 模块规范,使用 import 和 export 进行模块导入和导出。
  "keyword": "vue3 audio 自定义组件",//在npm上可被搜索的关键字
  "description": "基于vue3的audio组件", //描述
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  //当 npm 包发布时,files 指定的文件会被推送到 npm 服务器中,如果指定的是文件夹,那么该文件夹下面所有的文件都会被提交。
  "files": [
    "dist"
  ],
  "main": "./dist/l-vue3-audio.umd.js", //入口文件
  //module 字段可以定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用。如果 npm 包导出的是 ESM 规范的包,使用 module 来定义入口文件。
  "module": "./dist/l-vue3-audio.es.js",
  "exports": {
    ".": {
      "import": "./dist/l-vue3-audio.es.js",
      "require": "./dist/l-vue3-audio.umd.js"
    }
  },
  "dependencies": {
    "element-plus": "^2.6.2",
    "vue": "^3.4.21"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.0.4",
    "sass": "^1.72.0",
    "vite": "^5.2.0",
    "vite-plugin-compression": "^0.5.1",
    "vite-plugin-dts": "^3.8.1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"//开源协议
}

打包

当我们都配置好以后,我们就要打包了,这是我们要上传得文件

  1. 打包,生成dist文件
npm run build

在这里插入图片描述
2. 注册npm账号 官网地址

  • 有些小伙伴可能本地的npm镜像源采用的是淘宝镜像源或者其它的,如果想要发布npm包,我们得吧我们得npm源切换为官方得源,命令如下:
npm config set registry=https://registry.npmjs.org
  1. 发布前准备
    在dist文件生成package.json文件,自定义组件名(唯一,重名报错重新起一个就行),版本号不能相同每次
    在dist根目录中运行
npm init -y //生成package.json
{
  "name": "l-vue3-audio",//打包后npm下载的名字
  "version": "0.0.8",//版本号,每次要保持不一致,删除指定版本后也不能在提交相同版本
  "description": "基于vue3的audio组件",
  "keyword": "vue3 audio 自定义组件",
  "main": "l-vue3-audio.es.js",//入口文件
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  //npm 所搜的关键词
  "keywords": [
    "vue",
    "vue3",
    "audio",
    "vue3audio"
  ],
  "author": "",
  "license": "ISC"//开源
}
  1. 添加npm用户
    在dist目录下,运行命令
npm adduser

添加npm账号得用户名和密码还有邮箱地址
在这里插入图片描述
按回车键
在这里插入图片描述
填写一次性密码后登陆
5. 执行发布命令

npm publish

在这里插入图片描述
已经上传成功

这里讲解一下几个上传失败的原因
在这里插入图片描述
已经有此版本请重新修改版本号,重新上传

  • 这里在讲个我途中遇见的问题,一个小插曲吧,挺闹心的,希望爱豆们不要遇见

在这里插入图片描述
登陆npm的时候一直登陆不上去,查找了半天原因是因为我开了代理,然后我就把代理关闭,还是不行,清除npm缓存npm cache clean --force,还是不可以,迫不得已重启了一下,好了,哎就是这么神奇吧,没有重启解决不了的,有了就多重启几遍,啊啊啊啊啊~~~~~~

终于成功啦✌✌✌
在这里插入图片描述
对了还有一个给爱豆避雷一下,在上传途中我打包上传了很多次,每次都叠加版本号,以至于越来越高,前面都还都不可用,无奈之下我就删除前面的版本号,npm unpublish l-vue3-audio@版本号 --force,后面想重新从第一个版本上传,发现一直报错,npm不允许上传已经删除的包以及版本号,因为每次上传都是会分配一个唯一值,所以删除就不可以在使用

使用组件

当我们要在项目中使用的时候就复制npm i l-vue3-audio
在这里插入图片描述
package.json文件中就有了我们安装的组件
在这里插入图片描述
这个时候只要像element-plus 那样引入就可以全局使用了,在main.js中引入

import lVue3Audio from 'l-vue3-audio'
import 'l-vue3-audio/style.css'
app.use(lVue3Audio)

在我们使用的页面使用即可
在这里插入图片描述

这里我们就全部结束啦,恭喜你又前进了一步,拥有了第一个自己npm包,愿我们在前进的道路上步步稳赢,加油爱豆们☛☀✈✈

稍微讲解一下这个插件的参数吧

ttsAudioUrlaudio 的 url
isPauseTtsAudio 是否暂停播放试听默认 true
forwardSecond 快进速度默认 10
backSecond 快退速度默认 3
color1背景渐变颜色从左往右1默认 #9cafe8
color2背景渐变颜色从左往右2默认 #9cebb8
isinit是否重置默认fasle

源码

  • 25
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要将Node.js + Express + Vue3 + Vite应用程序部署到阿里云上,您可以按照以下步骤进行操作: 1. 在阿里云上创建一个ECS实例,选择适合您需求的操作系统和配置。确保您在安全组中打开了HTTP(80端口)和HTTPS(443端口)的访问权限。 2. 在本地开发环境中构建Vue3应用程序。在项目根目录下打开终端或命令提示符,运行以下命令: ```bash npm run build ``` 这将使用Vite构建工具编译和打包Vue3应用程序,并将生成的静态文件保存在`dist`目录中。 3. 将Vue3应用程序的静态文件上传到阿里云ECS实例。您可以使用FTP工具(如FileZilla)将`dist`目录中的文件上传到ECS实例的`/var/www/html`目录(或您自定义的web目录)。 4. 在ECS实例上安装Node.jsnpm。您可以通过SSH远程连接到ECS实例并使用以下命令安装它们: ```bash # 安装Node.js curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - sudo apt-get install -y nodejs # 安装npm sudo apt-get install -y npm ``` 5. 在ECS实例上创建一个名为`server.js`的文件,并添加以下内容: ```javascript const express = require('express'); const path = require('path'); const app = express(); // 静态文件托管 app.use(express.static(path.join(__dirname, 'html'))); // 所有路由重定向到index.html app.get('*', (req, res) => { res.sendFile(path.join(__dirname, 'html', 'index.html')); }); // 启动服务器 const port = process.env.PORT || 80; app.listen(port, () => { console.log(`Server is running on port ${port}`); }); ``` 在上述示例中,我们使用Express创建了一个简单的服务器。我们通过`express.static`中间件来托管上传的Vue3应用程序的静态文件,然后将所有路由重定向到`index.html`以支持Vue的单页应用。最后,我们使用`app.listen`方法来启动服务器,并指定端口号为80。 6. 在ECS实例上运行以下命令来安装Express和其他项目依赖: ```bash npm install express ``` 7. 在ECS实例上运行以下命令启动Express服务器: ```bash node server.js ``` 这将在ECS实例上启动Express服务器,并监听端口80。 8. 现在,您的Node.js + Express + Vue3 + Vite应用程序已经在阿里云ECS实例上运行起来了。您可以在浏览器中访问ECS实例的公网IP地址来查看应用程序。 请注意,上述示例仅提供了一个简单的部署流程,并且假设您已经完成了阿里云ECS实例的设置和配置。在实际部署过程中,您可能还需要考虑安全性、性能优化、域名绑定等方面的问题。另外,您可能还需要使用Nginx等工具来配置反向代理或实现负载均衡等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值