ckeditor富文本应用(npm打包)(二) {#h_ckeditor富文本应用(npm打包)(二)}
这篇主要记录npm方式打包,我希望可以把ckeditor打包成一个js文件,然后在打开当前页面的时候才引入打包的js,这样对项目侵入较少。我还可以在项目中进行配置。
npm打包方式 我在上面的方法上做了简单修改。
package.json
"scripts": {
"build": "webpack --mode development",
"build:prod": "webpack --mode production"
},
"dependencies": {
"@ckeditor/ckeditor5-adapter-ckfinder": "^38.1.0",
"@ckeditor/ckeditor5-alignment": "^38.1.0",
"@ckeditor/ckeditor5-autoformat": "^38.1.0",
"@ckeditor/ckeditor5-autosave": "^38.1.0",
"@ckeditor/ckeditor5-basic-styles": "^38.1.0",
"@ckeditor/ckeditor5-block-quote": "^38.1.0",
"@ckeditor/ckeditor5-build-decoupled-document": "^38.1.0",
"@ckeditor/ckeditor5-core": "^38.1.0",
"@ckeditor/ckeditor5-dev-utils": "^38.1.1",
"@ckeditor/ckeditor5-editor-decoupled": "^38.1.0",
"@ckeditor/ckeditor5-essentials": "^38.1.0",
"@ckeditor/ckeditor5-font": "^38.1.0",
"@ckeditor/ckeditor5-heading": "^38.1.0",
"@ckeditor/ckeditor5-image": "^38.1.0",
"@ckeditor/ckeditor5-indent": "^38.1.0",
"@ckeditor/ckeditor5-inspector": "^4.1.0",
"@ckeditor/ckeditor5-link": "^38.1.0",
"@ckeditor/ckeditor5-list": "^38.1.0",
"@ckeditor/ckeditor5-media-embed": "^38.1.0",
"@ckeditor/ckeditor5-paragraph": "^38.1.0",
"@ckeditor/ckeditor5-paste-from-office": "^38.1.0",
"@ckeditor/ckeditor5-remove-format": "^38.1.0",
"@ckeditor/ckeditor5-restricted-editing": "^38.1.0",
"@ckeditor/ckeditor5-table": "^38.1.0",
"@ckeditor/ckeditor5-theme-lark": "^38.1.0",
"@ckeditor/ckeditor5-typing": "^38.1.0",
"@ckeditor/ckeditor5-ui": "^38.1.0",
"@ckeditor/ckeditor5-utils": "^38.1.0",
"@ckeditor/ckeditor5-widget": "^38.1.0",
"css-loader": "^6.8.1",
"postcss-loader": "^7.3.3",
"raw-loader": "^4.0.2",
"webpack": "^5.88.1",
"webpack-cli": "^5.1.4"
}
webpack.config.js
'use strict';
const path = require( 'path' );
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
module.exports = {
entry: './app.js',
output: {
path: path.resolve( __dirname, 'dist' ),
filename: 'bundle.js',
library: '$editor', window.$editor这样的方式使用
libraryTarget: 'umd'
},
module: {
rules: [
{
test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
use: [ 'raw-loader' ]
},
{
test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
attributes: {
'data-cke': true
}
}
},
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: styles.getPostCssConfig( {
themeImporter: {
themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
},
minify: true
} )
}
}
]
},
]
},
devtool: 'source-map',
performance: { hints: false }
};
app.js
export {
DecoupledEditor,
Essentials, UploadAdapter, Autosave,
Paragraph, Heading,
Bold, Italic,Underline, Strikethrough, Code, Subscript, Superscript,
Alignment, Autoformat,
BlockQuote, ListProperties , TodoList, Link, MediaEmbed,
PasteFromOffice,
Table, TableToolbar,
TextTransformation,
Indent,
RestrictedEditingMode, StandardEditingMode,
Font,
RemoveFormat,
Image, ImageToolbar, ImageCaption, ImageStyle,
ImageResizeEditing, ImageResizeHandles,ImageResizeButtons,
ImageInsert,AutoImage,
}
index.html
<div id="toolbar-container"></div>
<div id="editor"></div>
<script src="dist/bundle.js"></script>
<script>
window.$editor.DecoupledEditor
.create(document.querySelector('#editor'), {
plugins: [
window.$editor.Essentials,
window.$editor.UploadAdapter,
window.$editor.Autosave,
window.$editor.Paragraph,
window.$editor.Heading,
window.$editor.Bold,
window.$editor.Italic,
window.$editor.Underline,
window.$editor.Strikethrough,
window.$editor.Code,
window.$editor.Subscript,
window.$editor.Superscript,
window.$editor.Alignment,
window.$editor.Autoformat,
window.$editor.BlockQuote,
window.$editor.ListProperties,
window.$editor.TodoList,
window.$editor.Link,
window.$editor.MediaEmbed,
window.$editor.PasteFromOffice,
window.$editor.Table,
window.$editor.TableToolbar,
window.$editor.TextTransformation,
window.$editor.Indent,
window.$editor.Font,
window.$editor.RemoveFormat,
// window.$editor.RestrictedEditingMode,
// window.$editor.StandardEditingMode,
window.$editor.Image,
window.$editor.ImageToolbar,
window.$editor.ImageCaption,
window.$editor.ImageStyle,
window.$editor.ImageInsert,
window.$editor.ImageResizeEditing,
window.$editor.ImageResizeHandles,
window.$editor.ImageResizeButtons,
window.$editor.AutoImage,
],
toolbar: {
items: [
'undo',
'redo',
'heading',
'|',
'bold', 'italic', 'underline', 'strikethrough', 'code', 'subscript', 'superscript', 'removeFormat',
'|',
'fontFamily', 'fontSize', 'fontColor', 'fontBackgroundColor',
'|',
'alignment',
'link',
'bulletedList',
'numberedList',
'todoList',
'|',
// 'restrictedEditing','restrictedEditingException',
'|',
'outdent',
'indent',
'|',
'insertImage',
'resizeImage',
'blockQuote',
'insertTable',
'mediaEmbed',
'simpleBox'
],
shouldNotGroupWhenFull: true
},
image: {
resizeOptions: [
{
name: 'resizeImage:original',
value: null,
icon: 'original'
},
{
name: 'resizeImage:50',
value: '50',
icon: 'medium'
},
{
name: 'resizeImage:75',
value: '75',
icon: 'large'
}
],
toolbar: [
'imageStyle:inline',
'imageStyle:block',
'imageStyle:side',
'|',
'toggleImageCaption',
'imageTextAlternative',
'|',
'resizeImage',
]
},
autosave: {
save(editor) {
return new AutoSaveData().save(editor.getData())
},
waitingTime: 2000
},
language: 'cn',
placeholder: 'Type the content here!',
extraPlugins: [MyCustomUploadAdapterPlugin],
updateSourceElementOnDestroy: true
})
.then(editor => {
// eslint-disable-next-line no-console
console.log(Array.from(editor.ui.componentFactory.names()))
// CKEditorInspector.attach(editor)
const toolbarContainer = document.getElementById('toolbar-container')
const toolbarElement = editor.ui.view.toolbar.element;
toolbarContainer.appendChild(toolbarElement)
// 启用只读模式
editor.on('change:isReadOnly', (evt, propertyName, isReadOnly) => {
if (isReadOnly) {
toolbarElement.style.display = 'none';
} else {
toolbarElement.style.display = 'flex';
}
});
this.editor = editor
})
.catch(error => {
console.error(error.stack)
})
</script>
文件上传
class MyUploadAdapter {
constructor(loader) {
this.loader = loader
}
upload() {
return this.loader.file
.then(file => new Promise((resolve, reject) => {
this._initRequest();
this._initListeners(resolve, reject, file);
this._sendRequest(file);
}));
}
abort() {
if (this.xhr) {
this.xhr.abort();
}
}
_initRequest() {
const xhr = this.xhr = new XMLHttpRequest();
xhr.open('POST', 'http://example.com/image/upload/path', true);
xhr.responseType = 'json';
}
_initListeners(resolve, reject, file) {
const xhr = this.xhr;
const loader = this.loader;
const genericErrorText = `上传失败: ${file.name}.`;
xhr.addEventListener('error', () => reject(genericErrorText));
xhr.addEventListener('abort', () => reject());
xhr.addEventListener('load', () => {
const response = xhr.response;
if (!response || response.error) {
return reject(response && response.error ? response.error.message : genericErrorText);
}
resolve({
default: response.url
});
});
if (xhr.upload) {
xhr.upload.addEventListener('progress', evt => {
if (evt.lengthComputable) {
loader.uploadTotal = evt.total;
loader.uploaded = evt.loaded;
}
});
}
}
_sendRequest(file) {
const data = new FormData();
data.append('upload', file);
this.xhr.send(data);
}
}
export default function MyCustomUploadAdapterPlugin(editor) {
editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
return new MyUploadAdapter(loader);
};
}