惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
S
Securelist
博客园 - Franky
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
GbyAI
GbyAI
Microsoft Azure Blog
Microsoft Azure Blog
The Cloudflare Blog
云风的 BLOG
云风的 BLOG
N
News and Events Feed by Topic
AI
AI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Schneier on Security
Schneier on Security
Attack and Defense Labs
Attack and Defense Labs
Vercel News
Vercel News
腾讯CDC
Google DeepMind News
Google DeepMind News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
Netflix TechBlog - Medium
量子位
S
Schneier on Security
Hacker News: Ask HN
Hacker News: Ask HN
Cyberwarzone
Cyberwarzone
S
Security Affairs
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
N
News and Events Feed by Topic
T
Tenable Blog
PCI Perspectives
PCI Perspectives
MyScale Blog
MyScale Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Cyber Attacks, Cyber Crime and Cyber Security
W
WeLiveSecurity
N
News | PayPal Newsroom
P
Proofpoint News Feed
O
OpenAI News
C
CERT Recently Published Vulnerability Notes
B
Blog
Cisco Talos Blog
Cisco Talos Blog
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
Y
Y Combinator Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Spread Privacy
Spread Privacy

程沛权 - 养了三只猫

声画共生:黄贯中的摇滚美学宇宙 适用于 Oxlint 与 Oxfmt 的 Oxc 优先工作流 年终总结:2025 年的一些回顾和 2026 年的一些小规划 Obsidian x 飞牛 NAS:打造免费的跨平台笔记同步与备份方案 适用于 ESLint V9 的现代化扁平化配置 解决 better-sqlite3 连接 SQLite 时报错 Could not locate the bindings file 本色十年 记录一次 ERR_INCOMPLETE_CHUNKED_ENCODING 的问题排查 使用 remark-directive 为 Unifiedjs 提供 Markdown 视频语法的解析 干净的 TypeScript 项目在编译时报错 Cannot find module 'undici-types' 的原因和解决 macOS 基于 Android Studio 修改模拟器 Hosts 给猫儿子的新年礼物 它很喜欢! 年终总结:2023 年的一些回顾和 2024 年的一些小规划 千元预算组装入门 NAS 设备 分享 NAS 的硬件基础知识 我写了一本书《前端工程化:基于 Vue.js 3.0 的设计与实践》 想分享一下它背后的故事 Git的选择性合并操作笔记:合并某个版本或某个提交 年终总结:2022 年的一些回顾和 2023 年的一些小规划 知乎收藏夹助手:自动化将专栏的文章添加到收藏夹 用Vite更简单的解决Vue3项目的预渲染问题 Pinia怎么用?Vue3全局状态的管理工具Pinia教程 每天吃什么?上班带什么饭?程序员教你做菜啦!
Vue Picture Cropper 发布 1.x 版本 讲一讲背后的设计理念
chengpeiquan · 2026-02-24 · via 程沛权 - 养了三只猫

这个春节假期对 vue-picture-cropper 这个包进行了一次改版,主要想解决一些工程设计上的老问题,例如 #49#45 这些 issue 提到的问题。

虽然是个 Breaking Change ,但对用户来说迁移成本不大,并且有了一些更灵活的用法(例如组合式函数),我自己则在重写源码时积累了一些思考点,在这篇文章里分享一下。

本文说明 vue-picture-cropper 从 v0.x 升级到 v1.x 时,在打包方式、样式加载和实例管理上的设计取舍与原因。

为什么会有这个包

在谈 1.x 的设计之前,有必要先回顾一下 0.x 的初衷。

0.x 的第一个版本发布于 2020 年 11 月。当时 Vue 3.x 刚刚发布,整体生态尚处于早期阶段,许多常用库还未完成适配,工程实践也尚未完全稳定。这导致当时的 Vue 3 业务项目很容易因为生态不足而延误工期。

