Contribute to this guide

guide(旧版) 使用 webpack 从源代码集成

⚠️ 我们更改了安装方法,此旧版指南仅供用户参考。如果您想详细了解这些更改,请参阅 迁移到新的安装方法 指南。

CKEditor 5 当前使用 webpack@5 构建。所有构建、示例和演示均使用此捆绑程序生成。使用其他捆绑程序(如果配置正确)也应该可以构建 CKEditor 5,例如 RollupBrowserify,但这些设置尚未正式支持。但是,有一个用于 Vite 的集成。它目前仍处于试验阶段,仅支持有限的功能。例如,@ckeditor/ckeditor5-dev-translations 允许本地化编辑器,它仅适用于 webpack。将来将在此主题上进行更多工作。

因此,本指南的前提条件是您正在使用 webpack 作为您的构建工具。

这种方案允许您完全控制 CKEditor 5 的构建过程。这意味着您实际上不再使用构建,而是直接从源代码构建 CKEditor 5 到您的项目中。这种集成方法可以让您完全控制要包含哪些功能以及 webpack 的配置方式。

您可以通过 自定义现有构建 并集成您的自定义构建来实现与这种方法类似的结果。这将提供更快的构建时间(因为 CKEditor 5 将只构建一次并提交),但是,它需要维护一个单独的存储库并将代码从该存储库安装到您的项目中(例如,通过发布一个新的 npm 包或使用 Lerna 等工具)。这使得它不如本方案中描述的方法方便。

首先,您需要安装将在现有项目中使用的源包。如果您将集成基于现有构建之一,您可以从该构建的 package.json 文件中获取它们(例如,请参阅 经典构建的 package.json)。此时,您可以选择编辑器类型和所需的功能。但是,请记住,所有包(不包括 @ckeditor/ckeditor5-dev-*)必须与基本编辑器包具有相同的版本。

将这些依赖项复制到您的 package.json 中,并调用 npm install 来安装它们。您也可以单独安装它们。插件的示例列表可能如下所示

npm install --save @ckeditor/ckeditor5-theme-lark \
  @ckeditor/ckeditor5-autoformat \
  @ckeditor/ckeditor5-basic-styles \
  @ckeditor/ckeditor5-block-quote \
  @ckeditor/ckeditor5-editor-classic \
  @ckeditor/ckeditor5-essentials \
  @ckeditor/ckeditor5-heading \
  @ckeditor/ckeditor5-link \
  @ckeditor/ckeditor5-list \
  @ckeditor/ckeditor5-paragraph

第二步是安装构建编辑器所需的依赖项。如果您想自定义 webpack 配置,列表可能会有所不同,但这是一个典型的设置

npm install --save \
    @ckeditor/ckeditor5-dev-translations@43 \
    @ckeditor/ckeditor5-dev-utils@43 \
    css-loader@5 \
    postcss-loader@4 \
    raw-loader@4 \
    style-loader@2 \
    webpack@5 \
    webpack-cli@4

如果您想在项目中使用 **TypeScript**,列表将有所不同 - 此外,您还需要安装 ts-loader

npm install --save ts-loader

# Webpack 配置

您现在可以配置 webpack。在构建 CKEditor 5 时,您需要处理几件事

  • 处理 CKEditor 主题的 CSS 文件。它们使用 import 'path/to/styles.css' 语句包含在 CKEditor 5 源代码中,因此您需要 合适的加载器
  • 类似地,您需要处理捆绑 SVG 图标,它们也直接导入到源代码中。为此,您需要 raw-loader
  • 最后,要本地化编辑器,您需要使用 @ckeditor/ckeditor5-dev-translations webpack 插件。

# JavaScript

假设您使用与 CKEditor 5 构建相同的资产处理方法,则最小配置将如下所示

// webpack.config.js

const path = require( 'path' );
const { CKEditorTranslationsPlugin } = require( '@ckeditor/ckeditor5-dev-translations' );
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );

module.exports = {
    entry: './main.js',
    output: {
        path: path.resolve( __dirname, 'dist' ),
        filename: 'bundle.js'
    },
    plugins: [
        // More plugins.
        // ...

        new CKEditorTranslationsPlugin( {
            // See https://ckeditor.npmjs.net.cn/docs/ckeditor5/latest/features/ui-language.html
            language: 'pl'
        } )
    ],
    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
                            } )
                        }
                    }
                ]
            }
        ]
    }
};

如果您无法使用最新的 webpack(在本指南编写时,它是 5),则提供的配置也适用于 webpack 4。

# TypeScript

或者,您可能需要处理 .ts 文件以便在项目中使用 TypeScript。为此,可以使用 ts-loader。Webpack 配置必须考虑这些更改。

// webpack.config.js

const path = require( 'path' );
const { CKEditorTranslationsPlugin } = require( '@ckeditor/ckeditor5-dev-translations' );
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );

module.exports = {
    entry: './main.ts',
    output: {
        path: path.resolve( __dirname, 'dist' ),
        filename: 'bundle.js'
    },
    plugins: [
        // More plugins.
        // ...

        new CKEditorTranslationsPlugin( {
            // See https://ckeditor.npmjs.net.cn/docs/ckeditor5/latest/features/ui-language.html
            language: 'pl'
        } )
    ],
    resolve: {
        extensions: [ '.ts', '.js', '.json' ],
        extensionAlias: {
            '.js': [ '.js', '.ts' ]
        }
    },
    module: {
        rules: [
            {
                test: /\.ts/,
                use: [ 'ts-loader' ]
            },
            {
                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
                            } )
                        }
                    }
                ]
            }
        ]
    }
};

# Webpack Encore

如果您使用 Webpack Encore,则可以使用以下配置

const { CKEditorTranslationsPlugin } = require( '@ckeditor/ckeditor5-dev-translations' );
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );

Encore.
    // Your configuration.
    // ...

    .addPlugin( new CKEditorTranslationsPlugin( {
        // See https://ckeditor.npmjs.net.cn/docs/ckeditor5/latest/features/ui-language.html
        language: 'pl'
    } ) )

    // Use raw-loader for CKEditor 5 SVG files.
    .addRule( {
        test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
        loader: 'raw-loader'
    } )

    // Configure other image loaders to exclude CKEditor 5 SVG files.
    .configureLoaderRule( 'images', loader => {
        loader.exclude = /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/;
    } )

    // Configure PostCSS loader.
    .addLoader({
        test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
        loader: 'postcss-loader',
        options: {
            postcssOptions: styles.getPostCssConfig( {
                themeImporter: {
                    themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
                },
                minify: true
            } )
        }
    } )

# TypeScript 配置

如果您想使用 TypeScript,请在项目根目录中添加 tsconfig.json 文件。您可以复制以下示例配置或创建自己的配置。

// tsconfig.json

{
    "compilerOptions": {
        "types": [],
        "lib": [
        "ES2019",
        "ES2020.String",
        "DOM",
        "DOM.Iterable"
        ],
        "noImplicitAny": true,
        "noImplicitOverride": true,
        "strict": true,
        "module": "es6",
        "target": "es2019",
        "sourceMap": true,
        "allowJs": true,
        "moduleResolution": "node",
        "skipLibCheck": true
    },
    "include": ["./**/*.ts"],
    "exclude": [
        "node_modules/**/*"
    ]
}

# 运行编辑器 - 方法 1

# JavaScript

您现在可以将所有需要的插件和创建器直接导入到您的代码中并在那里使用它。最简单的方法是从每个构建存储库中提供的 src/ckeditor.js 文件中复制它。

// ckeditor.js

import { ClassicEditor as ClassicEditorBase } from '@ckeditor/ckeditor5-editor-classic';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';

export default class ClassicEditor extends ClassicEditorBase {}

ClassicEditor.builtinPlugins = [
    Essentials,
    Autoformat,
    Bold,
    Italic,
    BlockQuote,
    Heading,
    Link,
    List,
    Paragraph
];

ClassicEditor.defaultConfig = {
    toolbar: {
        items: [
            'heading',
            '|',
            'bold',
            'italic',
            'link',
            'bulletedList',
            'numberedList',
            'blockQuote',
            'undo',
            'redo'
        ]
    },
    language: 'en'
};

此模块将导出一个编辑器创建器类,其中包含所有已内置的插件和配置设置。要使用配置的编辑器,只需导入该类并调用静态 .create() 方法,就像在所有 示例 中一样。

// main.js

import ClassicEditor from './ckeditor';

ClassicEditor
    // Note that you do not have to specify the plugin and toolbar configuration — using defaults from the build.
    .create( document.querySelector( '#app' ) )
    .then( editor => {
        console.log( 'Editor was initialized', editor );
    } )
    .catch( error => {
        console.error( error.stack );
    } );

# TypeScript

如果您想使用 TypeScript,ckeditor 文件看起来类似。但是,不要忘记 .ts 扩展名。

// ckeditor.ts

import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';

export default class CustomEditor extends ClassicEditor {}

CustomEditor.builtinPlugins = [
    Essentials,
    Autoformat,
    Bold,
    Italic,
    BlockQuote,
    Heading,
    Link,
    List,
    Paragraph
];

CustomEditor.defaultConfig = {
    toolbar: {
        items: [
            'heading',
            '|',
            'bold',
            'italic',
            'link',
            'bulletedList',
            'numberedList',
            'blockQuote',
            'undo',
            'redo'
        ]
    },
    language: 'en'
};

然后,您可以在应用程序中使用 TypeScript 使用配置的编辑器。

// main.ts

import ClassicEditor from './ckeditor';

ClassicEditor
    // Note that you do not have to specify the plugin and toolbar configuration — using defaults from the build.
    .create( document.querySelector( '#app' ) as HTMLElement )
    .then( editor => {
        console.log( 'Editor was initialized', editor );
    } )
    .catch( error => {
        console.error( error.stack );
    } );

# 运行编辑器 - 方法 2

# JavaScript

运行编辑器的第二种方法是直接使用创建器类,而不创建中间子类。上面的代码将转换为

// main.js

import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';

ClassicEditor
    .create( document.querySelector( '#app'), {
        // The plugins are now passed directly to .create().
        plugins: [
            Essentials,
            Autoformat,
            Bold,
            Italic,
            BlockQuote,
            Heading,
            Link,
            List,
            Paragraph,
        ],

        // So is the rest of the default configuration.
        toolbar: [
            'heading',
            'bold',
            'italic',
            'link',
            'bulletedList',
            'numberedList',
            'blockQuote',
            'undo',
            'redo'
        ]
    } )
    .then( editor => {
        console.log( editor );
    } )
    .catch( error => {
        console.error( error );
    } );

# TypeScript

您也可以使用 TypeScript 翻译上面的代码。

// main.ts

import { ClassicEditor } from '@ckeditor/ckeditor5-editor-classic';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Link } from '@ckeditor/ckeditor5-link';
import { List } from '@ckeditor/ckeditor5-list';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';

ClassicEditor
    .create( document.querySelector( '#app') as HTMLElement, {
        // The plugins are now passed directly to .create().
        plugins: [
            Essentials,
            Autoformat,
            Bold,
            Italic,
            BlockQuote,
            Heading,
            Link,
            List,
            Paragraph
        ],

        // So is the rest of the default configuration.
        toolbar: [
            'heading',
            'bold',
            'italic',
            'link',
            'bulletedList',
            'numberedList',
            'blockQuote',
            'undo',
            'redo'
        ]
    } )
    .then( editor => {
        console.log( editor );
    } )
    .catch( error => {
        console.error( error );
    } );

# 构建

最后,您可以构建您的应用程序。将构建命令添加到 package.json 的脚本中。

