npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@huyooo/vue-tiptap

v1.0.5

Published

面向 Vue 3 的 TipTap 富文本编辑器组件库,封装了常用正文编辑、目录、标题、表格、任务列表、图片上传、图片缩放、图片对齐、图片拖拽、图片工具栏等交互。

Readme

@huyooo/vue-tiptap

面向 Vue 3 的 TipTap 富文本编辑器组件库,封装了常用正文编辑、目录、标题、表格、任务列表、图片上传、图片缩放、图片对齐、图片拖拽、图片工具栏等交互。

特性

  • 基于 Vue 3 + TipTap 3。
  • 输出 TipTap JSON,适合直接存储到数据库。
  • 内置基础格式、标题、列表、任务列表、代码块、表格、目录、链接、颜色、高亮、上下标等能力。
  • 图片支持粘贴/拖拽上传、加载态、上传态、撤销、拖拽移动、宽度预设、滑块缩放、四角 + 四边中点缩放、左/中/右对齐。
  • 图片缩放始终保持宽高比,并限制最大宽度不超过编辑器内容区域。
  • 图片工具栏状态会与当前选中图片的对齐和宽度同步。
  • 发布产物 external 掉 Vue、TipTap 等依赖,避免重复打包和多实例问题。

安装

npm install @huyooo/vue-tiptap

vue 是 peer dependency,宿主项目必须安装 Vue 3:

npm install vue

TipTap 相关包是本包的 dependencies,安装 @huyooo/vue-tiptap 时会由 npm 自动安装,业务项目不需要手动逐个安装 @tiptap/*

基础用法

<template>
  <TiptapEditor
    v-model="content"
    :editable="true"
    :upload-image="uploadImage"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { TiptapEditor } from '@huyooo/vue-tiptap'
import '@huyooo/vue-tiptap/style.css'

const content = ref({
  type: 'doc',
  content: [{ type: 'paragraph' }],
})

async function uploadImage(file: File) {
  // 上传到你自己的文件服务或对象存储,返回编辑器可访问的图片 URL。
  const formData = new FormData()
  formData.append('file', file)

  const response = await fetch('/api/upload-image', {
    method: 'POST',
    body: formData,
  })
  const data = await response.json()
  return data.url
}
</script>

必须引入样式:

import '@huyooo/vue-tiptap/style.css'

组件 API

TiptapEditor

| 属性 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | v-model | JSONContent \| null | null | 编辑器内容,TipTap JSON 格式。 | | editable | boolean | true | 是否可编辑。只读模式下不显示工具栏、图片缩放点和图片菜单。 | | autofocus | boolean | true | 是否自动聚焦。弹窗中建议传 false。 | | limit | number \| null | null | 字数限制。 | | tiptapEditorContentClass | string | '' | 追加到编辑器根容器上的 class。 | | uploadImage | (file: File) => Promise<string> | undefined | 图片上传回调,返回可访问图片 URL。可编辑模式下建议必传。 |

事件

| 事件 | 参数 | 说明 | | --- | --- | --- | | tableOfContentsUpdate | unknown | 目录数据更新。 |

暴露方法

TiptapEditor 通过 defineExpose 暴露:

| 方法 | 说明 | | --- | --- | | getEditor() | 获取 TipTap Editor 实例。 | | scrollView(id: string) | 根据 data-toc-id 滚动到对应标题。 |

内容格式

编辑器读写 TipTap JSON,不是 HTML 或 Markdown。推荐数据库字段直接存储 JSON:

{
  "type": "doc",
  "content": [
    {
      "type": "paragraph",
      "content": [
        {
          "type": "text",
          "text": "Hello"
        }
      ]
    }
  ]
}

空文档建议使用:

const emptyDoc = {
  type: 'doc',
  content: [{ type: 'paragraph' }],
}

图片上传

上传流程

  1. 用户粘贴或拖拽图片到编辑器。
  2. 编辑器先插入图片占位节点,显示“图片上传中”。
  3. 调用 uploadImage(file)
  4. 上传成功后,将占位节点替换成真实图片 URL,并显示“图片加载中”直到图片资源加载完成。
  5. 上传失败时移除占位图。

uploadImage 只负责上传并返回 URL:

type UploadImageFn = (file: File) => Promise<string>

返回值必须是浏览器可以访问的图片地址,例如 CDN URL、签名 URL 或公开 URL。

撤销行为

上传完成替换 URL 不进入 undo 历史。用户粘贴图片后按撤销,会直接删除整张图片,不会退回“图片上传中”的占位状态。

临时文件清理

编辑器组件无法知道业务侧“是否最终保存文章”,因此临时文件清理由宿主业务负责。

推荐做法:

  • 打开编辑弹窗时记录已有图片 key,作为 baselineKeys
  • 本次弹窗中上传成功的图片记录到 sessionUploadedKeys
  • 用户取消或关闭弹窗时,删除 sessionUploadedKeys - baselineKeys
  • 保存成功后,只保留当前内容仍引用的本次上传图片,其余删除。
  • 如果弹窗关闭后上传才完成,上传回调应立即删除该文件,避免永久残留。
  • 保存前如果还有图片上传中,应阻止保存,避免把 loading 占位写入数据库。

业务侧可以从 TipTap JSON 中递归收集图片节点:

function collectImageSrcs(node: any, result = new Set<string>()) {
  if (node?.type === 'image' && typeof node.attrs?.src === 'string') {
    result.add(node.attrs.src)
  }
  node?.content?.forEach((child: any) => collectImageSrcs(child, result))
  return result
}

如果你的系统保存的是对象存储 key,建议在保存前把临时签名 URL 归一化为稳定 key,避免过期 URL 写入数据库。

图片交互

缩放

图片选中后显示四角 + 四边中点共 8 个缩放点。缩放规则:

  • 永远保持宽高比。
  • 四角和四边中点都按图片中心等比缩放。
  • 最大宽度不超过编辑器内容区域。
  • loading 状态下不显示缩放点,避免上传/加载过程中误操作。

对齐

图片是块级节点。对齐通过图片外层行容器实现:

  • 左对齐:图片靠左。
  • 居中:图片居中。
  • 右对齐:图片靠右。

选中图片时:

  • 外层虚线表示图片所在行和对齐范围。
  • 图片本体实线表示真实图片边界。
  • 图片工具栏左/中/右按钮会与当前图片 align 属性实时同步。

拖拽

图片支持拖拽移动位置。拖拽图片本体时,编辑器会显示紫色 drop cursor,表示图片将插入的位置。

注意:

  • loading 状态图片不可拖拽。
  • 缩放点拖拽只处理缩放,不触发移动。
  • 拖拽移动由 ProseMirror 节点拖拽机制处理。

只读模式

只读模式:

<TiptapEditor v-model="content" :editable="false" />

只读模式不会显示:

  • 顶部编辑工具栏
  • 图片工具栏
  • 图片缩放点
  • 表格行列菜单

样式说明

本包发布一个独立样式文件:

import '@huyooo/vue-tiptap/style.css'

如果宿主项目使用局部滚动容器或弹窗,请确保编辑器外层有明确高度或最小高度:

<TiptapEditor style="min-height: 400px" />

导出内容

import {
  TiptapEditor,
  Aside,
  Toc,
  RepositorieTitle,
  TiptapTitle,
} from '@huyooo/vue-tiptap'

import type {
  UploadImageFn,
  ModelType,
  Option,
  ShouldShowProps,
} from '@huyooo/vue-tiptap'

构建与发布

本地构建

npm run build

构建产物:

  • dist/index.js
  • dist/style.css
  • dist/**/*.d.ts