因此,这个包最初并不是面向通用场景的组件库,而是一个为业务项目快速适配的个人小工具。那个时候我的设计目标非常明确:

  • 快速为 Vue 3 提供一层可用的 Cropper 封装
  • 尽可能降低接入成本
  • 以 “开箱即用” 为优先原则

因此在 0.x 中选择将 cropperjs 内置打包,用户只需安装一个包即可使用。

这个处理方式从当时的设计目标来看,是合理的取舍 —— 它优先解决了 “可用性” 和 “便捷性” 的问题,而不是工程边界与依赖模型的完备性。

但这几年随着项目逐步被更多场景使用,工程规模扩大、依赖关系复杂化,这种早期的便捷型设计也逐渐暴露出局限性。

1.x 的调整,并不是对 0.x 的否定,而是在使用场景变化之后的一次架构升级。

为什么仍然是 Cropper.js 1.x

虽然 Cropper.js 主分支已经切换到 2.x,但 v1 与 v2 在架构和使用方式上存在显著差异:

对比维度1.x 特点2.x 特点
架构传统单体 JavaScript 库,所有 API 通过构造函数和配置项提供基于 Web Components(自定义元素)重构,将不同能力拆分成可组合的元素(如 <cropper-image><cropper-selection>),表达方式更偏 “原生 DOM 组件化”
API 与使用模式以配置项/方法为主,适合 Vue 或纯 JS 组件封装一部分 API 通过 DOM 事件、属性和自定义元素组合替代原配置项,如 viewMode、dragMode 等迁移到不同元素的属性/事件
生态兼容性和迁移成本API 形态对 Vue 封装友好,稳定且迁移成本低Web Components 架构现代,但与 Vue 响应式和生命周期体系存在适配成本,需要额外桥接层
成熟度与稳定性已长期稳定维护,用户基础大,语义明确引入现代架构思路,但升级仍需用户适配 API 和行为,版本替换不够直接

基于以上差异,选择 v1 的工程考量如下:

  • 稳定可预测的 API,方便组件封装与调用
  • 低迁移成本与低学习成本,保持本库用户现有使用习惯
  • 生态友好,遇到问题更容易查找和解决
  • 面向现状工程使用,确保构建、SSR 和多实例场景可控

因此,本库依然选择依赖 Cropper.js 1.x ,而非 2.x ,以保证成熟稳定、可维护和生态兼容性。

如果希望使用 Cropper.js 2.x 的现代 Web Components 架构,建议直接使用 Cropper.js 原生库,而非本库的 Vue 封装层。

为什么仅提供 ESM 版本

从 1.x 开始,本库仅以 ESM(ES Modules)形式发布,不再提供 CommonJS (CJS) 或 IIFE 构建版本。

背景与原因:

  • 现代 Vue 项目默认使用 ESM

    绝大多数 Vue 3 项目都基于 Vite 或其他现代打包工具,这些环境原生支持 ESM。提供 CJS 或 IIFE 构建在这些场景下意义不大,同时增加维护成本。

  • IIFE / CDN 使用量极低

    通过 CDN 分发 IIFE 构建的场景在实践中非常少见,本库统计和社区反馈均显示几乎无人使用。继续提供会增加打包体积和测试负担,但对用户价值有限。

  • 简化构建与维护

    移除 CJS/IIFE 后,库的构建流程更简单,TypeScript 类型和模块导出更一致,也避免了 CJS 下 default + named export 的潜在混乱问题。

如果你的项目依赖 CJS / IIFE,请迁移到支持 ESM 的环境,例如使用 Vite、Nuxt 或现代 Webpack 版本。

关于 Bundle 的调整

在 0.x 中,Cropper.js 作为内部依赖被打包进 vue-picture-cropper 的产物中,用户只需安装一个包即可使用:

而在 1.x ,Cropper.js 不再被打包进本库的 Bundle ,需要由使用方在项目中显式安装并锁定 Cropper.js 1.x:

虽然 0.x 看起来方便,但在工程项目里存在这样的问题:

