注释很详细,直接上代码
温馨提示:本博客建立在ElementPlus组件库
、pinia
及其持久化插件
已安装的前提下,前两者很容易找到相应博客,后者可能会踩坑,此处附上另一篇博客
vue3+vite+ts+pinia实现持久化存储
项目结构:
源码:
other.ts
import {ref,computed} from "vue"; import {defineStore} from "pinia"; export const useOtherStore = defineStore( "other", () => { let useLessMess=ref('ggggg'); let doubleMess = computed(() => { return useLessMess.value+useLessMess.value; }) let getDoubleMess=()=>{ return doubleMess.value; } return {getDoubleMess} } )
复制
video.ts
import { ref, computed } from "vue"; import { defineStore } from "pinia"; import {useOtherStore} from "./other"; import axios from "axios"; export const useVideoStore = defineStore( "video", () => { /** * 当前播放视频索引 */ let videoIndex = ref(0); /** * 当前播放视频地址 */ let videoUrl = computed(() => { return videoUrlList.value[videoIndex.value]; }); /** * 视频地址列表 */ let videoUrlList = ref<string[]>([]); /** * 上一曲按钮是否可用 */ let isPreviousButtonShow = computed(() => { return !(videoIndex.value == 0); }); /** * 下一曲按钮是否正在加载中 */ let isNextButtonLoading = ref(false); /** * 初始化 * @returns {void} */ function init() { //如果没有视频则获取视频 if (videoUrlList.value.length == 0) { getVideoJson(false); } } /** * 获取视频Json * @param {boolean} [op=true] -是否是初始化以外的操作 * @returns {void} */ function getVideoJson(op: boolean = true) { axios .get("http://api.treason.cn/API/nan.php") .then(function (response) { const data = response.data; //判断是否重复 if (op && videoUrlList.value.find((item) => item == data)) { console.log("重复"); getVideoJson(); } else { //去掉最后两个字符 const url = data.substring(0, data.length - 2); videoUrlList.value.push(url); } }) .catch(function (error) { console.log(error); }); } /** * 点击上一曲按钮 */ function previousVideo() { if (videoIndex.value > 0) { videoIndex.value--; } } /** * 点击下一曲按钮 */ async function nextVideo() { if (videoIndex.value < videoUrlList.value.length - 1) { videoIndex.value++; } else { isNextButtonLoading.value = true; await getVideoJson(); isNextButtonLoading.value = false; videoIndex.value++; } } /** * 获取其他模块的数据 */ function getOtherData() { return useOtherStore().getDoubleMess(); } return { /** * 当前播放视频索引 * @type {number} */ videoIndex, /** * 当前播放视频地址 * @type {string} */ videoUrl, /** * 视频地址列表 * @type {Array<string>} */ videoUrlList, /** * 上一曲按钮是否可用 * @type {boolean} */ isPreviousButtonShow, /** * 下一曲按钮是否正在加载中 * @type {boolean} */ isNextButtonLoading, /** * 初始化 * @returns {void} */ init, /** * 点击上一曲按钮 * @returns {void} */ previousVideo, /** * 点击下一曲按钮 * @returns {void} */ nextVideo, /** * 获取其他模块的数据 * @returns {string} */ getOtherData }; }, { persist:{ key:"videoData",//可以指定存储的key(即存入的名字) storage:sessionStorage,//可以指定存储的存储方式:1. sessionStorage(会话储存,临时) 2. localStorage(本地储存,永久) // paths:["videoUrlList"]//可以指定需要存储的内容(计算属性别存啊,存了也无效的) } } );
复制
App.vue
<script setup lang="ts"> import { ref, onMounted } from "vue"; import { useVideoStore } from "@/stores/video"; import { storeToRefs } from "pinia"; const videoStore = useVideoStore(); const { videoUrl, isPreviousButtonShow, isNextButtonLoading } = storeToRefs(videoStore); const { init, previousVideo, nextVideo ,getOtherData} = videoStore; /** * @type {boolean} -是否开启自动播放 */ let isAuto = ref(false); /** * 视频是否播放到结尾 */ let isVideoEnd = ref(false); /** * 视频播放到结尾 */ function videoEndSlove() { isVideoEnd.value = true; if (isAuto.value) { setTimeout(() => { nextVideo(); }, 1000) } } /** * 切换自动连播状态 */ function switchSlove() { if (isVideoEnd.value && isAuto.value) { nextVideo(); } } /** * 批量更新 */ function usePatchJust(){ //如果只是单个变量直接修改就行,当然得注意别修改const类型的地址 videoStore.$patch(//没有改变内容,示范而已 (state) => { state.isNextButtonLoading = state.isNextButtonLoading; state.videoUrlList = state.videoUrlList; state.videoIndex = state.videoIndex; } ) } init(); //初始化一下 usePatchJust();//批量更新 console.log(getOtherData());//获取其他数据并输出 </script> <template> <el-card class="el-card-class"> <template #header> <div class="header-class"> <div>懒大人演唱会</div> <div> <el-switch @change="switchSlove" v-model="isAuto" active-text="自动播放"></el-switch> </div> </div> </template> <template #default> <div> <video @play="isVideoEnd = false" @ended="videoEndSlove" controls="true" autoplay :src="videoUrl" class="video-class"></video> </div> </template> <template #footer> <el-button type="primary" size="large" @click="previousVideo" :disabled="!isPreviousButtonShow">上一首</el-button> <el-button type="primary" size="large" @click="nextVideo" :loading="isNextButtonLoading">下一首</el-button></template> </el-card> </template> <style scoped> .el-card-class { width: 1320px; height: 910px; } .header-class { display: flex; justify-content: space-between; } .video-class { width: 100%; max-width: 100%; /* 最大宽度 */ height: 720px; object-fit: contain; /* 控制视频如何适应其框架 */ border-radius: 5px; } </style>
复制
main.ts
import './assets/main.css' import { createApp } from 'vue' import { createPinia } from 'pinia' // 导入插件 import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' import router from './router' const app = createApp(App) // 创建pinia,别创建多个了,看清楚昂 const pinia = createPinia() // 使用pinia插件,插件是加在pinia实例上的而不是app,注意注意 pinia.use(piniaPluginPersistedstate) app.use(pinia)//这步不能忘哇 app.use(router) app.use(ElementPlus) app.mount('#app')
复制
效果演示