Tiptap自定義插件
基本上無害
發(fā)布于 云南 2025-06-30 · 4690瀏覽 1贊
Tiptap是一個基于ProseMirror的現(xiàn)代化無渲染富文本編輯器框架,其強大的可擴展性允許開發(fā)者創(chuàng)建自定義插件來滿足特定需求。下面我將詳細介紹如何在Tiptap中開發(fā)自定義插件。

## 1. Tiptap插件基礎概念

Tiptap的內容由三大核心組件構成:

- **節(jié)點(Node)**:文檔的基本結構單元,對應DOM節(jié)點
- **標記(Mark)**:用于給選中的文本添加特殊樣式或語義
- **擴展(Extension)**:功能擴展的基礎類,可以擴展編輯器功能

自定義插件通常通過繼承`Extension`類來實現(xiàn)。

## 2. 創(chuàng)建自定義插件的基本步驟

### 2.1 插件基本結構

```javascript
import { Extension } from '@tiptap/core'

const CustomExtension = Extension.create({
  name: 'customExtension', // 插件名稱
  
  // 添加配置選項
  addOptions() {
    return {
      // 你的配置項
    }
  },
  
  // 添加全局屬性
  addGlobalAttributes() {
    return [
      {
        types: ['paragraph', 'heading'], // 應用到的節(jié)點類型
        attributes: {
          // 屬性定義
        }
      }
    ]
  },
  
  // 添加編輯器命令
  addCommands() {
    return {
      customCommand: () => ({ commands }) => {
        // 命令實現(xiàn)
      }
    }
  }
})
```

### 2.2 實際案例:字體大小插件

以下是一個完整的字體大小插件實現(xiàn):

```javascript
import { Extension } from "@tiptap/core";
import "@tiptap/extension-text-style";

// 聲明類型擴展
declare module "@tiptap/core" {
  interface Commands<ReturnType> {
    fontSize: {
      setFontSize: (fontSize: string) => ReturnType;
      unsetFontSize: () => ReturnType;
    };
  }
}

export const FontSizeExtension = Extension.create({
  name: "fontSize",

  addOptions() {
    return {
      types: ["textStyle"],
    };
  },

  addGlobalAttributes() {
    return [
      {
        types: this.options.types,
        attributes: {
          fontSize: {
            default: null,
            parseHTML: (element) => element.style.fontSize,
            renderHTML: (attributes) => {
              if (!attributes.fontSize) return {};
              return { style: `font-size: ${attributes.fontSize};` };
            },
          },
        },
      },
    ];
  },

  addCommands() {
    return {
      setFontSize: (fontSize) => ({ chain }) => {
        return chain()
          .setMark("textStyle", { fontSize })
          .run();
      },
      unsetFontSize: () => ({ chain }) => {
        return chain()
          .setMark("textStyle", { fontSize: null })
          .removeEmptyTextStyle()
          .run();
      },
    };
  },
});
```

### 2.3 行高插件示例

另一個實用的行高插件實現(xiàn):

```javascript
import { Extension } from "@tiptap/core";

declare module "@tiptap/core" {
  interface Commands<ReturnType> {
    lineHeight: {
      setLineHeight: (lineHeight: string) => ReturnType;
      unsetLineHeight: () => ReturnType;
    };
  }
}

export const LineHeightExtension = Extension.create({
  name: "lineHeight",

  addOptions() {
    return {
      types: ["paragraph", "heading"],
      defaultLineHeight: null,
    };
  },

  addGlobalAttributes() {
    return [
      {
        types: this.options.types,
        attributes: {
          lineHeight: {
            default: this.options.defaultLineHeight,
            renderHTML: (attributes) => {
              if (!attributes.lineHeight) return {};
              return { style: `line-height: ${attributes.lineHeight};` };
            },
            parseHTML: (element) => ({
              lineHeight: element.style.lineHeight || this.options.defaultLineHeight
            }),
          },
        },
      },
    ];
  },

  addCommands() {
    return {
      setLineHeight: (lineHeight) => ({ tr, state, dispatch }) => {
        tr = tr.setSelection(state.selection);
        state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos) => {
          if (this.options.types.includes(node.type.name)) {
            tr = tr.setNodeMarkup(pos, undefined, {
              ...node.attrs,
              lineHeight,
            });
          }
        });
        if (dispatch) dispatch(tr);
        return true;
      },
      unsetLineHeight: () => ({ tr, state, dispatch }) => {
        // 類似setLineHeight,但重置行高
      }
    };
  },
});
```

## 3. 插件核心API詳解

### 3.1 addOptions()

用于定義插件的配置選項,這些選項可以通過`this.options`在插件內部訪問。

```javascript
addOptions() {
  return {
    types: ['paragraph'],
    defaultColor: 'red'
  }
}
```

### 3.2 addGlobalAttributes()

定義全局屬性,可以應用到指定的節(jié)點類型上。

```javascript
addGlobalAttributes() {
  return [{
    types: this.options.types,
    attributes: {
      color: {
        default: this.options.defaultColor,
        renderHTML: attributes => ({
          style: `color: ${attributes.color}`
        }),
        parseHTML: element => element.style.color
      }
    }
  }]
}
```

### 3.3 addCommands()

添加編輯器命令,可以通過`editor.commands`調用。

```javascript
addCommands() {
  return {
    setColor: color => ({ chain }) => {
      return chain()
        .setMark('textStyle', { color })
        .run()
    },
    unsetColor: () => ({ chain }) => {
      return chain()
        .setMark('textStyle', { color: null })
        .removeEmptyTextStyle()
        .run()
    }
  }
}
```

### 3.4 renderHTML()和parseHTML()

控制插件如何渲染到HTML和從HTML解析。

```javascript
// 在addGlobalAttributes或addAttributes中使用
attributes: {
  align: {
    default: 'left',
    renderHTML: attributes => ({
      style: `text-align: ${attributes.align}`
    }),
    parseHTML: element => element.style.textAlign || 'left'
  }
}
```

## 4. 插件使用方式

創(chuàng)建插件后,需要在編輯器初始化時注冊:

```javascript
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { FontSizeExtension } from './font-size-extension'

const editor = new Editor({
  extensions: [
    StarterKit,
    FontSizeExtension,
    // 其他擴展
  ],
  content: '<p>Hello World!</p>',
})
```

## 5. 高級技巧

### 5.1 繼承現(xiàn)有擴展

可以通過`.extend()`方法擴展現(xiàn)有功能:

```javascript
import Heading from "@tiptap/extension-heading";

const CustomHeading = Heading.extend({
  addOptions() {
    return {
      ...this.parent?.(), // 保留原有選項
      levels: [1, 2, 3], // 修改選項
    };
  },
});
```

### 5.2 處理節(jié)點選擇

可以使用`addNodeView()`創(chuàng)建復雜的節(jié)點視圖。

### 5.3 使用ProseMirror API

Tiptap基于ProseMirror,可以直接使用ProseMirror的API進行更底層的操作。

基本上無害
火星宛如雪花,從42號有軌電車車頂?shù)墓渭娖魃巷w落而下。
瀏覽 4690
1
相關推薦
最新評論
贊過的人 1
評論加載中...

暫無評論,快來評論吧!