cropperjs 被打包进 vue-picture-cropper ,如果用户项目中也单独使用了 cropperjs ,或其他库也依赖 cropperjs ,就可能出现:多份 cropperjs 、多个实例冲突、工程产物体积变大

简单来说,0.x 是 “内置运行时依赖的封装组件” ,而 1.x 是 “对等依赖(peer dependency)模式下的 Vue 适配层” 。

这样做带来的好处,体现在在运行时层面是:

  • 避免重复打包与多实例问题(由宿主项目统一管理版本,只会存在一份依赖)
  • 保证运行时构造函数来源唯一
  • 降低原型链与 instanceof 判断异常的风险

体现在工程层面是:

  • 将版本控制权交还给项目本身
  • 避免 “幽灵依赖” 式的隐式依赖关系
  • 消除因传递依赖升级带来的不可预测风险
  • 更符合现代包管理最佳实践(更小的 Bundle 、更好的 Tree-Shaking )
  • 让库职责更加单一清晰

注:“幽灵依赖(phantom dependency)”指代码运行时依赖某个包,但该包未在当前项目的 package.json 中显式声明。这种情况通常源于 Node.js 的模块解析机制能够访问 node_modules 中的任意已安装包,从而导致依赖关系在项目层面不可见。

关于样式加载方式的变更

和 v0.x 不同,从 1.x 开始不再自动注入样式。

在 v0.x 中,样式会被打包为字符串,并在组件加载时动态创建 <style> 标签插入到页面中,例如:

从 1.x 开始,需要主动在项目的入口文件导入组件样式:

为什么移除自动注入?

自动注入样式在早期虽然方便,但也带来一些问题:

  • 可控性低:样式在组件加载时才插入,难以统一管理和覆盖全局主题
  • Tree-Shaking 无效:打包时无法有效剔除未使用的样式
  • 冲突与重复:在大型工程或多实例场景下,可能会重复注入或覆盖其他样式
  • 一致性问题:SSR、CSS Modules 或其他构建工具环境中,动态注入样式可能导致渲染不一致

改为显式导入后:

  • 项目可以统一管理样式入口
  • 支持 Tree-Shaking 和按需加载
  • 与现代前端构建工具和工程实践高度兼容

在 1.x ,样式加载更透明、可控、可维护,符合现代前端工程化最佳实践。

关于实例的获取方式调整

在 v0.x 版本中,cropper 实例是通过模块级变量管理的:

这种设计带来了一些限制:

  • 多实例冲突:同一页面同时使用多个裁剪框时,必须额外包一层子组件,否则实例会互相覆盖。
  • 逻辑复用不直观:开发者在多实例场景下调用方法或复用逻辑时不够方便,使用体验受限。

因此 1.x 完全重构了实例管理方式:

  • 组件实例独立管理 Cropper

    每个 VuePictureCropper 组件拥有自己的状态,不再依赖模块级变量,保证实例相互独立。

  • 通过组件 ref 直接获取实例

    开发者可以安全地调用 getDataURL、getBlob、getFile 等方法,无需担心覆盖或冲突。

  • 支持多实例和逻辑复用更简单

    在同一页面中同时存在多个裁剪框,各实例互不干扰,操作逻辑更清晰、可复用性更高。

如果希望在纯逻辑中使用裁剪能力,而不依赖模板 ref,可以使用 1.x 提供的组合式函数 useCropper,直接获得与实例绑定的控制器,实现逻辑层面的复用和集中管理。

写在最后

总的来说,1.x 在 Bundle、样式和实例三方面做了统一取舍:依赖与样式由使用方显式管理,实例与组件一一对应。这样既便于构建与 SSR,又支持多实例与逻辑复用。

虽然是一个 Breaking Change ,但 1.x 的 VuePictureCropper 组件 Props 仍然保持和 0.x 一致,主要有以下不同点:

  1. 依赖与版本变化
  2. 组件内置样式的载入方式变化
  3. 获取 Cropper 实例的方式变化

具体迁移步骤请参考 从 v0.x 迁移 以及 在线示例