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

## 1. Tiptap插件基礎(chǔ)概念

Tiptap的內(nèi)容由三大核心組件構(gòu)成:

- **節(jié)點(diǎn)(Node)**:文檔的基本結(jié)構(gòu)單元,對(duì)應(yīng)DOM節(jié)點(diǎn)
- **標(biāo)記(Mark)**:用于給選中的文本添加特殊樣式或語(yǔ)義
- **擴(kuò)展(Extension)**:功能擴(kuò)展的基礎(chǔ)類,可以擴(kuò)展編輯器功能

自定義插件通常通過(guò)繼承`Extension`類來(lái)實(shí)現(xiàn)。

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

### 2.1 插件基本結(jié)構(gòu)

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

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

### 2.2 實(shí)際案例:字體大小插件

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

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

// 聲明類型擴(kuò)展
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 行高插件示例

另一個(gè)實(shí)用的行高插件實(shí)現(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()

用于定義插件的配置選項(xiàng),這些選項(xiàng)可以通過(guò)`this.options`在插件內(nèi)部訪問(wèn)。

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

### 3.2 addGlobalAttributes()

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

```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()

添加編輯器命令,可以通過(guò)`editor.commands`調(diào)用。

```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)建插件后,需要在編輯器初始化時(shí)注冊(cè):

```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,
    // 其他擴(kuò)展
  ],
  content: '<p>Hello World!</p>',
})
```

## 5. 高級(jí)技巧

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

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

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

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

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

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

### 5.3 使用ProseMirror API

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

基本上無(wú)害
火星宛如雪花,從42號(hào)有軌電車車頂?shù)墓渭娖魃巷w落而下。
瀏覽 5352
1
相關(guān)推薦
最新評(píng)論
贊過(guò)的人 1
評(píng)論加載中...

暫無(wú)評(píng)論,快來(lái)評(píng)論吧!