博客开发记录0x01——网易云音乐插件
发布于 2 年前 作者 taotang 2939 次浏览 来自 分享

插件设计

上一篇提到,代码高亮是通过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);
      }
    }
  });
};

插件效果

有插件

无插件

1 回复

挺棒的,只不过页面适配感觉得再调整一下?比如第一和第五首歌,后面歪了,可以给前面设置固定宽度,后面就不会歪了

回到顶部