普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月22日首页

深入浅出 TinyEditor 富文本编辑器系列5:开发环境配置

2026年1月22日 14:16

你好,我是 Kagol,个人公众号:前端开源星球

TinyEditor 是一个基于 Quill 2.0 的富文本编辑器,在 Quill 基础上扩展了丰富的模块和格式,功能强大、开箱即用。

本文是《深入浅出 TinyEditor 富文本编辑器系列》文章的第5篇,主要介绍 TinyEditor 的开发环境配置。

搭建适当的开发环境对于为 TinyEditor 做贡献至关重要。本文将引导你完成整个流程,从克隆代码仓库到运行开发服务器。

前置条件

开始之前,请确保已安装以下工具:

  • Node.js:版本 18 或更高
  • pnpm:版本 9.13.0(项目的包管理器)
  • Git:用于版本控制

必须使用 pnpm,因为本项目使用 pnpm workspaces 进行 monorepo 管理。其他包管理器可能无法正常工作。

项目架构概述

TinyEditor 采用包含多个包的 monorepo 结构:

快速设置流程

设置过程可以可视化为一个简化的工作流:

分步设置

1. 克隆代码仓库

git clone git@github.com:opentiny/tiny-editor.git
cd tiny-editor

2. 安装依赖

项目使用 pnpm workspaces 管理多个包。使用以下命令安装所有依赖:

pnpm i

这将为 monorepo 中的所有包安装依赖,包括:

  • 核心编辑器库
  • 文档站点
  • 示例项目
  • 协作编辑后端

3. 启动开发服务器

对于一般开发,使用主要的开发命令:

pnpm dev

这将在 http://localhost:5173/tiny-editor/ 启动文档开发服务器。

开发工作流

核心库开发

要开发核心 fluent-editor 库:

pnpm watch

此命令以监听模式构建库,当源文件更改时自动重新构建。

文档开发

要开发文档:

# 文档开发服务器
pnpm -F docs dev

文档站点使用 VitePress,包含交互式演示和示例。

示例项目开发

要开发示例项目:

# 启动示例开发服务器
pnpm dev:projects

这将运行展示 TinyEditor 各种功能的示例项目。

协作后端开发

对于协作编辑功能:

# 启动协作后端
pnpm -F collaborative-editing-backend dev

这将为实时协作编辑启动 WebSocket 服务器。

包结构

Package 用途 开发命令
fluent-editor 核心编辑器库 pnpm watch
docs 文档站点 pnpm -F docs dev
projects 示例项目 pnpm dev:projects
collaborative-editing-backend 实时协作服务器 pnpm -F collaborative-editing-backend dev

构建命令

库构建

# 构建库用于发布
pnpm build:lib

这将构建库,同时输出 ES modules 和 CommonJS 格式。

文档构建

# 构建文档用于生产环境
pnpm build

项目构建

# 构建示例项目
pnpm build:projects

测试

项目使用 Jest 进行单元测试,使用 Playwright 进行端到端测试:

# 运行单元测试
pnpm test 
# 运行 E2E 测试
pnpm -F docs test 
# 安装 E2E 测试的浏览器依赖
pnpm install:browser

代码质量

项目使用 ESLint 配合 Antfu 配置来保证代码质量:

# 检查所有文件
pnpm lint 
# 修复检查问题
pnpm lint:fix

ESLint 配置支持 TypeScript、Vue,并强制执行一致的代码风格。

Git 钩子

项目使用 pre-commit 钩子来确保代码质量:

  • Pre-commit:运行 lint-staged 修复检查问题
  • Commit-msg:使用 verifyCommit.js 验证提交信息

开发技巧

开发核心库时,使用 pnpm watch 自动重新构建更改。库会以 ES modules 和 CommonJS 两种格式输出到 dist/ 目录。

Workspace 依赖

包使用 workspace 依赖(workspace:^)相互引用,确保在开发期间始终使用最新的本地版本。

补丁管理

项目使用 pnpm patches 修改 Quill 依赖。补丁文件位于 patches/quill@2.0.3.patch

开发环境为你提供了为 TinyEditor 做贡献所需的一切,无论是修复错误、添加功能还是改进文档。

联系我们

GitHub:github.com/opentiny/ti…(欢迎 Star ⭐)

