Skip to content

音乐播放器保持播放的经典问题 - $at.vm 的开发问题讨论导向 #1

@ZoMaii

Description

@ZoMaii

音乐播放器保持播放的经典问题 - $at.vm 的开发问题讨论

在 VM 的设计定义中,是主要使用 new Worker() 的方式进行最小化隔离。但整个 $at() 是面向单页面单路由应用设计的,因此在这里以 Web 音乐播放器 为例子,进行的一个讨论。

常见方案:

  1. SPA 应用制作
  2. Iframe 标签阻止刷新
  3. 弹出窗口
  4. Web Storage
  5. BroadcastChannel

第一种是向 Client-Browser 返回的报文中固定 <audio> 标签

它们第一次的报文通常如下,然后依靠不同的 脚本 (Script)#content 的内容进行修正。一般是访问服务器的某个API端口,靠数据修改。是属于早期经典的 SPA 应用。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SPA Music Player</title>
</head>
<body>
    <div id="content">
        <!-- 页面内容将在这里动态加载 -->
    </div>

    <div id="player">
        <audio id="audio" controls>
            <source src="music.mp3" type="audio/mpeg">
        </audio>
    </div>
</body>
</html>

伴随着发展。它不再被认为在生产环境中是安全的。

第二个方案是 由两个 HTML 页面实现隔离

这种方案相对 SPA 来说是折中的;A 页面保持变化,也就是 GUI 层面,B 页面保持状态不变化。可以实现真正的状态缓存。

这种办法最便捷的就是使用 iframe 创建不同的页面管理通信。

亦或者是 window.open() 的方式打开新页面窗口,使用 postMessage() 的方式进行交换。 BroadcastChannel() 的频道建立。这些是基于早期的即时通信的论坛技术实现的。

一、二方案都是不清理缓存的方案,从而实现的持续化播放。

在一和二方案之间的伪装方案:Web Storage

这种方案开销最大,管控最麻烦,暴露度高。因而很快就被抛弃。大致就是依靠对象(或者 json) 存储,恢复播放状态。

但由于 Client-Browser 会销毁整个页面,因而创建的播放器会被摧毁造成播放中断。而且在网络波动大时,中断缺点会被无限制地放大。

理清思路,解决技术债

在任何工业生产需要的情况下,服务器报文都是在前几行声明完整的通信和核验信息,空几行(\n\n\n) 供浏览器分割解析 HTML 文本。 Server 本身并不提供 DOM 解析 和局部更新,缓存操作都是由浏览器自主完成完善的。

也就是说,真正提供资源整合服务的是你的浏览器。传统意义上的服务是由服务器提供,但现代意义上的服务是由浏览器提供。或者叫做 终端感知边界部署 之类的。

核心总结为以下三条:

  • DOM 变化的唯一执行者是浏览器,触发方式为「HTML 解析」或「JS 调用 DOM API」;
  • 服务器的所有输出都是「纯文本」,对 DOM 无任何操作能力;
  • 前端框架的 DOM 更新,本质是封装了原生 DOM API,最终仍由浏览器执行。

现代化方案:

现代化方案更关注 消费端生产,遵循 声明即配置 的一个重要原则。也就是说,万物皆可参与消费。而不是工业化流程。

因此,字面量逻辑被改写。更适合每一个人参与到这个生产流程中。

React,Vue - NodeJS

这是另外一种 SPA 的展示,触发器(trigger) 在生产链的变更中被重视起来。URL 变为了一种崭新的表达式。工业产物变为消费产物。

// React + React Router 示例
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import { useRef, useState } from 'react';