// package.json

{
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  }
}

现在您可以在终端中键入 npm run build 以在您的项目上运行 webpack。富文本编辑器将成为其中的一部分。

# 选项:最小化 JavaScript

Webpack 4 引入了 模式的概念。它附带两种预定义模式:developmentproduction。后者会自动启用 uglifyjs-webpack-plugin,它负责 JavaScript 最小化。因此,只需使用 --mode production 选项执行 webpack 或者在您的 webpack.config.js 中设置 mode: 'production' 即可优化构建。

在 1.2.7 版本之前,uglifyjs-webpack-plugin 存在一个错误,导致 webpack 出现以下错误而崩溃:TypeError: Assignment to constant variable。如果您遇到此错误,请确保您的 node_modules 包含此包的最新版本(以及 webpack 使用此版本)。

CKEditor 5 构建使用 Terser 而不是 uglifyjs-webpack-plugin,因为 后者不再支持

# 选项:提取 CSS

最常见的要求之一是将 CKEditor 5 CSS 提取到一个单独的文件中(默认情况下它包含在输出的 JavaScript 文件中)。为此,您可以使用 mini-css-extract-plugin

npm install --save \
    mini-css-extract-plugin \
    css-loader@5

并将其添加到您的 webpack 配置中

const MiniCssExtractPlugin = require( 'mini-css-extract-plugin' );

module.exports = {
    // More configuration.
    // ...

    plugins: [
        // More plugins.
        // ...

        new MiniCssExtractPlugin( {
            filename: 'styles.css'
        } )
    ],

    module: {
        rules: [
            {
                test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
                use: [ 'raw-loader' ]
            },
            {
                test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: styles.getPostCssConfig( {
                                themeImporter: {
                                    themePath: require.resolve( '@ckeditor/ckeditor5-theme-lark' )
                                },
                                minify: true
                            } )
                        }
                    }
                ]
            }
        ]
    }
};

Webpack 现在将创建一个名为 styles.css 的单独文件,您需要手动将其加载到您的 HTML 中(使用 <link rel="stylesheet"> 标签)。

# 选项:构建到 ES5 目标

CKEditor 5 是用 ECMAScript 2015(也称为 ES6)编写的。CKEditor 5 当前支持 的所有浏览器都具有足够的 ES6 支持来运行 CKEditor 5。因此,CKEditor 5 构建也以原始 ES6 格式发布。

但是,您的环境可能需要 ES5。例如,如果您使用原始 UglifyJS 等工具(它们尚未支持 ES6+),您可能需要将 CKEditor 5 源代码转换为 ES5。这将创建大约 80% 的更大构建,但将确保您的环境能够处理 CKEditor 5 代码。

要创建 CKEditor 5 的 ES5 构建,您可以使用 Babel

npm install --save babel-loader @babel/core @babel/preset-env regenerator-runtime

然后,将此项添加到 webpack module.rules 部分

module: {
    rules: [
        {
            // Match files from the `ckeditor5` package but also `ckeditor5-*` packages.
            test: /(ckeditor5(?:-[^\/\\]+)?)[\/\\].+\.js$/,
            use: [
                {
                    loader: 'babel-loader',
                    options: {
                        presets: [ require( '@babel/preset-env' ) ]
                    }
                }
            ]
        },
        // More rules.
        // ...
    ]
}

然后,加载 regenerator-runtime(需要在转换后使 ES6 生成器工作),将其作为第一个 入口点 添加

entry: [
    require.resolve( 'regenerator-runtime/runtime.js' ),

    // Your entries.
    // ...
]

此设置确保将源代码转换为 ES5。但是,它不确保加载所有 ES6 polyfill。因此,如果您想尝试 提供 IE11 兼容性,请确保也加载 babel-polyfill

babel-preset-env 包允许您选择要支持的环境并将 ES6+ 功能转换为与该环境的功能相匹配。在没有配置的情况下,它将生成 ES5 构建。