H5: 使用Web Audio API播放音乐
简介
记录关于自己使用 Web Audio API 的 AudioContext 播放音乐的知识点。
需求分析

1.列表展示音乐;
2.上/下一首、播放/暂停/续播;
3.播放模式切换:循环播放、单曲循环、随机播放;
4.播放状态显示:当前播放的音乐名、播放时间、总时间、进度条效果;
5.播放控制器显示在底部区域;
6.支持音量调节;
7.浏览器隐藏、显示的交互后,也能正常有效播放(播放、声音)。
注意
安卓与IOS上有不同的兼容性,所以采用了 Web Audio API 的 AudioContext ,兼容性强大(但是截止写文章前,IOS17+版本不支持,没有声音)。
稍微复杂点点的逻辑就是AudioContext与手机系统的关联,可以看看 AudioContext: createMediaElementSource。

具体实现
test/music/musicPlayer/musics.ts
test/music/musicPlayer/useMusicPlayer.ts
test/music/index.vue
1.test/music/musicPlayer/musics.ts
interface musicItem {title: stringsrc: stringtime: stringmp3Name: string
}
const musicList: musicItem[] = [{title: 'How to Love',src: '',time: '03:39',mp3Name: 'sx_music_HowtoLove_CashCash'},{title: '空空如也',src: '',time: '03:34',mp3Name: 'sx_music_kongkongruye'},{title: '2 Soon',src: '',time: '03:19',mp3Name: 'sx_music_Soon_JonYoung'},{title: '孤勇者',src: '',time: '04:16',mp3Name: 'sx_music_guyongzhe'},{ title: '秒针', src: '', time: '02:58', mp3Name: 'sx_music_miaozhen' },{title: '热爱105˚的你',src: '',time: '03:15',mp3Name: 'sx_music_reai105dudeni'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'},{title: '她会魔法吧',src: '',time: '03:01',mp3Name: 'sx_music_tahuimofaba'}
] // 音乐列表信息export { type musicItem, musicList }
2.test/music/musicPlayer/useMusicPlayer.ts
import { ref, nextTick } from 'vue'
import { musicList } from './musics'enum PlayMode {REPEAT, // 循环播放SINGLE_CYCLE, // 单曲循环RANDOM // 随机播放
}const musicPlayer = ref<HTMLAudioElement | null>()
const musicPlayingIndex = ref(-1) // 播放的音乐的下标
const musicIsPlaying = ref(false) // 是否播放中
const currentTime = ref(0) // 正在播放的音乐时间点
const musicPlayMode = ref(PlayMode.REPEAT) // 播放模式
const progressInterval = 500 // 计时器触发的频率
let defaultVolume = 1 // 音量 0-1
let timer: NodeJS.Timer | null = null // 计时器 ---此处需要在 .eslintrc.js/.cjs 文件中配置 globals: { NodeJS: true }
let source: MediaElementAudioSourceNode | null = null
let audioCtx: AudioContext | null = null
let gainNode: GainNode | null = null
let audioContextAttr: string | null = null
if ('AudioContext' in window) {audioContextAttr = 'AudioContext'
} else if ('webkitAudioContext' in window) {audioContextAttr = 'webkitAudioContext'
}const useMusicPlayer = () => {const _getMusicFile = (mp3Name: string) => {// 此处需要相对路径// vite项目// return new URL(`../../../assets/music/${mp3Name}.mp3`, import.meta.url).href// webpack项目return require(`../../../assets/music/${mp3Name}.mp3`)}/** 设置:音量百分比 0-100 变为 0-1* @param v number 0-100*/const _saveDefaultVolume = (v: number) => {let num = vif (v < 0) {num = 0} else if (v > 100) {num = 100}defaultVolume = num / 100return defaultVolume}/*** 计时器:回调-更新显示-MP3的播放时间*/const _intervalUpdatePlayTime = () => {const player = musicPlayer.valueif (!player) returncurrentTime.value = player.currentTime}/*** 计时器:清除*/const _clearTimer = () => {if (!timer) returnclearInterval(timer)timer = null}/*** 计时器:绑定&开始*/const _startTimer = () => {_clearTimer()timer = setInterval(_intervalUpdatePlayTime, progressInterval)}/*** 方法:取两个值之间的随机数*/const _random = (min = 0, max = 100) => Math.floor(Math.random() * (max - min + 1)) + min/*** 销毁:断开audio与AudioContext之间的链接*/const _destroyConnect = () => {if (source) {source.disconnect()}if (gainNode) {gainNode.disconnect()}if (audioCtx) {audioCtx.close()}source = nullgainNode = nullaudioCtx = nullmusicPlayer.value = null}/*** 音乐:初始化audio与AudioContext的绑定* 目的是为了 IOS 上能调整音量*/const _init = () => {if (!audioContextAttr) return// 先暂停已有的播放pause()// 对已创建的绑定关系进行解绑_destroyConnect()// 若在body中找得到对应的dom,则进行移除const findDom = document.getElementById('musicPlayerAudio') as HTMLAudioElementif (findDom) {findDom.remove()}// 创建audio,加入body中const dom = document.createElement('audio')dom.id = 'musicPlayerAudio'document.body.appendChild(dom)// 给audio绑定播放结束的回调函数dom.onended = onAudioEnded// 创建AudioContext、source、gainNode,进行关联(便于IOS控制音量)const UseAudioContext = (window as any)[audioContextAttr]audioCtx = new UseAudioContext()if (!audioCtx) returnsource = audioCtx.createMediaElementSource(dom)gainNode = audioCtx.createGain()source.connect(gainNode)gainNode.connect(audioCtx.destination)// 设置音量if (defaultVolume === 0) {dom.muted = true} else {dom.muted = false}gainNode.gain.value = defaultVolume// 存储dom,便于后续访问audio对应的属性musicPlayer.value = dom// 若播放控制器的状态未启动,则启动if (audioCtx && audioCtx.state === 'suspended') {audioCtx.resume()}}/*** 音乐:播放器-音量调整*/const setVolume = (volume: number) => {const v = _saveDefaultVolume(volume)const player = musicPlayer.valueif (!player) returnif (v === 0) {player.muted = true} else {player.muted = false}if (!gainNode || !gainNode.gain) returngainNode.gain.value = v}/*** 音乐:播放器-暂停*/const pause = () => {const player = musicPlayer.valueif (!musicIsPlaying.value || !player) {return}musicIsPlaying.value = falseplayer.pause()_clearTimer()}/*** 音乐:播放器-播放*/const playByLast = () => {const player = musicPlayer.valueif (!player || !player.src) returnif (audioCtx && audioCtx.state === 'suspended') {audioCtx.resume()}nextTick(() => {// play触发时,会先自动加载资源player.play().then(() => {musicIsPlaying.value = true_startTimer()})})}/*** 音乐:播放器-播放-通过下标*/const playByIndex = (index: number) => {if (index < 0 || index + 1 > musicList.length) {return}musicIsPlaying.value = false// 重新初始化,便于释放上一个播放器所占用的内存_init()const player = musicPlayer.valueif (!player) {return}// 重置当前播放了的时长currentTime.value = 0// 更新要播放的下标musicPlayingIndex.value = indexif (!musicList[index].src) {// 若资源路径不存在,则进行对应的路径引入musicList[index].src = _getMusicFile(musicList[index].mp3Name)}if (!musicList[index].src) {console.error('find music file failed')return}player.src = musicList[index].srcplayByLast()}/*** 音乐:随机播放*/const randomPlay = () => {const index = _random(0, musicList.length - 1)playByIndex(index)}/*** 音乐:播放器-下一首*/const playNext = () => {if (musicPlayMode.value === PlayMode.RANDOM) {randomPlay()} else {const index: number =musicPlayingIndex.value + 1 === musicList.length ? 0 : musicPlayingIndex.value + 1playByIndex(index)}}/*** 音乐:播放器-上一首*/const playPrev = () => {if (musicPlayMode.value === PlayMode.RANDOM) {randomPlay()} else {const index: number =musicPlayingIndex.value < 1 ? musicList.length - 1 : musicPlayingIndex.value - 1playByIndex(index)}}/*** 回调:播放结束后,下一首播放什么*/const onAudioEnded = () => {switch (musicPlayMode.value) {case PlayMode.REPEAT:playNext()breakcase PlayMode.SINGLE_CYCLE:playByIndex(musicPlayingIndex.value)breakcase PlayMode.RANDOM:randomPlay()breakdefault:break}return true}/** 自动播放音乐 */const startPlayInRoom = () => {// 用户第一次点击时,自动播放音乐const initMusicAutoPlayOnReload = () => {document.removeEventListener('click', initMusicAutoPlayOnReload, true)playByIndex(0)}document.addEventListener('click', initMusicAutoPlayOnReload, true)}return {musicList,musicPlayer,musicPlayingIndex,musicIsPlaying,currentTime,musicPlayMode,setVolume,pause,playByIndex,playByLast,playPrev,playNext,_clearTimer,startPlayInRoom}
}export { PlayMode, useMusicPlayer }
3.test/music/index.vue
<template><div class="music-box"><!-- 音乐列表 --><div class="music-list"><divv-for="(music, index) in musicList":key="index"class="music-item":class="{ 'music-item-active': musicPlayer.musicPlayingIndex.value === index }"@click.stop="switchAudio(index)"><div class="item-left"><div class="item-left-title">{{ music.title }}</div><svgv-if="musicPlayer.musicPlayingIndex.value === index && musicPlayer.musicIsPlaying.value"id="equalizer"width="13px"height="11px"viewBox="0 0 10 7"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#3994f9"><rectid="bar1"transform="translate(0.500000, 6.000000) rotate(180.000000) translate(-0.500000, -6.000000) "x="0"y="5"width="1"height="2px"></rect><rectid="bar2"transform="translate(3.500000, 4.500000) rotate(180.000000) translate(-3.500000, -4.500000) "x="3"y="2"width="1"height="5"></rect><rectid="bar3"transform="translate(6.500000, 3.500000) rotate(180.000000) translate(-6.500000, -3.500000) "x="6"y="0"width="1"height="7"></rect></g></svg><svgv-else-if="musicPlayer.musicPlayingIndex.value === index"id="equalizer"width="13px"height="11px"viewBox="0 0 10 7"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g fill="#3994f9"><rect x="0" y="5" width="1" height="2px"></rect><rect x="3" y="2" width="1" height="5"></rect><rect x="6" y="0" width="1" height="7"></rect></g></svg></div><div class="item-right">{{ music.time }}</div></div></div><!-- 播放控制 --><div class="music-control"><div class="control-content"><div class="control-content-left"><divclass="music-btn prev"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="prev"/><div:class="['music-btn', musicPlayer.musicIsPlaying.value ? 'pause' : 'play']"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="togglePlayer"/><divclass="music-btn next"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="next"/></div><div class="control-content-center"><div class="center-title">{{ currentMusicTitle || '-' }}</div><div ref="audioProgressWrap" class="center-progress-wrap"><div ref="audioProgress" class="center-progress-wrap-active" /></div><div class="center-time"><div class="center-time-now">{{ formatSecond(musicPlayer.currentTime.value) }}</div><div class="center-time-total">{{ currentMusicTotalTimeStr }}</div></div></div><div class="control-content-right"><divv-if="musicPlayer.musicPlayMode.value === PlayMode.REPEAT"class="music-btn playRepeat"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="nextPlayMode"/><divv-if="musicPlayer.musicPlayMode.value === PlayMode.SINGLE_CYCLE"class="music-btn singleCycle"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="nextPlayMode"/><divv-if="musicPlayer.musicPlayMode.value === PlayMode.RANDOM"class="music-btn playRandom"@touchstart.passive="onTouchEvent"@touchend.passive="onTouchEvent"@click="nextPlayMode"/></div></div></div></div>
</template><script setup lang="ts">import { ref, computed, watch } from 'vue'import { PlayMode, useMusicPlayer } from './musicPlayer/useMusicPlayer'import { musicList } from './musicPlayer/musics'const systemSoundMode = ref(true) // 该变量应该在store中,便于设置页面控制全局声音的开启与否const musicPlayer = useMusicPlayer()const audioProgressWrap = ref()const audioProgress = ref()/** 当前播放的音乐名 */const currentMusicTitle = computed(() =>musicPlayer.musicPlayingIndex.value + 1 > 0? musicList[musicPlayer.musicPlayingIndex.value].title: '')/** 当前播放的音乐总时间 */const currentMusicTotalTimeStr = computed(() =>musicPlayer.musicPlayingIndex.value + 1 > 0? musicList[musicPlayer.musicPlayingIndex.value].time: '00:00')/** 操作:切换播放模式 */const nextPlayMode = () => {musicPlayer.musicPlayMode.value = (musicPlayer.musicPlayMode.value + 1) % 3switch (musicPlayer.musicPlayMode.value) {case PlayMode.REPEAT:console.log('循环播放')breakcase PlayMode.RANDOM:console.log('随机播放')breakcase PlayMode.SINGLE_CYCLE:console.log('单曲循环')breakdefault:break}}/** 事件:当点击按钮时的过渡效果 */const onTouchEvent = (event: Event) => {const tg = event.currentTarget as HTMLElementif (!tg) returnif (event.type === 'touchstart') {tg.classList.add('touch')}if (event.type === 'touchend') {tg.classList.remove('touch')}}/** 格式化:秒数=>ss:mm */const formatSecond = (second: number) => {let hourStr = `${Math.floor(second / 60)}`let secondStr = `${Math.ceil(second % 60)}`if (hourStr.length === 1) {hourStr = `0${hourStr}`}if (secondStr.length === 1) {secondStr = `0${secondStr}`}return `${hourStr}:${secondStr}`}/** 操作:播放所选音乐 */const switchAudio = (index: number) => {const player = musicPlayer.musicPlayer.valueif (!systemSoundMode.value) {window.alert('所有声音已关闭')}if (player?.src && player?.src.includes(musicList[index].mp3Name)) {if (musicPlayer.musicIsPlaying.value) {return}musicPlayer.playByLast()} else {musicPlayer.playByIndex(index)}}/** 操作:上一首 */const prev = () => {if (!systemSoundMode.value) {window.alert('所有声音已关闭')}musicPlayer.playPrev()}/** 操作:下一首 */const next = () => {if (!systemSoundMode.value) {window.alert('所有声音已关闭')}musicPlayer.playNext()}/** 操作:播放/暂停 */const togglePlayer = () => {const player = musicPlayer.musicPlayer.valueif (!systemSoundMode.value) {window.alert('所有声音已关闭')}if (musicPlayer.musicIsPlaying.value && player?.src) {// 正在播放,则暂停musicPlayer.pause()} else if (!player?.src) {// 未开始播放,则播放第一首musicPlayer.playByIndex(0)} else {// 暂停了,则继续播放刚才的musicPlayer.playByLast()}}/** 监听:当前播放中的音乐的进度时间=>进度条变化 */watch(() => musicPlayer.currentTime.value,() => {const player = musicPlayer.musicPlayer.valueif (!audioProgressWrap.value || !audioProgress.value || !player) {return}const offsetLeft =(player.currentTime / player.duration) * audioProgressWrap.value.offsetWidthaudioProgress.value.style.width = `${offsetLeft}px`})
</script><style lang="less" scoped>@bottomHeight: 97px;@controlHeight: 63px;@controlBottom: 34px;.music-box {width: 100%;height: 100%;background-color: #141624;position: relative;display: flex;flex-direction: column;}.music-list {flex: 1;overflow-y: auto;scrollbar-width: none;-ms-overflow-style: none;&::-webkit-scrollbar {display: none;}.music-item:nth-of-type(1) {margin-top: 7px;}.music-item {padding: 12px 20px 19px;display: flex;align-items: center;justify-content: space-between;font-size: 14px;font-weight: 500;line-height: 120%;color: #8f9095;.item-left {display: flex;align-items: center;.item-left-title {height: 17px;margin-right: 10px;}#equalizer {position: relative;}#bar1 {animation: bar1 1.2s infinite linear;}#bar2 {animation: bar2 0.8s infinite linear;}#bar3 {animation: bar3 1s infinite linear;}#bar4 {animation: bar4 0.7s infinite linear;}@keyframes bar1 {0% {height: 2px;}50% {height: 7px;}100% {height: 2px;}}@keyframes bar2 {0% {height: 5px;}40% {height: 1px;}80% {height: 7px;}100% {height: 5px;}}@keyframes bar3 {0% {height: 7px;}50% {height: 0;}100% {height: 7px;}}@keyframes bar4 {0% {height: 2px;}50% {height: 7px;}100% {height: 2px;}}}}.music-item-active {.item-left {.item-left-title {color: #3994f9;}}.item-right {color: #3994f9;}}}.music-control {height: @bottomHeight;padding: 0 10px;background-color: #141624;.control-content {height: @controlHeight;border-radius: 7px;background-color: #1b1d2a;display: flex;align-items: center;justify-content: space-between;.control-content-left {display: flex;align-items: center;.prev,.pause,.play {margin-right: 10px;}.next {margin-right: 17px;}}.control-content-center {margin-top: 1px;flex: 1;.center-title {margin-bottom: 5px;line-height: 120%;font-size: 13px;font-weight: 500;color: #fff;}.center-progress-wrap {width: 100%;height: 2px;background-color: #3e404e;.center-progress-wrap-active {width: 0;height: 100%;background-color: #3994f9;}}.center-time {height: 50%;margin-top: 10px;display: flex;justify-content: space-between;align-items: center;.center-time-now,.center-time-total {font-size: 10px;font-weight: 400;line-height: 12px;color: #3994f9;}.center-time-total {color: #8f9095;}}}.control-content-right {padding-left: 10px;}.music-btn {width: 33px;height: 33px;&.prev {background: url('../../assets/images/music/music-prev.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-prev-touch.png');}}&.play {background: url('../../assets/images/music/music-play.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-play-touch.png');}}&.pause {background: url('../../assets/images/music/music-pause.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-pause-touch.png');}}&.next {background: url('../../assets/images/music/music-next.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-next-touch.png');}}&.playRepeat {background: url('../../assets/images/music/music-repeat.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-repeat-touch.png');}}&.singleCycle {background: url('../../assets/images/music/music-single-cycle.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-single-cycle-touch.png');}}&.playRandom {background: url('../../assets/images/music/music-random.png');background-size: 100%;background-repeat: no-repeat;&.touch {background: url('../../assets/images/music/music-random-touch.png');}}}}}
</style>
最后
觉得有用的朋友请用你的金手指点一下赞,或者评论留言一起探讨技术!
相关文章:
H5: 使用Web Audio API播放音乐
简介 记录关于自己使用 Web Audio API 的 AudioContext 播放音乐的知识点。 需求分析 1.列表展示音乐; 2.上/下一首、播放/暂停/续播; 3.播放模式切换:循环播放、单曲循环、随机播放; 4.播放状态显示:当前播放的音乐…...
Parasoft C/C++test:汽车网络安全ISO 21434最佳实践
为什么汽车网络安全很重要Why Automotive Cybersecurity Is Important 许多汽车公司向电子道路车辆的转变从根本上改变了整个行业,提高了汽车的互联性和智能性。随着电子汽车变得更加互联和智能,它们也越来越依赖软件来实现车辆操作,驱动更多…...
如何卸载干净 IDEA(图文讲解)windows和Mac教程
大家好,我是sun~ 很多小伙伴会问 Windows / Mac 系统上要怎么彻底卸载 IDEA 呢? 本文通过图片文字,详细讲解具体步骤: 如何卸载干净 IDEA(图文讲解) Windows1、卸载 IDEA 程序2、注册表清理3、残留清理 M…...
Docker搭建Gitlab
拉取镜像:docker pull gitlab/gitlab-ce创建映射目录: mkdir -p /usr/local/gitlab/config mkdir -p /usr/local/gitlab/data mkdir -p /usr/local/gitlab/logs运行容器: docker run -d -p 443:443 -p 8000:8000 -p 222:22 --name gitlab …...
STM32F4X SDIO(四) SDIO控制器
STM32F4X SDIO(四) SDIO控制器 STM32F4X SDIO控制器SDIO控制器框图SDIO控制器时钟适配器寄存器FIFO控制单元命令路径数据路径 SDIO寄存器SDIO控制相关寄存器SDIO电源控制寄存器 (SDIO_POWER)SDIO时钟控制寄存器 (SDIO_CLKCR)SDIO_CK相位 SDIO命令响应相关…...
【flink】Task 故障恢复详解以及各重启策略适用场景说明
文章目录 一. 重启策略种类(Restart Strategies)1. Fixed Delay Restart Strategy2. Failure Rate Restart Strategy3. Fallback Restart Strategy4. No Restart Strategy 二. 故障恢复策略(Failover Strategies)1. (全…...
一个计算机高手的成长3
这是转在茶余的帖子。文中绝大部分技术术语我不懂,所以无资格评论他的技术价值。但文章强烈的逻辑说服力,和通篇流露的进取精神,使我觉得这是篇有价值的帖子,至少值得一读。 就像我开始从MIS转到通信一样,我看过大量通…...
2023应届生能力考试含解析(Java后端开发)——(1)
1.以下代码的循环次数是 ( ) public class Test {public static void main(String[] args) {int i 7;do {System.out.println(--i);--i;} while (i ! 0);System.out.println(i);} } A 0 B 1 C 7 D 无限次 这段代码会导致无限循环的原因是在 do-while 循环中&#…...
Ansible中的任务执行控制
循环 简单循环 {{item}} 迭代变量名称 loop: - value1 - value2 - ... //赋值列表{{item}} //迭代变量名称循环散列或字典列表 - name: create filehosts: host1tasks:- name: file moudleservice:name: "{{ item.name }}"state: "{{…...
利用maven的dependency插件分析工程的依赖
dependency:analyze https://maven.apache.org/plugins/maven-dependency-plugin/analyze-mojo.html 分析项目的依赖,确定哪些:用了并且声明了、用了但没有声明、没有使用但声明了。 dependency:analyze可以单独使用,所以它总是会执行test-…...
【广州华锐互动】VR野外求生技能学习,让你感受真实的冒险之旅!
随着科技的迅速发展,虚拟现实(VR)技术为人们提供了一个全新的、身临其境的探险体验。通过将用户带入一个仿真的、沉浸式的虚拟环境,VR互动体验让人们在安全的氛围中感受到野外探险的乐趣。本文将从视觉呈现、沉浸式体验、交互性和应用范围四个方面&#…...
k8s、调度约束
Kubernetes 是通过 List-Watch **** 的机制进行每个组件的协作,保持数据同步的,每个组件之间的设计实现了解耦 用户是通过 kubectl 根据配置文件,向 APIServer 发送命令,在 Node 节点上面建立 Pod 和 Container。 APIS…...
Redis的介绍,以及Redis的安装(本机windows版,虚拟机Linux版)和Redis常用命令的介绍
目录 一. Redis简介 二. Redis的安装 2.1 Linux版安装 2.2 windows版安装 三. Redis的常用命令 一. Redis简介 Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。 它…...
电子器件 MOS管的参数、选型与使用技巧
一、电路符号 MOS管分为 G(栅极)、S(源极)、D(漏极) 三极,在图中 S 极有两条线,D 极只有一条线。 1.1 NMOS 和 PMOS 下图中,左侧是 PMOS,右侧是 NMOS。箭头…...
EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析
EtherCAT主站SOEM -- 2 -- SOEM之ethercatbase.h/c文件解析 一 ethercatbase.h/c文件功能预览:二 ethercatbase.h/c 文件的主要函数的作用:2.1 ecx_writedatagramdata:2.2 ecx_setupdatagram:2.3 ecx_adddatagram:2.4 …...
Spring集成高性能队列Disruptor
Disruptor简介 Disruptor(中文翻译为“破坏者”或“颠覆者”)是一种高性能、低延迟的并发编程框架,最初由LMAX Exchange开发。它的主要目标是解决在金融交易系统等需要高吞吐量和低延迟的应用中的并发问题。 Disruptor特点 无锁并发&#x…...
C++——类和对象(中)完结
赋值运算符重载 运算符重载 C 为了增强代码的可读性引入了运算符重载 , 运算符重载是具有特殊函数名的函数 ,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 函数名字为:关键…...
Sqoop的安装和使用
目录 一.安装 二.导入 1.全量导入 一.MySQL导入HDFS 二.MySQL导入Hive 2.增量导入 一.过滤导入hdfs/hive 二.导出 一.安装 1.下载地址:sqoop下载地址 2.解压 tar -zxvf ./sqoop-1.4.7.bin__hadoop-2.6.0.tar.gz -C ../module/ 3.改名和配置归属权限 #改名…...
java毕业设计基于springboot+vue的村委会管理系统
项目介绍 采用JAVA语言,结合SpringBoot框架与Vue框架以及MYSQL数据库设计并实现的。本村委会管理系统主要包括个人中心、村民管理、村委会管理、村民信息管理、土地变更管理、农业补贴管理、党员信息管理等多个模块。它帮助村委会管理实现了信息化、网络化…...
【C++】多态 ⑪ ( 纯虚函数和抽象类 | 纯虚函数语法 | 抽象类和实现 | 代码示例 )
文章目录 一、纯虚函数和抽象类1、纯虚函数2、纯虚函数语法3、抽象类和实现 二、完整代码示例 一、纯虚函数和抽象类 1、纯虚函数 纯虚函数 : 在 C 语言中 , " 纯虚函数 " 是 特殊类型的 虚函数 , " 纯虚函数 " 在 父类 中 声明 , 但是没有实现 ; 抽象类 …...
未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
