❝После введения в эту статью соответствующих концепций архитектуры микроядра брат Абао предложит вам начать с 🍉воспроизведения видео и шаг за шагом проанализировать трилогию проектирования архитектуры микроядра (плагина).
❞
1. Введение в микроядерную архитектуру
1.1 Концепция микроядра
Архитектура микроядра, иногда называемая архитектурой подключаемых модулей, представляет собой функционально-ориентированную расширяемую архитектуру, которая обычно используется для реализации приложений на основе продуктов. Архитектурный шаблон микроядра позволяет добавлять дополнительные функции приложения в виде подключаемых модулей к основному приложению, обеспечивая расширяемость, функциональное разделение и изоляцию.
Архитектурный шаблон микроядра включает в себя два типа архитектурных компонентов: базовая система и подключаемые модули."Логика приложения разделена на независимые подключаемые модули и базовые системы, что обеспечивает масштабируемость, гибкость, функциональную изоляцию и настраиваемые функции логики обработки."
Функции базовой системы на рисунке относительно стабильны и не будут постоянно модифицироваться из-за расширения бизнес-функций, в то время как подключаемые модули могут постоянно корректироваться или расширяться в соответствии с потребностями реальных бизнес-функций."Суть архитектуры микроядра заключается в том, чтобы инкапсулировать части, которые могут нуждаться в постоянном изменении, в надстройках, чтобы достичь цели быстрого и гибкого расширения, не влияя на стабильность всей системы."
Базовая система микроядерной архитектуры обычно обеспечивает минимальный набор функций, необходимых для работы системы. Многие операционные системы используют микроядерную архитектуру, откуда и произошло ее название. С точки зрения бизнес-приложений базовая система обычно представляет собой общую бизнес-логику без особых случаев, специальных правил или пользовательского кода для сложных ситуаций.
Подключаемые модули — это автономные модули, которые содержат определенную обработку, дополнительные функции и пользовательский код для улучшения или расширения дополнительных бизнес-возможностей базовой системы."Обычно подключаемые модули являются независимыми, а некоторые подключаемые модули зависят от нескольких других подключаемых модулей. Важно свести к минимуму взаимодействие между плагинами, чтобы избежать проблем с зависимостями."
1.2 Преимущества архитектуры микрокордер
- Высокая гибкость. Общая гибкость — это способность быстро реагировать на изменения в окружающей среде. Из-за низкой связи между плагинами изменения обычно изолированы и могут быть реализованы быстро. В целом, базовая система стабильна и быстра, обладает некоторой надежностью и требует незначительных модификаций.
- Тестируемость: Плагины можно тестировать независимо друг от друга и легко создавать макеты для демонстрации или создания прототипов новых функций без изменения базовой системы.
- Высокая производительность: хотя микроядерная архитектура сама по себе не обеспечивает производительности приложений, приложения, созданные с использованием микроядерной архитектуры, обычно работают хорошо, поскольку ненужные функции можно настраивать или адаптировать.
После введения базовых знаний, связанных с архитектурой микроядра, мы возьмем видеоплеер арбуз в качестве примера, чтобы проанализировать применение архитектуры микроядра в видеоплеере арбуз.
Прочитайте последние популярные статьи брата А Бао (спасибо за вашу поддержку и поддержку 🌹🌹🌹):
- 1.2W Words | Удивительное вводное руководство по TypeScript(1000+ 👍)
- 10 лучших проектов TS, которые ослепляют ваши глаза(640+ 👍)
- Одна статья для понимания дженериков и приложений TypeScript (7,8 тыс. слов)(500+ лайков)
2. Введение в арбузный видеоплеер
Арбузный видеоплеерЭкономящий трафик HTML5-видеоплеер с парсером. Он анализирует MP4, HLS, FLV с нижнего уровня, чтобы исследовать большее контролируемое пространство для воспроизведения видео.
(Источник изображения - http://h5player.bytedance.com/)
Его функции заключаются в анализе MP4, HLS, FLV из нижнего слоя для изучения более крупных элементов управления воспроизведением видео и имеют следующие функции:
-
Простота расширения: гибкая система подключаемых модулей, автоматическое переключение между ПК и мобильным терминалом, безопасный механизм белого списка;
-
Богаче: мощное управление MP4, плавное переключение по запросу, эффективная экономия полосы пропускания;
-
Относительно полный: полный механизм продукта, мониторинг ошибок и отчетность, а также автоматическая обработка перехода на более раннюю версию.
Чтобы начать работу с арбузным видеоплеером, требуется всего три шага: установка, след DOM и создание экземпляра для завершения использования проигрывателя.
(Источник изображения - pingan8787)
"Watermelon Video Player выступает за то, чтобы все дизайны были плагинами, от такой маленькой, как кнопка воспроизведения, до поддержки функции прямой трансляции."Если вы хотите лучше настроить плеер под свой бизнес, очень важно понимать механизм плагинов.В самом плеере есть много встроенных плагинов, таких как отчеты об ошибках, загрузка, воспроизведение и т. д. Если вы хотите настроить эффект, то можете отключить встроенные плагины, а свои только разработайте.
По умолчанию плагин самозапускающийся.Если кастомный плагин не хочет самозапускаться или менять механизм исполнения плеера по умолчанию, рекомендуется разработать его, унаследовав класс плеера."Чтобы реализовать предложение о том, что «все дизайны являются подключаемыми модулями», команда видеоплеера арбуза принимает архитектуру микроядра. Давайте начнем анализировать практику микроядра видеоплеера арбуза."
3. Использование микроядра в видеоплеере Watermelon
Архитектурный шаблон микроядра включает в себя два типа архитектурных компонентов: базовую систему и подключаемые модули. В арбузном видеоплеере основная система реализована классом Player, UML-диаграмма, соответствующая этому классу, выглядит следующим образом:
(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/player.js)
Подключаемый модуль в основном представляет собой различные встроенные подключаемые модули в видеоплеере арбуз, такие как компонент управления громкостью на панели управления, текстура проигрывателя, изображение проигрывателя в картинке и управление загрузкой проигрывателя и т. д. В дополнение к вышеупомянутым подключаемым модулям, в настоящее время Watermelon Video Player предоставляет в общей сложности 22 подключаемых модуля, и полные встроенные подключаемые модули показаны на следующем рисунке:
(Встроенный плагин видеоплеера «Арбуз»)
Для проектирования базовой системы микроядра используются три ключевые технологии: управление подключаемыми модулями, подключение подключаемых модулей и связь между подключаемыми модулями. Ниже мы сосредоточимся на этих трех ключевых моментах, чтобы поэтапно разобрать, как реализован арбузный видеоплеер.
3.1 Управление плагинами
Базовая система должна знать, какие плагины доступны в данный момент, как их загружать и когда их загружать. Распространенной реализацией является механизм реестра плагинов. Базовая система предоставляет реестр подключаемых модулей (который может быть файлом конфигурации, кодом или базой данных).Реестр подключаемых модулей содержит информацию о каждом подключаемом модуле, включая его имя, местоположение и время загрузки (загрузка при запуске). , или загружается по запросу) и т. д.
Прежде чем анализировать механизм управления плагином арбузного видеоплеера, давайте посмотримxgplayer/packages/xgplayer/src
Структура каталога:
├── control
│ ├── collect.js
│ ├── cssFullscreen.js
│ ├── danmu.js
│ ├── ....
│ └── volume.js
├── error.js
├── index.js
├── player.js
├── proxy.js
├── style
│ ├── index.scss
│ ├── ...
│ └── variable.scss
└── utils
├── animation.js
├── database.js
├── ...
└── util.js
Наблюдая за приведенной выше структурой каталогов, мы можем обнаружить, что плагины арбузного видеоплеера хранятся в единомcontrol
Под содержанием. Итак, теперь вопрос в том, как загружаются эти плагины? Когда загружается? Чтобы ответить на этот вопрос, начнем с точки входа в проект:
// packages/xgplayer/src/index.js
import Player from './player' // ①
import * as Controls from './control/*.js' // ②
import './style/index.scss' // ③
export default Player // ④
отindex.js
файл, мы обнаруживаем, что во второй строке кода мы используемimport * as Controls from './control/*.js'
Пакетный оператор импортирует все встроенные плагины плеера. Эта функция выполняется с помощьюbabel-plugin-bulk-importЭтот плагин делает это.
Помимо использования вышеуказанных плагинов, вы также можете использовать контекстный API Webpack для достижения этой цели.Выполняя функцию require.context для получения определенного контекста, вы можете автоматически импортировать модули. Во внешнем проекте, если вы столкнулись с ситуацией, когда многие модули импортируются из папки, вы можете использовать этот API, он будет проходить указанные файлы в папке, а затем импортировать модули автоматически, без необходимости явного вызова импортировать каждый раз, чтобы импортировать модуль.
Пример использования контекстного API Webpack выглядит следующим образом:
const contextRequire = require.context("./modules", true);
const modules = [];
contextRequire.keys().forEach((filename) => {
if (filename.match(/^\.\/[^_][\w/]*\.([tj])s$/)) {
modules.push(contextRequire(filename));
}
});
Хорошо, вернемся к теме. Теперь мы знаем, что все встроенные плагины арбузного видеоплеера сделаныbabel-plugin-bulk-importЭтот плагин загружается на этапе сборки. Если вы не хотите использовать встроенные элементы управления в плеере, вы можете использоватьignores
Пункт конфигурации закрывается и заменяется одноименным функциональным плагином, разработанным вами:
new Player({
el:document.querySelector('#mse'),
url: 'video_url',
ignores: ['replay'] // 默认值[]
});
В следующей ссылке разберем, как встроенные плагины арбузного видеоплеера подключаются к основной системе.
3.2 Штекерное соединение
Подключение подключаемых модулей относится к тому, как подключаемые модули подключаются к базовой системе. Вообще говоря, базовая система должна указывать спецификацию соединения между подключаемым модулем и базовой системой, а затем подключаемый модуль реализуется в соответствии со спецификацией, и базовая система может быть загружена в соответствии со спецификацией.
Чтобы понять, как встроенные плагины видео арбузов связаны с базовой системой, мне нужно проанализировать существующие встроенные плагины. Здесь мы используем простойloading
Пример встроенного плагина:
// packages/xgplayer/src/control/loading.js
import Player from '../player'
let loading = function () {
let player = this;
let util = Player.util;
let container = util.createDom('xg-loading', `
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewbox="0 0 100 100">
<path d="M100,50A50,50,0,1,1,50,0"></path>
</svg>
`, {}, 'xgplayer-loading')
player.root.appendChild(container)
}
Player.install('loading', loading)
(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/control/loading.js)
В приведенном выше коде самой важной является последняя строка, т.е.Player.install('loading', loading)
эта линия. Как подсказывает название,install
Этот метод используется для установки плагина, и его конкретная реализация выглядит следующим образом:
// packages/xgplayer/src/player.js
class Player extends Proxy {
static install (name, descriptor) {
if (!Player.plugins) {
Player.plugins = {}
}
Player.plugins[name] = descriptor
}
}
Наблюдая за приведенным выше кодом, мы можем видеть, чтоinstall
Метод поддерживает два параметраname
а такжеdescriptor
, которые представляют имя и дескриптор плагина соответственно. при звонкеPlayer.install
После метода информация о плагине будет зарегистрирована в классе Player.plugins
под пространством имен. Следует отметить, что это только для завершения регистрации плагина. с использованиемPlayer
Когда класс создаст экземпляр плеера, будет выполнена операция инициализации плагина Код выглядит следующим образом:
class Player extends Proxy {
constructor(options) {
if (
this.config.controlStyle &&
util.typeOf(this.config.controlStyle) === "String"
) {
// ...
// 从服务器成功获取配置信息后,
// 再调用self.pluginsCall()
} else {
this.pluginsCall();
}
}
}
существуетPlayer
Конструктор класса вызоветpluginsCall
метод для инициализации плагина, гдеpluginsCall
Конкретная реализация метода заключается в следующем:
class Player extends Proxy {
pluginsCall() {
let self = this;
if (Player.plugins) {
let ignores = this.config.ignores;
Object.keys(Player.plugins).forEach(name => {
let descriptor = Player.plugins[name];
// 忽略ignores配置项关闭的插件
if (!ignores.some(item => name === item)) {
if (["pc", "tablet", "mobile"].some(type => type === name)) {
if (name === sniffer.device) {
setTimeout(() => {
descriptor.call(self, self);
}, 0);
}
} else {
descriptor.call(this, this);
}
}
});
}
}
}
После понимания вышеизложенного давайте представим, как настроить плагин видеоплеера арбуз. В Watermelon Video Player для создания собственного плагина нужно всего два шага:
"1. Разрабатывайте плагины"
// pluginName.js
import Player from 'xgplayer';
let pluginName=function(player){
// 插件逻辑
}
Player.install('pluginName',pluginName);
"2. Используйте плагины"
import Player from 'xgplayer';
let player = new Player({
id: 'xg',
url: '//abc.com/**/*.mp4'
})
Хорошо, давайте перейдем к следующей ссылке, которая предназначена для анализа того, как взаимодействуют основная система видеоплеера с арбузами и подключаемые модули.
3.3 Связь с плагином
Подключаемая связь относится к связи между подключаемыми модулями. Несмотря на то, что при проектировании подключаемые модули полностью отделены друг от друга, в реальном бизнес-процессе неизбежно будет бизнес-процесс, требующий взаимодействия нескольких подключаемых модулей, что требует взаимодействия между двумя подключаемыми модулями;"Поскольку между плагинами нет прямой связи, связь должна проходить через базовую систему, поэтому базовая система должна обеспечивать механизм связи плагинов.".
Эта ситуация аналогична ситуации с компьютером.ЦП компьютера, жесткий диск, память и сетевая карта представляют собой независимо разработанные конфигурации.Однако во время работы компьютера должна быть связь между ЦП и памятью, памятью и жестким диском. , Компьютер обеспечивает их через шину на материнской плате Функции связи между компонентами.
Точно так же мы используем встроенный плагин видеоплеера «Арбуз» в качестве точки входа для анализа механизма взаимодействия плагина.poster
Возьмем в качестве примера встроенные плагины.poster
Плагин используется для установки изображения обложки проигрывателя, которое отображается при инициализации проигрывателя до того, как пользователь нажмет кнопку воспроизведения.
Плагин используется следующим образом:
new Player({
el:document.querySelector('#mse'),
url: 'video_url',
poster: '//abc.com/**/*.png' // 默认值""
});
Соответствующий исходный код плагина выглядит следующим образом:
import Player from '../player'
let poster = function () {
let player = this;
let util = Player.util
let poster = util.createDom('xg-poster', '', {}, 'xgplayer-poster');
let root = player.root
if (player.config.poster) {
poster.style.backgroundImage = `url(${player.config.poster})`
root.appendChild(poster)
}
// 监听播放事件,播放时隐藏封面图
function playFunc () {
poster.style.display = 'none'
}
player.on('play', playFunc)
// 监听销毁事件,执行清理操作
function destroyFunc () {
player.off('play', playFunc)
player.off('destroy', destroyFunc)
}
player.once('destroy', destroyFunc)
}
Player.install('poster', poster)
(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/control/poster.js)
Наблюдая за исходным кодом, мы можем видеть, что плагин сначала отслеживаетplay
событие, которое нужно скрытьposter
плакат. Кроме того, у игрокаdestory
События для реализации операций очистки, таких как удалениеplay
прослушиватели событий иdestroy
мероприятие.
Для достижения вышеуказанных функций исходный код предоставляется через экземпляр проигрывателя.on
,off
а такжеonce
Три метода достижения, я полагаю, что большинство читателей знакомы с этими тремя методами, они используются для добавления мониторинга (вкл.), удаления мониторинга (откл.) и одиночного мониторинга (один раз).
Так откуда же взялись три вышеупомянутых метода? Прочитав исходный код арбузного видеоплеера, мы обнаружили, что описанный выше метод заключается в том, что класс Player наследуется от класса Proxy, а в классе Proxy он наследуется от класса Proxy путем построения наследования.event-emitterОн реализован классом EventEmitter сторонней библиотеки.
существуетposter
Плагин отслеживаетplay
а такжеdestroy
события, когда эти события будут запущены? Разберем их по отдельности:
"1. игровое событие"
// packages/xgplayer/src/proxy.js
this.ev = ['play', 'playing', 'pause', 'ended', 'error', 'seeking',
'seeked','timeupdate', 'waiting', 'canplay', 'canplaythrough',
'durationchange', 'volumechange', 'loadeddata'].map((item) => {
return {
[item]: `on${item.charAt(0).toUpperCase()}${item.slice(1)}`
}
});
this.ev.forEach(item => {
self.evItem = Object.keys(item)[0]
let name = Object.keys(item)[0]
self.video.addEventListener(Object.keys(item)[0], function () {
if (name === 'error') {
if (self.video.error) {
self.emit(name, new Errors('other',
self.currentTime, self.duration,
self.networkState, self.readyState,
self.currentSrc, self.src,
self.ended, {
line: 41,
msg: self.error,
handle: 'Constructor'
}))
}
} else {
self.emit(name, self)
}
});
(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/proxy.js)
Когда арбузный видеопроигрыватель инициализируется, он вызывает элемент Video.addEventListener
метод для мониторинга различных собственных событий в соответствующей функции обработчика событий вызоветemit
метод отправки событий.
"2. разрушительное событие"
// packages/xgplayer/src/player.js
function destroyFunc() {
this.emit("destroy");
// fix video destroy https://stackoverflow.com/questions/3258587/how-to-properly-unload-destroy-a-video-element
this.video.removeAttribute("src"); // empty source
this.video.load();
if (isDelDom) {
parentNode.removeChild(this.root);
}
for (let k in this) {
delete this[k];
}
this.off("pause", destroyFunc);
}
(https://github.com/bytedance/xgplayer/blob/master/packages/xgplayer/src/player.js)
Когда арбузный видеоплеер будет уничтожен, он будет называтьсяdestroyFunc
метод внутри этого метода будет продолжать вызыватьemit
метод испусканияdestroy
мероприятие. После этого, если другие плагины прослушиваютdestroy
событие, то будет запущен соответствующий обработчик событий для выполнения соответствующей работы по очистке. Для связи между плагинами вы также можете использоватьplayer
API, связанный с событиями, реализован в объекте игрока и не будет здесь подробно описываться.
Ранее мы проанализировали, как арбузный видеоплеер реализует архитектуру микроядра с трех аспектов управления плагинами, подключения плагинов и связи между плагинами.Давайте воспользуемся картинкой, чтобы резюмировать основное содержание:
4. Резюме
На примере видеоплеера «арбуз» в этой статье подробно рассматриваются моменты проектирования и реализации архитектуры микроядра. На самом деле, в дополнение к большому количеству встроенных плагинов, Watermelon Video Player также предоставляет некоторые функциональные плагины, такие как функциональные плагины flv и hls, для различных сценариев воспроизведения.
Кроме того, проанализировав видеоплеер с изображением арбуза, мы обнаружили, что разработать полностью функциональный компонент очень сложно, и нужно учитывать множество моментов.Здесь я кратко организую его в виде ментальной карты.Читатели могут обратиться к к этому.
Читатели, которые хотят узнать больше о арбузном видеоплеере, могут прочитать мою предыдущую подборку«Анализ функции видеоплеера «Арбуз»»Эта статья.
(https://www.yuque.com/docs/share/a86a12a1-77c4-4f78-854b-af185f90bec4?#)
5. Справочные ресурсы
В этой статье используетсяmdniceнабор текста