预览 npm 包内容

npm pack --dry-run

发布前建议确认:

  • 包中只包含 distREADME.mdpackage.json 等必要文件。
  • 不包含 public/vite.svg、源码临时文件、测试产物。
  • dist/index.js 没有把 Vue 和 TipTap 全量打包进去。

发布

npm run release

release 会执行版本选择并发布:

bumpp && npm publish

如果需要直接 patch 并发布:

npm run pub

发布前必须执行:

npm run build
npm pack --dry-run

依赖发布原则

  • 不要在 npm 尚未发布新版本前,提前把下游项目依赖改到不存在的版本。
  • 发布完成并确认 npm 上存在新版本后,下游项目再显式升级版本号。
  • 下游依赖版本要写明确的新版本,例如从 ^1.0.0 升到 ^1.0.1,不要只依赖旧 semver 范围自动漂移。

依赖策略

本包使用库模式构建,并 external 掉主要依赖。

这表示构建 dist/index.js 时不会把 Vue、TipTap 等依赖源码复制进 bundle,但 npm 安装时仍会自动安装 dependencies 中的依赖。

当前策略:

  • vuepeerDependencies,宿主项目必须提供,避免出现多个 Vue 实例。
  • @tiptap/*lowlightvue-color-kit@iconify/vuedependencies,安装本包时自动安装。
  • 构建时 external 所有上述依赖,减少产物体积并避免重复打包。

常见问题

为什么安装后还要引入 style.css

库模式会把 CSS 输出为独立文件。宿主项目需要显式引入:

import '@huyooo/vue-tiptap/style.css'

为什么不把 TipTap 全部打进 index.js

不打进 bundle 可以显著减少产物体积,并让 npm 依赖树正常去重。TipTap 仍在 dependencies 里,使用方安装本包时会自动安装。

为什么 Vue 是 peer dependency?

Vue 必须由宿主项目提供,确保应用中只有一套 Vue 实例,避免响应式、插件上下文和组件渲染异常。

图片上传后关闭弹窗会自动删除吗?

组件本身不知道业务弹窗是否保存成功,不会自动删除远端文件。宿主业务必须按“临时上传会话”清理未保存文件。

数据库存 HTML 还是 JSON?

推荐存 TipTap JSON。HTML 可以用于渲染导出,但不建议作为主存储格式。