官网:opentiny.github.io/tiny-editor

个人博客:kagol.github.io/blogs/

小助手微信:opentiny-official

公众号:OpenTiny

昨天 — 2026年1月21日首页

深入浅出 TinyEditor 富文本编辑器系列4:基础使用示例

2026年1月21日 14:52

你好,我是 Kagol,个人公众号:前端开源星球

TinyEditor 是一个基于 Quill 2.0 的富文本编辑器,在 Quill 基础上扩展了丰富的模块和格式,功能强大、开箱即用。

本文是《深入浅出 TinyEditor 富文本编辑器系列》文章的第4篇,主要介绍 TinyEditor 的基础使用示例。

本文提供了 TinyEditor 入门的综合示例,涵盖了面向初学者开发者的基本实现模式和常见用例。

快速开始实现

初始化 TinyEditor 最基本的方法是通过容器元素和配置选项创建新实例:

import FluentEditor from '@opentiny/fluent-editor'
 
const editor = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
    toolbar: [
      ['bold', 'italic', 'underline'],
      ['link', 'blockquote'],
      [{ list: 'ordered' }, { list: 'bullet' }]
    ]
  }
})

核心架构概述

TinyEditor 扩展了 Quill.js,提供了增强的模块和功能。该架构采用模块化设计,每个功能都作为独立模块实现:

image.png

基本配置示例

基本文本编辑器设置

创建带有基本格式化工具的简单文本编辑器:

const basicEditor = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
    toolbar: [
      ['undo', 'redo'],
      ['bold', 'italic', 'strike', 'underline'],
      [{ script: 'super' }, { script: 'sub' }],
      [{ color: [] }, { background: [] }],
      [{ list: 'ordered' }, { list: 'bullet' }],
      ['link', 'blockquote', 'code-block']
    ]
  }
})

带表格的高级编辑器

对于需要表格功能的更复杂文档:

import { generateTableUp } from '@opentiny/fluent-editor'
import { defaultCustomSelect,TableMenuSelect, TableSelection, TableUp } from 'quill-table-up'
 
FluentEditor.register({ 'modules/table-up': generateTableUp(TableUp) }, true)
 
const advancedEditor = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
    toolbar: [
      ['undo', 'redo', 'format-painter', 'clean'],
      [
        { header: [false, 1, 2, 3, 4, 5, 6] },
        { size: ['12px', '14px', '16px', '18px', '24px', '32px'] },
        'bold', 'italic', 'strike', 'underline'
      ],
      [{ color: [] }, { background: [] }],
      [{ align: ['', 'center', 'right', 'justify'] }],
      [{ 'table-up': [] }],
      ['link', 'blockquote']
    ],
    'table-up': {
      customSelect: defaultCustomSelect,
      modules: [
        { module: TableSelection },
        { module: TableMenuSelect },
      ],
    },
  }
})

模块配置模式

协同编辑设置

通过 WebSocket provider 启用实时协作:

FluentEditor.register('modules/collaborative-editing', CollaborationModule, true)
 
const collaborativeEditor = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
      'collaborative-editing': {
        deps: {
          Y,
          Awareness,
          QuillBinding,
          QuillCursors,
          WebsocketProvider,
          IndexeddbPersistence,
        },
        provider: {
          type: 'websocket',
          options: {
            serverUrl: 'wss://ai.opentiny.design/tiny-editor/',
            roomName: ROOM_NAME,
          },
        },
        awareness: {
          state: {
            name: `userId:${Math.random().toString(36).substring(2, 15)}`,
            color: `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`,
          },
        },
        cursors: {
          template: `
              <span class="${CURSOR_CLASSES.SELECTION_CLASS}"></span>
              <span class="${CURSOR_CLASSES.CARET_CONTAINER_CLASS}">
                <span class="${CURSOR_CLASSES.CARET_CLASS}"></span>
              </span>
              <div class="${CURSOR_CLASSES.FLAG_CLASS}">
                <small class="${CURSOR_CLASSES.NAME_CLASS}"></small>
              </div>
          `,
          hideDelayMs: 500,
          hideSpeedMs: 300,
          transformOnTextChange: true,
        },
      },
  }
})

文件上传配置

配置带有自定义 MIME 类型限制的文件上传:

const editorWithUpload = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
    toolbar: [
      ['bold', 'italic'],
      ['image', 'video', 'link']
    ],
    'uploader': {
      mimetypes: [
        'image/jpeg',
        'image/png',
        'image/gif',
        'application/pdf'
      ],
      handler(range: Range, files: File[]) {
        return files.map((_, i) => i % 2 === 0 ? false : 'https://developer.mozilla.org/static/media/chrome.5e791c51c323fbb93c31.svg')
      },
      fail(file: File, range: Range) {
        this.quill.updateContents(new Delta().retain(range.index).delete(1).insert({ image: 'https://developer.mozilla.org/static/media/edge.741dffaf92fcae238b84.svg' }))
      },
    },
  }
})

常见使用场景

内容初始化

创建编辑器时设置初始内容:

const initialContent = `
<h1>Document Title</h1>
<p>This is a <strong>sample</strong> document with <em>formatted</em> text.</p>
<ul>
  <li>First item</li>
  <li>Second item</li>
</ul>
<blockquote>Important quote here</blockquote>
`
 
const editor = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
    toolbar: ['bold', 'italic', 'blockquote']
  }
})
 
// 在初始化后设置内容
editor.clipboard.dangerouslyPasteHTML(0, initialContent)

事件处理

监听编辑器事件以实现自定义功能:

const editor = new FluentEditor('#editor', {
  theme: 'snow'
})
 
// 监听文本变化
editor.on('text-change', (delta, oldDelta, source) => {
  console.log('Text changed:', delta)
})
 
// 监听选择变化
editor.on('selection-change', (range, oldRange, source) => {
  if (range) {
    console.log('User selected text:', range)
  } else {
    console.log('User lost focus')
  }
})

样式与主题

自定义主题应用

使用 snow 主题应用自定义样式:

const styledEditor = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
    toolbar: [
      [{ header: [1, 2, 3, false] }],
      ['bold', 'italic', 'underline'],
      [{ color: [] }, { background: [] }]
    ]
  },
  placeholder: 'Start typing your document...'
})

国际化设置

配置多语言支持:

const i18nEditor = new FluentEditor('#editor', {
  theme: 'snow',
  modules: {
    'i18n': {
      lang: 'zh-CN',
      fallback: 'en-US'
    },
    toolbar: ['bold', 'italic', 'link']
  }
})
 
// 动态切换语言
editor.getModule('i18n').setLanguage('en-US')

集成示例

Vue.js 集成

<template>
  <div>
    <div ref="editorRef" class="editor-container"></div>
  </div>
</template>
 
<script setup>
import { ref, onMounted } from 'vue'
import FluentEditor from '@opentiny/fluent-editor'
 
const editorRef = ref()
let editor
 
onMounted(() => {
  editor = new FluentEditor(editorRef.value, {
    theme: 'snow',
    modules: {
      toolbar: ['bold', 'italic', 'link']
    }
  })
})
</script>

React 集成

import { useEffect, useRef } from 'react'
import FluentEditor from '@opentiny/fluent-editor'
 
function EditorComponent() {
  const editorRef = useRef()
  const editorInstanceRef = useRef()
 
  useEffect(() => {
    editorInstanceRef.current = new FluentEditor(editorRef.current, {
      theme: 'snow',
      modules: {
        toolbar: ['bold', 'italic', 'link']
      }
    })
 
    return () => {
      editorInstanceRef.current = null
    }
  }, [])
 
  return <div ref={editorRef} className="editor-container" />
}

最佳实践

  1. 始终指定主题 - 'snow' 主题提供默认 UI
  2. 配置工具栏模块 - 定义用户可用的工具
  3. 处理内容初始化 - 在编辑器创建后设置初始内容
  4. 实现事件监听器 - 响应用户交互和内容变化
  5. 使用适当的清理 - 卸载组件时销毁编辑器实例

这些示例为使用 TinyEditor 构建复杂的富文本应用程序提供了基础。从基本设置开始,根据需要逐步添加更复杂的功能。

联系我们

GitHub:github.com/opentiny/ti…(欢迎 Star ⭐)

官网:opentiny.github.io/tiny-editor

个人博客:kagol.github.io/blogs/

小助手微信:opentiny-official

公众号:OpenTiny

❌
❌