GrapesJS Asset Manager 完全指南:配置、上传、编程式管理与自定义 UI
【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs
本篇技术指南围绕 GrapesJS 内置的 Asset Manager(资源管理器)模块展开,讲解如何通过assetManager配置初始化资源、配置拖拽上传与后端接口对接、利用全局/可见双集合进行编程式资源管理,以及通过custom配置与asset:custom事件完全替换默认 UI。读完本篇,你将掌握在 GrapesJS 项目中从零配置资源管理器、接入上传服务、自定义选择逻辑与打造独立外部资源管理器的完整实战方案。
模块定位:轻量且可扩展的资源管理核心
GrapesJS 的 Asset Manager 是一个轻量级模块,其核心只内置了image一种资源类型,但设计上极易扩展。在 packages/core/src/asset_manager/model/Assets.ts 中可以确认,image是注册在类型栈中的第一个(也是唯一内置的)类型定义:
Assets.prototype.types = [ { id: 'image', model: AssetImage, view: AssetImageView, isType(value: string) { if (typeof value == 'string') { return { type: 'image', src: value }; } return value; }, }, ];从源码结构看,资源类型由三部分组成:model(业务逻辑)、view(展示逻辑)与isType(类型识别函数),你可以在此基础上注册自己的video、svg-icon等类型。模块入口位于 packages/core/src/asset_manager/index.ts,其中明确声明storageKey = 'assets',即资源数据会随项目一并持久化存储。
Configuration:初始化配置与运行时读取
要修改默认配置,只需在grapesjs.init时传入assetManager属性:
const editor = grapesjs.init({ // ... assetManager: { assets: [...], // ...其他选项 } });配置项的大部分可在运行时通过模块的getConfig()读取并更新:
const amConfig = editor.AssetManager.getConfig();完整的配置类型定义与默认值集中在 packages/core/src/asset_manager/config/config.ts,下表整理了全部可用选项及其源码确认的默认值:
| 配置项 | 默认值 | 说明 |
|---|---|---|
assets | [] | 默认资源列表,支持字符串 URL 或对象 |
noAssets | '' | 无资源可展示时显示的内容 |
stylePrefix | 'am-' | 样式前缀 |
upload | '' | 上传接口地址,为空字符串时不发起 HTTP 上传 |
uploadName | 'files' | POST 请求中携带文件的字段名 |
headers | {} | 上传请求的自定义请求头 |
params | {} | 上传请求的自定义参数(如 CSRF token) |
credentials | 'include' | 上传请求的 credentials 设置,如'include'、'omit' |
multiUpload | true | 是否允许单次请求上传多个文件 |
multiUploadSuffix | '[]' | 多文件上传时追加到uploadName后的后缀 |
autoAdd | true | 上传成功后自动将响应中的资源加入集合 |
fetchOptions | undefined | 自定义传给 Fetch API 的选项 |
customFetch | undefined | 覆盖默认 Fetch 逻辑的自定义请求函数 |
uploadFile | undefined | 完全接管上传过程的函数 |
embedAsBase64 | true | 无uploadFile/upload时将资源以 Base64 内嵌 |
handleAdd | undefined | 处理内置"添加图片"表单提交的函数 |
beforeUpload | undefined | 上传前回调,返回false可取消上传 |
showUrlInput | true | 是否显示资源 URL 输入框 |
custom | false | 是否使用自定义 UI,可为布尔值或{ open, close }对象 |
dropzone | false | 整编辑器拖拽上传区(已废弃,见下方说明) |
openAssetsOnDrop | true | 拖拽落地后自动打开资源管理器(已废弃) |
dropzoneContent | '' | dropzone 内追加的内容(已废弃) |
需要注意,配置源码中dropzone、openAssetsOnDrop、dropzoneContent三个选项均已标注@deprecated,仅保留兼容,新项目中不应依赖它们。同时,文档中"upload默认false"的说法在现行源码中已演变为默认空字符串'':由 packages/core/src/asset_manager/view/FileUploader.ts 的构造逻辑可见,当upload为空且embedAsBase64为true时,上传会被自动降级为"读取为 Base64 数据 URI 加入集合",而非直接禁用。
Initialization:初始化资源集合
Asset Manager 开箱即用,只需在初始化时传入若干 URL 即可看到资源被加载:
const editor = grapesjs.init({ // ... assetManager: { assets: [ 'http://placehold.it/350x250/78c5d6/fff/image1.jpg', // 传对象可携带更多属性 { type: 'image', src: 'http://placehold.it/350x250/459ba8/fff/image2.jpg', height: 350, width: 250, name: 'displayName' }, { // 由于 'image' 是基础类型,省略 type 也会默认为 'image' src: 'http://placehold.it/350x250/79c267/fff/image3.jpg', height: 350, width: 250, name: 'displayName' }, ], } });资源对象的完整属性可参考 AssetImage Model:
defaults() { return { ...Asset.getDefaults(), type: 'image', unitDim: 'px', height: 0, width: 0, }; }即除type、src两个基础字段(定义于 Asset.ts)外,图片类型还支持unitDim(尺寸单位,默认px)、height、width,以及可自由扩展的自定义属性。模块在加载阶段会调用this.getAll().reset(this.config.assets)(见 index.ts 的onLoad()),把初始化配置灌入全局集合。另外注意 Asset.ts 底部Asset.prototype.idAttribute = 'src',资源以src作为唯一标识。
内置 Modal 与 open-assets 命令
内置的资源管理器弹窗(Modal)已实现,默认在以下场景自动出现:拖拽图片组件到画布、双击画布中的图片,以及与图片相关的其他操作(如 CSS 样式设置)。其底层由open-assets命令驱动,实现在 packages/core/src/commands/view/OpenAssets.ts。该命令会在run时设置目标、注入select/types行为,并通过editor.Modal.open()(Modal 模块详见 docs/modules/Modal.md)展示渲染结果。因此你也可以手动唤起:
// 该命令默认只展示 `image` 类型的资源 editor.runCommand('open-assets');若希望选择后能实际修改组件,需要传入目标组件,例如先选中画布中的图片再执行:
// 先确保编辑器全局可用:window.editor = editor; editor.runCommand('open-assets', { target: editor.getSelected() });命令支持在opts中传入types(展示类型白名单)、accept(上传文件类型过滤)、select、target、onClick、onDblClick、onSelect、modalTitle等选项,types会过滤全局集合后渲染,accept会直接设置到上传输入框的accept属性上。
Uploading assets:拖拽上传与后端对接
默认资源管理器自带易用的拖拽上传器,打开资源管理器即可见。你可以点击上传器选择文件,也可以直接把文件从电脑拖入触发上传。要让上传真正工作,需要先准备好接收资源的后端服务,并在配置中指定上传接口:
const editor = grapesjs.init({ // ... assetManager: { // ... // 上传接口地址,设为 false 可禁用上传(现行源码默认为空字符串) upload: 'https://endpoint/upload/assets', // POST 请求中传递文件的字段名,默认 'files' uploadName: 'files', // ... }, // ... });上传请求的构成
结合 FileUploader.ts 的实现,一次上传请求大致如下:
- 请求方法为
POST,credentials默认'include'; - 请求头默认附带
X-Requested-With: XMLHttpRequest(可通过headers覆盖或追加); - 若
multiUpload为true,每个文件以${uploadName}${multiUploadSuffix}(即默认files[])为字段名逐个append进FormData;否则只传第一个文件,字段名为uploadName; params中的键值对会一并追加进FormData;- 上传前会执行
beforeUpload(files),返回false则取消上传; - 默认使用 Fetch API,可通过
fetchOptions调整请求选项(如改为method: 'put'),或用customFetch(url, options)完全替换请求逻辑(应返回 Promise); - 若提供
uploadFile(ev, clb, opts),则整个上传流程由你接管,但需要自行触发所有asset:upload:*事件; - 在没有
uploadFile且upload为空、embedAsBase64为true时,文件会被FileReader读取为 Base64 数据 URI 并解析出宽高后加入集合(见embedAsBase64静态方法)。
Listeners:上传生命周期监听
若想在流程前后执行动作(如加载动画)或处理响应,可订阅这些事件:
// 上传开始 editor.on('asset:upload:start', () => { startAnimation(); }); // 上传结束(无论成功与否) editor.on('asset:upload:end', () => { endAnimation(); }); // 错误处理 editor.on('asset:upload:error', (err) => { notifyError(err); }); // 收到响应后处理 editor.on('asset:upload:response', (response) => { // ... });Response:服务端响应格式
上传完成后,默认(autoAdd: 1)编辑器期望在响应的data键中收到一个 JSON 数组,并自动将其加入全局集合。JSON 形如:
{ data: [ 'https://.../image.png', // ... { src: 'https://.../image2.png', type: 'image', height: 100, width: 200, }, // ... ]; }源码中onUploadResponse会先触发asset:upload:response事件,随后当config.autoAdd && target成立时执行target.add(json.data, { at: 0 })(插入到集合头部)。target即全局集合,因此只要响应格式正确,上传的资源会立即出现在面板中。
Programmatic usage:编程式管理资源
资源的一切编程式操作都通过模块 API 完成,先获取模块实例:
const am = editor.AssetManager;Asset Manager 内部维护两个资源集合(见 index.ts 构造函数中的同步逻辑):
- global:全部可用资源所在集合,通过
am.getAll()获取; - visible:当前实际渲染在面板中的集合,通过
am.getAllVisible()获取。
构造时全局集合上add/remove事件会自动同步到可见集合,这让你可以自由决定"何时展示哪些资源"。假设要做分类切换器,先把所有资源(也可在初始化时通过config.assetManager.assets定义)加入全局集合:
am.add([ { // 可传任意自定义属性 category: 'c1', src: 'http://placehold.it/350x250/78c5d6/fff/image1.jpg', }, { category: 'c1', src: 'http://placehold.it/350x250/459ba8/fff/image2.jpg', }, { category: 'c2', src: 'http://placehold.it/350x250/79c267/fff/image3.jpg', }, // ... ]);render()无参调用时会渲染全部资源:
// 不带任何参数 am.render(); am.getAll().length; // <- 3 am.getAllVisible().length; // <- 3接着只展示第一个分类的资源:
const assets = am.getAll(); am.render(assets.filter((asset) => asset.get('category') == 'c1')); am.getAll().length; // 仍是 3 个资源 am.getAllVisible().length; // 但只展示 2 个也可以混合多个数组渲染:
am.render([...assets1, ...assets2, ...assets3]);更新或删除资源:
// 通过 src 获取资源 const asset = am.get('http://.../img.jpg'); // 更新资源属性 asset.set({ src: 'http://.../new-img.jpg' }); // 删除资源(传资源对象或 src 均可) am.remove(asset); // 或 am.remove('http://.../new-img.jpg');更多 API 方法(open、close、isOpen、getContainer、addType、getType等)可查阅 API 参考。从源码可见,add()默认把新资源插入集合头部(opts.at = 0),get(src)通过idAttribute = 'src'按 URL 精确查找,remove则委托给通用删除逻辑。
Custom select logic:自定义选择逻辑
本节内容适用于 GrapesJS v0.17.26 及以上版本。
你可以用自定义选择逻辑打开资源管理器:
am.open({ types: ['image'], // 默认选项 // 不传 select 时,选中资源不会有任何动作 select(asset, complete) { const selected = editor.getSelected(); if (selected && selected.is('image')) { selected.addAttributes({ src: asset.getSrc() }); // 默认 UI 单击时触发 select(asset, false),双击时触发 select(asset, true) complete && am.close(); } }, });open()内部本质是执行open-assets命令(见 index.ts 的open()),把types与select合并进命令选项;am.close()与am.isOpen()则分别对应命令的 stop 与激活状态判断。
Customization:自定义 UI 与外部资源管理器
默认 UI 适合简单场景,但诸如搜索框、筛选器等复杂功能需要替换默认 UI。做法是开启custom配置并订阅asset:custom事件:
const editor = grapesjs.init({ // ... assetManager: { // ... custom: true, }, }); editor.on('asset:custom', (props) => { // props.open (boolean) - 资源管理器是否处于打开状态 // props.assets (Array<Asset>) - 全部资源数组 // props.types (Array<String>) - 被请求的资源类型,如 ['image'] // props.close (Function) - 关闭资源管理器的回调 // props.remove (Function<Asset>) - 删除资源的回调 // props.select (Function<Asset, boolean>) - 选中资源的回调 // props.container (HTMLElement) - 应挂载你 UI 的容器元素 // 在这里编写渲染/更新 UI 的逻辑 });自定义 UI 时,把挂载后的元素追加到容器是必须的:props.container.appendChild(this.$el);,因为默认情况下资源管理器位于 Modal 中(asset:custom事件的数据结构定义见 packages/core/src/asset_manager/types.ts 的AssetsCustomData)。
如果资源管理器是完全独立的外部模块(例如应展示在它自己的弹窗中),可通过assetManager.custom.open绑定状态:
const editor = grapesjs.init({ // ... assetManager: { // ... custom: { open(props) { // props 与 `asset:custom` 事件中的一致 // ... // 初始化并打开你的外部资源管理器 // ... // 重要: // 外部库关闭时必须把状态回传给编辑器, // 否则 GrapesJS 会以为资源管理器仍处于打开状态。 // 示例:myAssetManager.on('close', () => props.close()) }, close(props) { // 关闭外部资源管理器 }, }, }, });必须声明close函数,编辑器才能通过am.close()关闭外部资源管理器。这与 OpenAssets.ts 中的实现一致:当custom.open/custom.close为函数时,命令的open/close会分别委托给它们;否则走默认的 Modal 流程。同时,custom模式下render()会被跳过(见 index.ts 的render()首行判断),渲染职责完全交给自定义 UI。
Events:完整事件一览
资源模块的全部事件定义于 packages/core/src/asset_manager/types.ts 的AssetsEvents枚举,汇总如下:
| 事件 | 触发时机 | 回调参数 |
|---|---|---|
asset:add | 新资源加入集合 | asset |
asset:remove | 资源被移除 | asset |
asset:remove:before | 资源移除前 | asset |
asset:update | 资源属性更新 | asset, updatedProps |
asset:open | 资源管理器打开 | — |
asset:close | 资源管理器关闭 | — |
asset:upload:start | 上传开始 | — |
asset:upload:end | 上传结束 | result |
asset:upload:error | 上传出错 | error |
asset:upload:response | 收到上传响应 | res |
asset:custom | 自定义 UI 需要更新时 | AssetsCustomData |
asset | 上述所有事件的汇总(catch-all) | 事件数据对象 |
在 index.ts 的__propEv中可以看到,每个事件都会同时通过em.trigger(ev)广播到编辑器实例,并触发全局集合上同名事件,因此无论从editor.on(...)还是集合层面都能订阅。
延伸阅读
- 资源模块完整 API:docs/api/assets.md
- 配置项类型定义:packages/core/src/asset_manager/config/config.ts
- 模块实现与全局/可见集合同步逻辑:packages/core/src/asset_manager/index.ts
- 基础资源模型与图片模型:Asset.ts、AssetImage.ts
- 上传实现(含 Base64 降级逻辑):FileUploader.ts
- 资源视图基类与图片视图:AssetView.ts、AssetImageView.ts
open-assets命令实现:OpenAssets.ts
【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考