function App() {
  const audioRef = useRef(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [currentSong, setCurrentSong] = useState(null);

  return (
    <BrowserRouter>
      {/* 全局播放器组件,固定在底部 */}
      <div className="global-player">
        <audio
          ref={audioRef}
          src={currentSong?.url}
          onPlay={() => setIsPlaying(true)}
          onPause={() => setIsPlaying(false)}
        />
        <button onClick={() => audioRef.current[isPlaying ? 'pause' : 'play']()}>
          {isPlaying ? '暂停' : '播放'}
        </button>
      </div>

      {/* 路由内容区域 */}
      <Routes>
        <Route path="/" element={<Home setCurrentSong={setCurrentSong} />} />
        <Route path="/album/:id" element={<Album setCurrentSong={setCurrentSong} />} />
        <Route path="/artist/:id" element={<Artist setCurrentSong={setCurrentSong} />} />
      </Routes>
    </BrowserRouter>
  );
}

不得不说这种方式确实是革命性的,虽然程序设计上的变化难以察觉,但打开并丰富了工程组装方式以便于均衡负载和风险平摊。走上了专业化。

消费端新革命向讨论 - worker -$at.vm()

这是我们一直在讨论的方式,我们希望通过 new worker() 的方式,在不同页面间共享一个音乐进程。以下是较为常见的 Worker 方式。

  1. 专用 Worker (Dedicated Worker)

    • 页面刷新/跳转:会销毁 Worker
    • 单页应用路由变化:不会销毁(因为页面没有重新加载)
    • URL hash 变化:不会销毁
    • history.pushState/replaceState:不会销毁
  2. 共享 Worker (Shared Worker)

    • 页面跳转但其他页面仍在使用:不会销毁
    • 所有连接页面都关闭:会销毁
    • 单页应用路由变化:不会销毁
  3. Service Worker

    • 独立于页面生命周期,URL 变化通常不影响
    • 需要新的 Service Worker 更新时才会替换旧的

这三种通常存在于 Client-Browser 中,将权限下放给了用户的浏览器设备。

基于 Notes 中的核心哲学,$at.vm 注册的过程必须符合以下条件构造 Client-Node:

// 用户浏览器中的情况:

// 1. 页面加载时
//    ↓
// Service Worker 注册并安装(如果支持)
//    ↓
// 存储在:浏览器内部 → Service Worker 注册表
//    ↓
// 控制范围:所有同源页面

// 2. 用户打开新标签页
//    ↓
// Shared Worker 被创建(如果使用)
//    ↓
// 存储在:浏览器内部 → Worker 线程池
//    ↓
// 多个标签页可连接到同一个 Shared Worker

// 3. 服务器视角:
//    ↓
// 服务器完全不知道 Worker 的存在
//    ↓
// 只看到普通的 HTTP 请求

我们在原型中期待简化的是(基于 view 和 compute 的分割,方便全平台移植):

// 浏览器内部架构示意
浏览器进程
├── 主进程
├── 渲染进程(每个标签页)
│   └── 页面DOM、JavaScript
│       └── AudioContext/HTMLAudioElement
└── 音频进程(独立)
    └── 音频解码器
    └── 音频输出设备
    └── 播放队列

也就是说,$at.vm() 实现它的基础技术栈需要满足如下:

1. Service Worker (缓存 + 后台同步)         ---待开发
2. Web Audio API (底层音频控制)              ---待开发
3. Media Session API (系统级集成)           ---待开发
4. IndexedDB (状态持久化)                   ---$at.vm() 系统级支持
5. BroadcastChannel/SharedWorker (跨页面通信)

在 ToDo 中的 Flow Design Support,我们预计对 IndexedDB 的支持深入。

基于原生 ES6/JavaScript 简化的流程,同时要不影响普通开发者参与的程序设计流程,设计方面可能需要平台开发者主动导入其他框架形成合作。

$at.vm() 的默认装载激活项

  1. [等待 at.StructDataShare 原型机完善]new Worker() 用于 view 和 compute 的实现
  2. [等待 at.StructDataShare 完善] IndexedDB.* 用于对象存储
  3. [未开发完毕] Python.PySeq 用于数据的清洗导入
  4. [等待 atline.min.js 原型机完善]at.StructDataShare 用于框架的对外交流部分

Metadata

Metadata

Assignees

Labels

DiscussionDiscuss an issueStatementThis issue will be synchronized to other platforms.简体中文选择讨论语言

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions