首页
/ 在 Laravel 中通过 ZIP 自托管集成 CKEditor 5:完整配置与实战指南

在 Laravel 中通过 ZIP 自托管集成 CKEditor 5:完整配置与实战指南

2026-09-15 23:20:56作者:贡沫苏Truman

CKEditor 5 是一个模块化架构的富文本编辑器框架,它没有提供官方的 Laravel 集成包,但由于编辑器本质上是纯 JavaScript/TypeScript 库,可以在任何能托管静态资源的 Web 环境中运行——包括 PHP 生态的 Laravel。本文以 Laravel 官方集成指南 为骨架,完整讲解如何将自托管的 ZIP 构建包放入 Laravel 项目的 public 静态目录,并通过 import map 在 Blade 模板中加载与初始化编辑器,同时结合仓库源码补充配置项、许可证与数据交互的底层细节。读完本文,你将掌握一套无需 npm 打包工具、可离线部署的 CKEditor 5 + Laravel 集成方案。

集成方式选型:ZIP 自托管与 CDN 的区别

Laravel 集成 CKEditor 5 有两条主流路线,本文聚焦的是 ZIP 自托管方案:

  • ZIP 自托管:从 CDN 下载包含编辑器全部插件的压缩包,解压后放入项目静态目录,由你自己的服务器对外提供这些资源。适合无法依赖外部 CDN、有离线部署或内网环境要求的场景。相关文档见 从 ZIP 安装 CKEditor 5
  • CDN 加载:直接在页面中通过 <script><link> 引入 cdn.ckeditor.com 上的资源,同样不需要 npm 构建工具链,但需要网络可达 CKEditor CDN,且云分发需要商业许可证。见 Laravel 集成(CDN 版)

两条路线的共同前提是:CKEditor 5 是纯 JS/TS 库,Laravel(PHP)只负责把这些静态资源"喂"给浏览器,编辑器在前端完成初始化与渲染。

前置准备:准备一个 Laravel 项目

本文假设你已经拥有一个可运行的 Laravel 项目。如果还没有,可以使用 Composer 创建基础项目:

composer create-project laravel/laravel example-app

关于框架本身的安装与配置,请查阅 Laravel 官方安装文档(对应 Laravel 10.x 版本)。本文后续所有操作均基于 Laravel 默认目录结构,即 public/ 为 Web 服务器文档根目录。

第一步:下载并放置 ZIP 构建包

