寫在前面
現在視頻業務越來越流行了,播放器也比較多,作為前端工程師如何打造一個屬於自己的播放器呢?最快最有效的方式是基於開源播放器深度定製,至於選擇哪個開源播放器仁者見仁智者見智,可以參考開源播放器列表(https://www.awesomes.cn/subject/videos)選擇適合自己業務的播放器。
我們的播放器選擇了排名第一的video.js播放器,截至目前該播放器在Github擁有13,991 star, 4,059 fork,流行程度可見一斑。為了讓大家更多的瞭解它,我們細數下優點:
免費開源
這個意味著什麼就不多說了,附上項目地址(https://github.com/videojs/video.js)
兼容主流瀏覽器
在國內的前端開發環境往往需要支持到低級版本的IE瀏覽器,然後隨著Flash的退化,很多公司沒有配備Flash開發工程師,video.js提供了流暢的Flash播放器,而且在UI層面做到了和video的效果,實屬難得,比如全屏。
UI自定義
不管開源項目在UI方面做的如何漂亮,對於各具特色的業務來說都要自定義UI,一個方便簡單的自定義方式顯得格外重要,更何況它還自帶了編譯工具,只能用一個”贊“字形容。具體怎麼實現的,這裡先簡單描述下是使用JavaScript(es6)構建對象,通過Less編寫樣式規則,最後藉助Grunt編譯。
靈活插件機制
video.js提供一個插件定義的接口,使插件開發簡單易行。而且社區論壇也提供了一些好用的插件供開發者使用。附插件列表
比較完善的文檔
完善的文檔對於一個穩定的開源項目是多麼的重要,video.js提供了教程、API文檔、插件、示例、論壇等。官方地址
項目熱度
開源作者對項目的維護比較積極,提出的問題也能很快給予響應。開發者在使用過程中出現問題算是有一定保障。
書歸正傳,要想更自由的駕馭video.js,必然要了解內部原理。本文的宗旨就是通過核心代碼演示講解源碼運行機制,如果有興趣,不要離開,我們馬上開始了……
組織結構
由於源碼量較大,很多同學不知道從何入手,我們先來說下它的組織結構。
其中control-bar,menu,popup,slider,tech,tracks,utils是目錄,其他是文件。video.js是個非常優秀的面向對象的典型,所有的UI都是通過JavaScript對象組織實現的。
video.js是個入口文件,看源碼可以從這個文件開始。
setup.js處理播放器的配置安裝即data-setup屬性。
poster-image.js處理播放器貼片。
plugins.js實現了插件機制。
player.js構造了播放器類也是video.js的核心。
modal-dialog.js處理彈層相關。
media-error.js定義了各種錯誤描述,如果想理解video.js對各語言的支持,這個文件是必看的,它是橋樑。
loading-spinner.js實現了播放器加載的標誌,如果不喜歡默認加載圖標在這裡修改吧。
fullscreen-api.js實現各個瀏覽器的全屏方案。
extend.js是對node 繼承 and babel's 繼承的整合。
event-target.js 是event類和原生事件的兼容處理。
error-display.js 主要處理展示錯誤的樣式設置。
component.js 是video.js框架中最重要的類,是所有類的基類,也是實現組件化的基石。
close-button.js 是對關閉按鈕的封裝,功能比較單一。
clickable-component.js 如果想實現一個支持點擊事件和鍵盤事件具備交互功能的組件可以繼承該類,它幫你做了細緻的處理。
button.js 如果想實現一個按鈕瞭解下這個類是必要的。
big-play-button.js 這個按鈕是視頻還未播放時顯示的按鈕,官方將此按鈕放置在播放器左上角。
utils目錄顧名思義是一些常用的功能性類和函數。
tracks目錄處理的是音軌、字幕之類的功能。
tech目錄也是非常核心的類,包括對video封裝、flash的支持。
slider目錄主要是UI層面可拖動組件的實現,如進度條,音量條都是繼承的此類。
popup目錄包含了對彈層相關的類。
menu目錄包含了對菜單UI的實現。
control-bar目錄是非常核心的UI類的集合了,播放器下方的控制器都在此目錄中。
通過對組織結構的描述,大家可以,想了解video.js的哪一部分內容可以快速入手。如果還想更深入的瞭解如何正確使用這些類,請繼續閱讀繼承關係一節。
繼承關係
video.js是JavaScript面向對象實現很經典的案例,你一定會好奇在頁面上一個DOM節點加上data-setup屬性簡單配置就能生成一個複雜的播放器,然而在代碼中看不到對應的HTML”模板“。其實這都要歸功於”繼承“關係以及作者巧妙的構思。
在組織結構一節有提到,所有類的基類都是Component類,在基類中有個createEl方法這個就是JavaScript對象和DOM進行關聯的方法。在具體的類中也可以重寫該方法自定義DOM內容,然後父類和子類的DOM關係也因JavaScript對象的繼承關係被組織起來。
為了方便大家查閱video.js所有的繼承關係,整理了兩個圖表,一個是完整版,一個是核心版。
完整版
核心板
運行機制
video.js源碼代碼量比較大,我們要了解它的運行機制,首先確定它的主線是video.js文件的videojs方法,videojs方法調用player.js的Player類,Player繼承component.js文件的Component類,最後播放器成功運行。
我們來看下videojs方法的代碼、Player的構造函數、Component的構造函數,通過對代碼的講解基本整個運行機制就有了基本的瞭解,注意裡面用到的所有方法和其他類對象參照組織結構一節細細閱讀就可以掌握更多的運行細節。
videojs方法
Player的構造函數
Component的構造函數
這裡通過主線把基本的流程演示一下,輪廓出來了,更多細節還請繼續閱讀。
插件機制
一個完善和強大的框架都會繼承插件運行功能,給更多的開發者參與開發的機會進而實現框架功能的補充和延伸。我們來看下video.js的插件是如何運作的。
插件的定義
如果之前用過video.js插件的同學或者看過插件源碼,一定有看到有這句話videojs.plugin= pluginName
,我們來看下源碼:
不難看出,原理就是將插件(函數)掛載到Player對象的原型上,接下來看下是怎麼執行的。
插件的運行
在Player的構造函數里判斷是否有插件這個配置,如果有則遍歷執行。
UI"繼承"的原理
在繼承關係一節中有提到video.js的所有DOM生成都不是採用的傳統模板的方式,都是通過JavaScript對象的繼承關係來實現的。
在Component基類中有個createEl方法,在這裡可以使用DOM類生成DOM實例。每個UI類都會有一個el屬性,會在實例化的時候自動生成,源代碼在Component的構造函數中:
每個UI類有一個children屬性,用於添加子類,子類有可能扔具有children屬性,以此類推,播放器的DOM結構就是通過這樣的JavaScript對象結構實現的。
在Player的構造函數里有一句代碼this.initChildren();
啟動了UI的實例化。這個方法是在Component基類中定義的,我們來看下:
這段代碼的大意就是提取子類的名稱,然後獲取類並實例化,最後通過最關鍵的一句話this.contentEl().insertBefore(component.el(), refNode);完成了父類和子類的DOM關聯。相信inserBefore大家並不陌生吧,原生的DOM操作方法。
總結
至此,video.js的精華部分都描述完了,不知道大家是否有收穫。這裡簡單的總結一些閱讀video.js框架源碼的心得:
找準播放器實現的主線流程,方便我們有條理的閱讀代碼
瞭解框架代碼的組織結構,有的放矢的研究相關功能的代碼
理解類與類的繼承關係,方便自己構造插件或者修改源碼的時候知道從哪個類繼承
理解播放器的運行原理,有利於基於Component構造一個新類的實現
理解插件的運行機制,學會自己構造插件還是有必要的
理解UI的實現原理,就知道自己如何為播放器添加視覺層面的東西了
看看我的源碼解讀吧,能幫一點是一點,哈哈
閱讀更多 今日頭條技術團隊 的文章