主题
文章 markdown 编辑器
上一篇把文章功能搭好了:迁移、模型、控制器、视图都跑通了。现在给“写文章”和“编辑文章”换一支更顺手的笔——在页面里接入 Markdown 编辑器(用 CDN 的方式,不折腾打包工具)。
目标很简单:
- 在
posts/create
、posts/edit
两个页面使用 Markdown 编辑器来写正文 - 不改后端,直接复用现有的
content
字段和表单验证 - 加载要克制,只在需要的页面引入资源
开始之前
快速确认三件事(来自上一章):
- 迁移里有
text('content')
PostRequest
里有content: required|min:10
- 控制器
store
/update
使用$request->content
都有的话,继续。
第一步:给布局开两个“插槽”
做法很简单:在布局里预留样式和脚本的插入点,这样后面就可以只在某些页面按需加载编辑器资源。
找到 resources/views/components/layout.blade.php
,打开。
- 定位到
<head>
标签内,在末尾加一行:
html
@stack('styles')
- 滚动到页面底部,紧挨
</body>
前,加一行:
html
@stack('scripts')
插入后大致长这样:
html
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.14/dist/full.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.tailwindcss.com"></script>
@stack('styles')
<title>Laravel</title>
</head>
...
@stack('scripts')
</body>
</html>
这一步做完,页面看起来不会有变化,后面要靠它来“按需加载”。
第二步:创建文章页接入编辑器
找到 resources/views/posts/create.blade.php
,打开。滚动到文件最底部,依次追加样式和脚本;表单结构不用改。
样式:
html
@push('styles')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css" />
@endpush
脚本:
html
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
var textarea = document.getElementById('content')
if (!textarea) return
var easyMDE = new EasyMDE({
element: textarea,
spellChecker: false,
forceSync: true,
autosave: {
enabled: true,
uniqueId: 'post_create_content',
delay: 1000
},
placeholder: '使用 Markdown 编写内容...'
})
})
</script>
@endpush
现在登录后访问 /posts/create
,应该能看到编辑器了。写点内容,点保存,正常跳到详情页就对了。
小提示:一定保留 forceSync: true
,这样提交时才会把编辑器里的内容同步回隐藏的 <textarea>
。
第三步:编辑文章页接入编辑器
找到 resources/views/posts/edit.blade.php
,打开。也在文件底部追加样式和脚本;与创建页相比,脚本里多一个和文章绑定的 autosave 键,避免不同文章的草稿冲突。
样式:
html
@push('styles')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css" />
@endpush
脚本:
html
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
var textarea = document.getElementById('content')
if (!textarea) return
var easyMDE = new EasyMDE({
element: textarea,
spellChecker: false,
forceSync: true,
autosave: {
enabled: true,
uniqueId: 'post_edit_content_{{ $post->id }}',
delay: 1000
},
placeholder: '使用 Markdown 编写内容...'
})
})
</script>
@endpush
访问 /posts/{post}/edit
,应该能看到原来的内容已经载入编辑器了。改一改,保存,回到详情页,流程就顺了。
最后对齐一下后端
不用写代码,只核对两点:
- 控制器里
store
/update
使用$request->content
PostRequest
校验里有content: required|min:10
如果你提交时遇到 422,先从这两点排查。
常见问题
页面没有出现编辑器
- 看看布局是否加了
@stack('styles')/@stack('scripts')
- 创建/编辑页底部是否追加了
@push('styles')/@push('scripts')
- 浏览器 Network 面板里,CDN 资源是否加载成功
- 看看布局是否加了
提交后正文是空的或旧值
- 初始化时要有
forceSync: true
<textarea id="content" name="content">
与初始化里的element
要对应同一个节点
- 初始化时要有
草稿“串台”
- 创建页与编辑页的
uniqueId
不要相同;编辑页带上文章 ID 更稳妥
- 创建页与编辑页的
回滚也简单
- 删除创建/编辑页里追加的
@push('styles')/@push('scripts')
- 从布局里移除
@stack('styles')/@stack('scripts')
- 页面就会恢复成普通的
<textarea>
下一步可以做什么
- 详情页把 Markdown 渲染成 HTML(后端用
league/commonmark
,或前端用marked + DOMPurify
) - 做图片上传:支持粘贴/拖拽上传到服务器,自动插入图片链接
这两项内容稍长,可以在后面单独一节展开。