Skip to content
Laravel 技术公众号,欢迎关注

文章 markdown 编辑器

上一篇把文章功能搭好了:迁移、模型、控制器、视图都跑通了。现在给“写文章”和“编辑文章”换一支更顺手的笔——在页面里接入 Markdown 编辑器(用 CDN 的方式,不折腾打包工具)。

目标很简单:

  • posts/createposts/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
  • 做图片上传:支持粘贴/拖拽上传到服务器,自动插入图片链接

这两项内容稍长,可以在后面单独一节展开。