插件可以在组件内实例化后,其实例驱动插件动作起来。
即只传入相关的

  • 可配置

插件会完成所有的功能

  • 动态渲染结构
  • 事件处理函数实现

并抛出所需要的使用的东西(回调函数)。

实例:做一个 Tab 选择卡组件

从外到里搭架子

创建顺序

  1. 先有一个 app.ts
  2. 为 app.ts 增加一个 pgRender() 方法
    1. 在 plugin 提供
  3. 建立 tab 组件
    1. 怎样使用驱动的 options 的设置
  4. 回到插件驱动 实现驱动的 options

app.ts

  1. import Tab from './components/Tab';
  2. import { PgRender } from './plugins/';
  3. function App(): HTMLElement[] {
  4. return [
  5. Tab({
  6. curIdx: 3
  7. })
  8. ];
  9. }
  10. PgRender(
  11. App(),
  12. document.getElementById('app')!
  13. );

plugins/index.ts

import PgTab from './pg-tab';

export function PgRender(domCollection: HTMLElement[], root: HTMLElement) {
    const oWrapper: HTMLElement = document.createElement('div'),
        oFrag: DocumentFragment = document.createDocumentFragment();

    oWrapper.className = 'container';

    domCollection.forEach((item: HTMLElement) => {
        oFrag.appendChild(item);
    })

    oWrapper.appendChild(oFrag);
    root.appendChild(oWrapper);
}

export {
    PgTab
}

components/Tab/index.ts

import { PgTab } from '../../plugins'

interface IProps {
  curIdx?: number
}

function Tab(props: IProps) {
  return new PgTab(
    {
      currentIndex: props.curIdx,
      data: [
        {
          navItem: '导航1',
          pageItem: '页面1'
        },
        {
          navItem: '导航2',
          pageItem: '页面2'
        },
        {
          navItem: '导航3',
          pageItem: '页面3'
        },
        {
          navItem: '导航4',
          pageItem: '页面4'
        },
      ],
      callback(newIdx) {
        console.log(newIdx);
      }
    }
  ).render();
}

export default Tab;

plugins\pg-tab\index.ts

interface IOptions {
  currentIndex?: number;
  data: any[];
  callback?: (newIndx: number) => void | undefined;
}

class PgTab { // 驱动

  private _curIdx;
  private _data;
  private _callback;
  constructor(options: IOptions) {
    this._data = options.data;
    this._curIdx = this._checkIndex(options.currentIndex);
    this._callback = options.callback;
  }

  private _checkIndex(index: number | undefined): number {
    const _idx: number = index ? index >>> 0 : 0;

    if (_idx >= this._data.length) {
      return 0;
    }
    return _idx;
  }

  public render() {

  }
}

export default PgTab;

驱动视图渲染

视图的组成

  • 上面一个 Nav
  • 下面一个 Page

点击 Nav Item 来切换 Page Item

所以需要 三个盒子

  • 最外层的 wrapper
  • 导航的 Nav
  • 装 Page

plugins\pg-tab\index.ts

interface IOptions {
  currentIndex?: number;
  data: any[];
  callback?: (newIndx: number) => void | undefined;
}

interface IData {
  navItem: string;
  pageItem: string;
}

class PgTab {
  // 驱动

  private _curIdx;
  private _data;
  private _callback;

  private _oNavWrapper: HTMLElement | null = null;
  private _oPageWrapper: HTMLElement | null = null;
  constructor(options: IOptions) {
    this._data = options.data;
    this._curIdx = this._checkIndex(options.currentIndex);
    this._callback = options.callback;
  }

  private _checkIndex(index: number | undefined): number {
    const _idx: number = index ? index >>> 0 : 0;

    if (_idx >= this._data.length) {
      return 0;
    }
    return _idx;
  }

