最近开始看xgplayer的源码,分享一下外面的插件模式和批量导入性能的应用,能够借鉴利用在成千上万行代码的SDK文件优化中。
一 看源码从何动手
看源码最重要的就是可能调试,播放器的源码是运行在浏览器端的,所以咱们能够间接用浏览器进行调试,node上的源码如何调试我后续再发一篇文章解说。
二 插件模式的实现
其根本构造如下:
class Player { constructor() { // 构造函数的时候调用插件 this.pluginsCall() } // 调用插件的办法 pluginsCall () { let self = this if (Player.plugins) { // 遍历插件执行 Object.keys(Player.plugins).forEach(name => { let descriptor = Player.plugins[name] descriptor.call(this, this) } } }) } } // 动态挂载插件的办法 static install (name, descriptor) { if (!Player.plugins) { Player.plugins = {} } if (!Player.plugins[name]) { Player.plugins[name] = descriptor } }}
这里设计的比拟好的点是提供类的静态方法挂载插件,而后构造函数中执行插件,这就能让援用者在创立实例之前能够挂载自定义的插件,并在创立实例时执行插件。
源码内的20几个插件对立都在controls
文件夹下,构造如下:
├── cssFullscreen.js...├── i18n.js
所有的插件都是对立的构造,引入Player
,申明插件办法,而后调用Player.install
装置插件,以cssFullscreen.js
为例:
import Player from '../player'let cssFullscreen = function () {}Player.install('cssFullscreen', cssFullscreen)
接下来就是执行这些插件代码,并且导出Player的,这里就有知识点了,其导出的文件的源码如下:
import Player from './player'import * as Controls from './controls/*.js'export default Player
这里比拟奇妙的是 没有间接导出Player
,而是利用这个文件直达了一下,直达的时候import * as Controls from './controls/*.js'
这行代码就会批量打入所有controls
文件夹下的插件并执行,乍一看这个语法没有见过,不晓得是哪个过程做了解决,通过在babel-loader中调试发现,通过babel之后就曾经解决了,看babel的配置就破案了,是这个插件babel-plugin-bulk-import
。
三 babel-plugin-bulk-import
babel-plugin-bulk-import是一个babel的插件,该插件反对应用glob 的语法进行模块的批量导入,让咱们看看它README.md中的用法介绍。
Test case:
.├── case| ├── subfolder| | └── case3.js // module.exports = { case: 999 };| ├── case1.js // module.exports = { case: 1 };| ├── case2.js // module.exports = { case: 2 };| └── case3.js // module.exports = { case: require('./subfolder/case3') };└── src └── index.js
Example#1(local imports)
import * as all from './case/**/*.js';
will result in:
all = { case: { case1: { case: 1 }, case2: { case: 2 }, case3: { case: { case: 999 } }, subfolder: { case3: { case: 999 } } } }
Example #2 (imports from node_modules)
import * as all from 'lodash/{*,**/*}.js';
will result in:
all = { node_modules : { lodash: { LODASH_MODULE1: ITS_EXPORT_CONTENTS LODASH_MODULE2: ITS_EXPORT_CONTENTS ... } }}
实现原理
如何写babel插件能够再开一篇文章解说,这里先来看一下插件实现的大略思路,基本上就是匹配glob语法,应用glob获取文件,而后改写引入的逻辑。在babel-loader中 看一下通过babel之后的代码如下:
"'use strict';Object.defineProperty(exports, "__esModule", { value: true});var _cssFullscreen = require('./controls/cssFullscreen.js');var _cssFullscreen2 = _interopRequireDefault(_cssFullscreen);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var Controls = {};/**@param v 引入的整体对象@param p 引入模块的门路 作为整体对象的属性@param a 最初的模块导出**/function _buildTree(v, p, a) { var o = v; p.map(function (_, i) { o[_] = i == p.length - 1 ? a : o[_] || {}; o = o[_]; });}_buildTree(Controls, ['controls', 'cssFullscreen'], _cssFullscreen2.default);
其实xgplayer的插件中没有导出内容,因为只须要执行模块的代码即可。
四 总结利用
有了下面插件模式的设计和批量导入的能力,想到了能够解决开发中SDK单文件过大的问题,一个SDK常常就是几千行的代码:
class SDK { constructor() { } method1() { } ... method99() { }}export default SDK
利用批量导入改良之后的状况如下:
├── SDK.js├── index.js├── methods│ ├── method1.js...│ └── method99.js
SDK.js
中:
export default class SDK { constructor() { }}
methodx.js
中:
import SDK from '../SDK';SDK.prototype.methodx = function() { console.log('methodx');}
index.js
中:
import SDK from './SDK';import * as methods from './methods/*.js'export default SDK;
demo在这里:https://github.com/yylgit/lia...
其实不仅是SDK,所有咱们须要大量手动import
的中央都能够进行优化,比方import
路由,vueximport
子模块,redux中的combineReducers时import
每个reducer等等。
如果感觉有播种请关注微信公众号 前端良文 每周都会分享前端开发中的干货知识点。