ZIP 自托管的第一步是获取构建产物。CKEditor 5 官方 CDN 提供了随版本的 ZIP 归档(具体下载地址可参考 quick-start 指南 中的链接)。下载并解压后,归档内包含以下关键文件:

  • index.html —— 一个可直接运行的编辑器示例页;
  • ckeditor5/ckeditor5.js —— 推荐使用的 ESM 构建,内含编辑器与全部开源插件;
  • ckeditor5/ckeditor5.umd.js —— 备选的 UMD 构建;
  • ckeditor5/*.css —— 编辑器样式表,绝大多数场景使用 ckeditor5.css(样式文件的更多说明见 Editor and content styles 指南);
  • translations/ —— 编辑器 UI 翻译文件(用于 设置 UI 语言);
  • README.mdLICENSE.md

现在将解压得到的 ckeditor5.jsckeditor5.css 复制到 Laravel 项目的 public/assets/vendor/ 目录中(目录不存在则自行创建)。完成后的项目目录结构应类似:

├── app
├── bootstrap
├── config
├── database
├── public
│   ├── assets
│   │   └── vendor
│   │       ├── ckeditor5.js
│   │       └── ckeditor5.css
│   ├── .htaccess
│   ├── favicon.ico
│   ├── index.php
│   └── robots.txt
├── resources
│   ├── views
│   │   ├── welcome.blade.php
│   │   └── ...
├── routes
└── ...

将资源放在 public/ 下是 Laravel 的静态资源约定——只有该目录中的文件才能被 Web 服务器直接访问,这也决定了后续模板中 import map 与 CSS 链接的路径写法。

第二步:在 Blade 模板中引入并初始化编辑器

ZIP 归档内的 index.html 已经包含了全部所需的标记(markup),我们可以把它复制到 resources/views/welcome.blade.php 中。关键点在于:模板中的 CSS 链接与 import map 路径必须与你的目录结构一致。由于本示例中编辑器文件位于 resources/views/welcome.blade.php 的上两级目录 public/assets/vendor/,因此使用 ../../assets/vendor/ 前缀。

修改后的模板示例:

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title>CKEditor 5 - Quick start ZIP</title>
		<link rel="stylesheet" href="../../assets/vendor/ckeditor5.css">
		<style>
			.main-container {
				width: 795px;
				margin-left: auto;
				margin-right: auto;
			}
		</style>
	</head>
	<body>
		<div class="main-container">
			<div id="editor">
				<p>Hello from CKEditor 5!</p>
			</div>
		</div>
		<script type="importmap">
			{
				"imports": {
					"ckeditor5": "../../assets/vendor/ckeditor5.js",
					"ckeditor5/": "../../assets/vendor/"
				}
			}
		</script>
		<script type="module">
			import {
				ClassicEditor,
				Essentials,
				Paragraph,
				Bold,
				Italic,
				Font
			} from 'ckeditor5';

			ClassicEditor
				.create( {
					attachTo: document.querySelector( '#editor' ),
					licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
					plugins: [ Essentials, Paragraph, Bold, Italic, Font ],
					toolbar: [
						'undo', 'redo', '|', 'bold', 'italic', '|',
						'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor'
					]
				} )
				.then( editor => {
					window.editor = editor;
				} )
				.catch( error => {
					console.error( error );
				} );
		</script>
	</body>
</html>

模板中的关键机制

  1. <script type="importmap">:浏览器原生支持 import map,它将裸模块标识符 ckeditor5 映射到实际文件路径。"ckeditor5": "../../assets/vendor/ckeditor5.js" 指定主入口,"ckeditor5/": "../../assets/vendor/" 用于解析插件内部相对导入的其余模块。
  2. <script type="module">:使用 ES Module 语法从 ckeditor5 标识符按需导入编辑器类与插件。ClassicEditor 来自 classiceditor.tsEssentialsParagraphBoldItalicFont 等插件来自 ckeditor5 主包(源码位于 packages/ckeditor5-core 及对应插件包)。
  3. attachTo: document.querySelector( '#editor' ):指定编辑器挂载到的 DOM 元素。该配置项定义在 editorconfig.ts,属于根配置 RootConfig 的一部分,替代了旧版的"传入 DOM 元素作为 create() 第一参数"的写法。
  4. pluginstoolbarplugins 数组声明编辑器加载的功能插件,toolbar 数组声明工具栏上显示的按钮。二者的对应关系是:导入插件 → 加入 plugins → 把该插件提供的按钮名加入 toolbar。更系统的配置方法见 配置编辑器功能
  5. .then() / .catch()create() 返回 Promise。then 回调中拿到编辑器实例(示例里挂到 window.editor 便于调试),catch 捕获初始化错误。

关于 licenseKey(44.0.0 起必填)

模板中的 licenseKey 不是可选占位符。自 CKEditor 5 44.0.0 版本起,licenseKey 属性是使用编辑器的必要条件。对于从 ZIP 自托管的场景,只有两种合规选择:

licenseKeyEditorConfig 的顶层配置项,定义于 editorconfig.ts。需要注意的是:'GPL' 密钥只适用于 npm 或 ZIP 自托管分发,云 CDN 分发不接受 'GPL' 密钥。如需评估商业版,可申请免费试用;试用密钥有效期为 14 天,仅供评估用途。

第三步:启动服务并验证

在 Laravel 项目根目录执行:

php artisan serve

然后访问 http://localhost:8000 即可看到编辑器。

有一点必须强调:使用 import map 加载 ES Module 时,必须通过 HTTP 服务器访问页面。直接以 file:// 协议双击打开 HTML 会触发浏览器的 CORS 安全策略(模块必须从同源加载),导致编辑器无法运行。php artisan serve 已经满足了这一前提;在生产环境,Nginx、Caddy 等 Web 服务器同样可以胜任。

深入实战:编辑器数据与 Laravel 后端的交互

编辑器渲染完成只是第一步,真正有价值的场景是把编辑内容提交给 Laravel 后端保存。核心 API 是 getData()setData(),其实现在 editor.ts,完整指南见 获取与设置数据

通过 getData() 主动获取内容

需要保存数据时(例如点击"保存"按钮后通过 fetch/Axios 发送),调用编辑器实例的 getData()

let editor;

ClassicEditor
	.create( {
		attachTo: document.querySelector( '#editor' ),
		licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
		plugins: [ /* ... */ ],
		toolbar: [ /* ... */ ]
	} )
	.then( newEditor => {
		editor = newEditor;
	} )
	.catch( error => {
		console.error( error );
	} );

