Laravel Backpack自定义CKEditor构建报错:如何添加type="module"不修改源码?
type="module"属性的解决方法 更新
根据@jcastroa87的评论,我已补充ckeditor-init.js内容,更新ckeditor.js以匹配项目实际代码,并说明CKEditor构建的创建流程:
- 通过CKEditor构建工具选择HTMLEditor预设,移除所有付费插件,保留默认工具栏,技术选型为VanillaJS,集成方式选自托管(npm),下载后将包里的
main.js重命名为项目中的ckeditor.js。
问题背景
我正在Laravel Backpack的setupCreateOperation()方法中,为CKEditor字段配置自定义CKEditor5构建。按官方指引操作后,控制台报错:Uncaught SyntaxError: Cannot use import statement outside a module。
经排查,CKEditor构建工具生成的代码包含import语句(核心代码片段如下),但Backpack通过@basset指令生成的script标签缺少type="module"属性,导致浏览器无法识别模块语法:
// ckeditor.js 核心代码片段 import { ClassicEditor, AccessibilityHelp, Autoformat, AutoImage, AutoLink, Autosave, Base64UploadAdapter, Bold, Code, CodeBlock, Essentials, GeneralHtmlSupport, Heading, HtmlComment, HtmlEmbed, ImageBlock, ImageCaption, ImageInline, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageTextAlternative, ImageToolbar, ImageUpload, Italic, Link, LinkImage, List, ListProperties, Markdown, MediaEmbed, Paragraph, SelectAll, ShowBlocks, SourceEditing, Table, TableCaption, TableCellProperties, TableColumnResize, TableProperties, TableToolbar, TextTransformation, Undo } from 'ckeditor5'; import 'ckeditor5/ckeditor5.css'; const editorConfig = { 'language': '{{ app()->getLocale() }}', toolbar: { items: [ 'undo', 'redo', '|', 'sourceEditing', 'showBlocks', '|', 'heading', '|', 'bold', 'italic', 'code', '|', 'link', 'insertImage', 'mediaEmbed', 'insertTable', 'codeBlock', 'htmlEmbed', '|', 'bulletedList', 'numberedList' ], shouldNotGroupWhenFull: false }, plugins: [ AccessibilityHelp, Autoformat, AutoImage, AutoLink, Autosave, Base64UploadAdapter, Bold, Code, CodeBlock, Essentials, GeneralHtmlSupport, Heading, HtmlComment, HtmlEmbed, ImageBlock, ImageCaption, ImageInline, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageTextAlternative, ImageToolbar, ImageUpload, Italic, Link, LinkImage, List, ListProperties, Markdown, MediaEmbed, Paragraph, SelectAll, ShowBlocks, SourceEditing, Table, TableCaption, TableCellProperties, TableColumnResize, TableProperties, TableToolbar, TextTransformation, Undo ], heading: { options: [ { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' }, { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' }, { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' }, { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' }, { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' }, { model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' }, { model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' } ] }, htmlSupport: { allow: [ { name: /^.*$/, styles: true, attributes: true, classes: true } ] }, image: { toolbar: [ 'toggleImageCaption', 'imageTextAlternative', '|', 'imageStyle:inline', 'imageStyle:wrapText', 'imageStyle:breakText', '|', 'resizeImage' ] }, initialData: 'Test', link: { addTargetToExternalLinks: true, defaultProtocol: 'https://', decorators: { toggleDownloadable: { mode: 'manual', label: 'Downloadable', attributes: { download: 'file' } } } }, list: { properties: { styles: true, startIndex: true, reversed: true } }, placeholder: 'Type or paste your content here!', table: { contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties'] } };
ckeditor-init.js内容:
async function bpFieldInitCustomCkeditorElement(element) { let ckeditorInstance = await ClassicEditor.create(element[0], editorConfig).catch(error => { console.error( error ); }); if (!ckeditorInstance) return; element.on('CrudField:delete', function (e) { ckeditorInstance.destroy(); }); ckeditorInstance.editing.view.document.on('layoutChanged', function (e) { element.trigger('change'); }); element.on('CrudField:disable', function (e) { ckeditorInstance.enableReadOnlyMode('CrudField'); }); element.on('CrudField:enable', function (e) { ckeditorInstance.disableReadOnlyMode('CrudField'); }); }
Backpack生成的script标签:
<script src="http://0.0.0.0:5173/public/assets/js/ckeditor.js"></script> <script src="http://0.0.0.0:5173/public/assets/js/ckeditor-init.js"></script>
环境版本:CKEditor5 43.1.0(npm安装)、backpack/crud 6.7.38、backpack/pro 2.2.17。
可行解决方案(无需修改Backpack源码)
方案1:自定义CKEditor字段视图
- 复制Backpack默认的CKEditor字段视图文件:
从vendor/backpack/crud/src/resources/views/fields/ckeditor.blade.php复制到resources/views/vendor/backpack/crud/fields/ckeditor.blade.php - 修改视图中的脚本加载部分,替换
@basset调用为手动添加type="module"的script标签:<!-- 替换原有的@basset指令 --> <script type="module" src="{{ asset('assets/js/ckeditor/ckeditor.js') }}"></script> <script type="module" src="{{ asset('assets/js/ckeditor/ckeditor-init.js') }}"></script> - 注意:如果
editorConfig在ckeditor.js中,需确保ckeditor-init.js能正确访问该变量,或直接将配置移到init文件中。
方案2:通过Laravel Basset扩展添加属性
Laravel Basset支持为资源添加自定义属性,可在AppServiceProvider的boot方法中注册宏:
use Backpack\Basset\Facades\Basset; public function boot() { Basset::macro('module', function ($path) { return $this->add($path)->attribute('type', 'module'); }); }
之后在自定义的CKEditor视图中使用@bassetModule(public_path('assets/js/ckeditor/ckeditor.js'))替代原有的@basset指令。
方案3:将CKEditor构建打包为非模块格式
修改CKEditor构建项目的webpack.config.js,将输出格式改为IIFE(立即执行函数表达式),无需type="module"属性:
- 打开CKEditor构建项目的
webpack.config.js - 修改
output.library.type为'iife' - 重新构建并替换项目中的
ckeditor.js文件
方案4:动态修改DOM添加属性
在页面加载时,通过JavaScript为目标script标签动态添加type="module"属性:
document.addEventListener('DOMContentLoaded', function() { const ckeditorScripts = document.querySelectorAll('script[src*="ckeditor.js"]'); ckeditorScripts.forEach(script => { script.setAttribute('type', 'module'); }); });
注意:需确保此脚本在CKEditor加载前执行,否则仍可能报错。
内容的提问来源于stack exchange,提问作者René Korsgaard

