博客开发记录0x01——网易云音乐插件
插件设计
上一篇提到,代码高亮是通过jQuery修改HTML实现的。为了保持对无法进行特殊渲染的阅读器的适配,我在a
标签上添加了特殊的参数。
<a href='https://music.163.com/#/song?id=1804721006' x-link='[email protected]#base64:eyJpZCI6IjE4MDQ3MjEwMDYifQ==;'>
《北京东路的日子》十年荣耀版
</a>
其中,x-link
的格式为:
插件实现
插件框架
// src/plugins/Plugin.tsx
export interface PostPluginVariable {
title: String;
dataIndex: String;
required: boolean;
}//插件参数,方便后续使用AntDesign的ProComponent生成配置表单
export class PostPlugin {
getNamespace():String {
return '';
}
getComponent():any {
return ()=>{};
}
getVariables():Array<PostPluginVariable> {
return [];
}
getLink(data:any):String {
return `_@${this.getNamespace()}#base64:${btoa(JSON.stringify(data))};`;
}
}
export class Plugins {
private static _PLUGINS:Map<String,any> = new Map<String, any>();
static regPlugin(pluginClass:any): void {
let p = new pluginClass();
if (!(p instanceof PostPlugin)) {
return;
}
if (this._PLUGINS.has(p.getNamespace())) {
return;
}
this._PLUGINS.set(p.getNamespace(),p);
}
static namespaces() {
return this._PLUGINS.keys();
}
static plugins() {
return this._PLUGINS.values();
}
static get(ns:String) {
return this._PLUGINS.get(ns);
}
}
插件主体
// src/plugins/Music.tsx
import { PostPlugin, PostPluginVariable } from '@/plugins/Plugin';
import { PropsWithChildren } from 'react';
export class NetEaseMusicPlayerPlugin extends PostPlugin {
getNamespace(): String {
return 'com.163.music.player';
}
getVariables(): Array<PostPluginVariable> {
return [
{
title: '歌曲id',
dataIndex: 'id',
required: true
},
];
}
getComponent(): Function {
return (props:PropsWithChildren<any>)=>{
return (
<iframe
frameBorder="no"
border="0"
marginWidth={0}
marginHeight={0}
width={'100%'}
height={86}
src={`//music.163.com/outchain/player?type=2&id=${props?.id}&auto=1&height=66`}
>{"{PLUGIN}"}</iframe>
);
};
}
}
export class NetEaseMusicPlayListPlugin extends PostPlugin {
getNamespace(): String {
return 'com.163.music.playlist';
}
getVariables(): Array<PostPluginVariable> {
return [
{
title: '歌单id',
dataIndex: 'id',
required: true
},
];
}
getComponent(): Function {
return (props:PropsWithChildren<any>)=>{
return (
<iframe
frameBorder="no"
border="0"
marginWidth={0}
marginHeight={0}
width={330}
height={450}
src={`//music.163.com/outchain/player?type=0&id=${props?.id}&auto=1&height=430`}
>{"{PLUGIN}"}</iframe>
);
};
}
}
注册插件
import { AliyunDrivePlugin, BaiduPanPlugin, WeiyunPlugin } from '@/plugins/CloudDrive';
import { NetEaseMusicPlayerPlugin, NetEaseMusicPlayListPlugin } from '@/plugins/Music';
import {Plugins} from '@/plugins/Plugin';
(function() {
Plugins.regPlugin(NetEaseMusicPlayerPlugin);
Plugins.regPlugin(NetEaseMusicPlayListPlugin);
})();
插件加载
export const PluginRender = () => {
$('a',$('.post')).each(function() {
let $$ = $(this);
let xLink = $$.attr('x-link') ?? '';
let match = xLink.match(/^_@(?<ns>.*?)#(?<data>.*?);$/);
if (match!==null) {
let plugin = Plugins.get(match.groups?.ns) ?? null;
if (plugin!==null) {
//创建一个div标签,将React组件挂载到该标签上,再把a标签替换成div标签
let dom = document.createElement("div");
dom.className = `post-plugin-${match.groups?.ns.replaceAll('.','-')}`;
ReactDOM.render(decorator(plugin,match.groups?.data),dom);
$$.replaceWith(dom);
}
}
});
};