// 假设页面中有 <button id="submit">提交</button>
document.querySelector( '#submit' ).addEventListener( 'click', () => {
	const editorData = editor.getData();

	fetch( '/articles', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify( {
			content: editorData,
			// 记得带上 Laravel 的 CSRF token
			_token: document.querySelector( 'meta[name="csrf-token"]' ).getAttribute( 'content' )
		} )
	} );
} );

利用表单自动同步(Classic 编辑器 + <textarea>

另一种更"经典"的集成方式是把 <textarea> 作为编辑器的挂载元素。该方式仅在 Classic 编辑器且挂载元素为 <textarea> 时生效:用户提交表单时,CKEditor 会自动把编辑器内容写回 <textarea> 的 value,无需任何额外 JS 即可把数据随表单 POST 到服务端。在 PHP 端直接读取即可:

<?php
	$editor_data = $_POST[ 'content' ];
?>

从数据库读取内容回填到 <textarea> 时,务必使用 htmlspecialchars() 转义,防止 HTML 内容破坏页面结构:

<?php
	$data = htmlspecialchars("<p>Hello, world!</p>", ENT_QUOTES, 'UTF-8');
?>

<textarea name="content" id="editor"><?= $data ?></textarea>

这样 <p> 会以 &lt;p&gt; 的形式安全地出现在文本域中,避免文本中的 <<IMPORTANT> 这类内容丢失。

setData() 动态替换内容

当需要以编程方式(例如异步加载数据后)替换编辑器内容时,使用 setData()

editor.setData( '<p>Some text.</p>' );

初始化内容的两种方式

  • 默认方式:编辑器会使用挂载 DOM 元素内部的 HTML 作为初始内容(如上文模板中 <div id="editor"><p>Hello from CKEditor 5!</p></div>);
  • root.initialData:若不便修改服务端输出的 HTML,或数据需通过 JS 异步加载,可在配置中通过 root.initialData 指定初始数据,它会覆盖 DOM 内的内容。

进阶:进一步定制编辑器

按需增减功能

CKEditor 5 采用插件化架构,功能按需加载。例如在 plugins 中加入 HeadingLinkListBlockQuote 等,并在 toolbar 中列出对应按钮。移除功能则用 removePlugins

ClassicEditor
	.create( {
		licenseKey: '<YOUR_LICENSE_KEY>', // Or 'GPL'.
		removePlugins: [ 'Heading' ],
		toolbar: [ 'bold', 'italic', 'bulletedList', 'numberedList', 'blockQuote' ]
	} )
	.catch( error => {
		console.log( error );
	} );

注意插件之间存在依赖关系——例如 Autolink 依赖 Link,只移除 Link 会抛出 plugincollection-required 错误,需要把依赖它的插件一并移除。这一点同样适用于 ZIP 构建,因为 ZIP 内是包含全部插件的完整构建,控制权在你手中。

服务端视角的注意事项

  • 富文本内容属于不可信输入,保存前应在服务端做 XSS 过滤与白名单校验,前端过滤不能作为唯一防线;
  • public/assets/vendor/ 中的静态文件建议按版本命名目录(如 vendor/ckeditor5/44.x/),以便升级时正确刷新浏览器缓存;
  • 若生产环境部署在子路径下,模板中的 ../../assets/vendor/... 相对路径需要相应调整。

下一步学习路径

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.21 K
2.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
864
1.37 K
docsdocs
暂无描述
Markdown
906
5.84 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
953
1.87 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
538
615
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.29 K
1.04 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.4 K
1.49 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
550
403
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.14 K
550