  public render() {
    this._oNavWrapper = document.createElement("div");
    this._oPageWrapper = document.createElement("div");
    const oTabWrapper: HTMLElement = document.createElement("div");

    this._oNavWrapper.className = "pg-nav";
    this._oPageWrapper.className = "pg-page";
    oTabWrapper.className = "pg-tab";

    this._data.forEach((item: IData, index: number) => {
      this._oNavWrapper!.innerHTML += `
                <div
                    class="nav-item${this._curIdx === index ? " current" : ""}"
                    style="width: ${500 / this._data.length}px"
                >${item.navItem}</div>
            `;
      this._oPageWrapper!.innerHTML += `
            <div
                class="page-item${this._curIdx === index ? " current" : ""}"
            >${item.pageItem}</div>
        `;
    });
    oTabWrapper.appendChild(this._oNavWrapper);
    oTabWrapper.appendChild(this._oPageWrapper);

    return oTabWrapper;
  }
}

export default PgTab;

image.png

样式处理

plugins\pg-tab\index.scss

.pg-tab {
  width: 500px;
  height: 500px;
  border: 1px solid #000;

  .pg-nav {
    height: 50px;
    border-bottom: 1px solid #000;

    .nav-item {
      float: left;
      line-height: 50px;
      text-align: center;
    }

    & .current {
      background-color: #000;
      color: #fff;
    }
  }

  .pg-page {
    position: relative;
    width: 100%;
    height: 450px;

    .page-item {
      display: none;
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      text-align: center;
      line-height: 450px;
      font-size: 100px;
    }

    & .current {
      display: block;
    }
  }
}

在 plugins\pg-tab\index.ts 引入样式 import "./index.scss";

绑定事件处理函数

plugins\pg-tab\index.ts

import "./index.scss";

interface IOptions {
  currentIndex?: number;
  data: any[];
  callback?: (newIndx: number) => void | undefined;
}

interface IData {
  navItem: string;
  pageItem: string;
}

class PgTab {
  // 驱动

  private _curIdx;
  private _data;
  private _callback;

  private _oNavWrapper: HTMLElement | null = null;
  private _oPageWrapper: HTMLElement | null = null;
  private _oNavItems: HTMLCollection | null = null;
  private _oPageItems: HTMLCollection | null = null;

  constructor(options: IOptions) {
    this._data = options.data;
    this._curIdx = this._checkIndex(options.currentIndex);
    this._callback = options.callback;
  }

  private _checkIndex(index: number | undefined): number {
    const _idx: number = index ? index >>> 0 : 0;

    if (_idx >= this._data.length) {
      return 0;
    }
    return _idx;
  }

  public render() {
    this._oNavWrapper = document.createElement("div");
    this._oPageWrapper = document.createElement("div");
    const oTabWrapper: HTMLElement = document.createElement("div");

    this._oNavWrapper.className = "pg-nav";
    this._oPageWrapper.className = "pg-page";
    oTabWrapper.className = "pg-tab";

    this._data.forEach((item: IData, index: number) => {
      this._oNavWrapper!.innerHTML += `
                <div
                    class="nav-item${this._curIdx === index ? " current" : ""}"
                    style="width: ${500 / this._data.length}px"
                >${item.navItem}</div>
            `;
      this._oPageWrapper!.innerHTML += `
            <div
                class="page-item${this._curIdx === index ? " current" : ""}"
            >${item.pageItem}</div>
        `;
    });
    oTabWrapper.appendChild(this._oNavWrapper);
    oTabWrapper.appendChild(this._oPageWrapper);

    this.bindEvent();

    return oTabWrapper;
  }

  private bindEvent() {
    this._oNavItems = this._oNavWrapper.getElementsByClassName("nav-item");
    this._oPageItems = this._oPageWrapper.getElementsByClassName("page-item");
    this._oNavWrapper.addEventListener(
      "click",
      this.handleNavClick.bind(this),
      false
    );
  }

  private handleNavClick(e: Event) {
    const tar = e.target as HTMLElement,
      className = tar.className;

    if (className === "nav-item") {
      this._oNavItems![this._curIdx].className = "nav-item";
      this._oPageItems![this._curIdx].className = "page-item";
      this._curIdx = [].indexOf.call(this._oNavItems, tar as never);

      this._oNavItems![this._curIdx].className += " current";
      this._oPageItems![this._curIdx].className += " current";
      this._callback && this._callback(this._curIdx); // 处理 callback 事件
    }
  }
}

export default PgTab;