我有一个使用 TypeScript 的 Next.js 项目。我参考了官方文档,结果如下。
BundledEditor.jsx:
// ** https://www.tiny.cloud/docs/tinymce/6/react-pm-bundle/ **//
import { Editor } from '@tinymce/tinymce-react'
// remove ssr, otherwise will cause ReferenceError: navigator is not defined
// TinyMCE so the global var exists
// eslint-disable-next-line no-unused-vars
import tinymce from 'tinymce/tinymce'
// DOM model
import 'tinymce/models/dom/model'
// Theme
import 'tinymce/themes/silver'
// Toolbar icons
import 'tinymce/icons/default'
// Editor styles
import 'tinymce/skins/ui/oxide/skin.min.css'
// importing the plugin js.
// if you use a plugin that is not listed here the editor will fail to load
import 'tinymce/plugins/advlist'
import 'tinymce/plugins/anchor'
import 'tinymce/plugins/autolink'
import 'tinymce/plugins/autoresize'
import 'tinymce/plugins/autosave'
import 'tinymce/plugins/charmap'
import 'tinymce/plugins/code'
import 'tinymce/plugins/codesample'
import 'tinymce/plugins/directionality'
import 'tinymce/plugins/emoticons'
import 'tinymce/plugins/fullscreen'
import 'tinymce/plugins/help'
import 'tinymce/plugins/image'
import 'tinymce/plugins/importcss'
import 'tinymce/plugins/insertdatetime'
import 'tinymce/plugins/link'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/media'
import 'tinymce/plugins/nonbreaking'
import 'tinymce/plugins/pagebreak'
import 'tinymce/plugins/preview'
import 'tinymce/plugins/quickbars'
import 'tinymce/plugins/save'
import 'tinymce/plugins/searchreplace'
import 'tinymce/plugins/table'
import 'tinymce/plugins/template'
import 'tinymce/plugins/visualblocks'
import 'tinymce/plugins/visualchars'
import 'tinymce/plugins/wordcount'
// importing plugin resources
import 'tinymce/plugins/emoticons/js/emojis'
// Content styles, including inline UI like fake cursors
/* eslint import/no-webpack-loader-syntax: off */
// import contentCss from 'tinymce/skins/content/dark/content.css'
import contentCss from '!!raw-loader!tinymce/skins/content/default/content.min.css'
import contentUiCss from '!!raw-loader!tinymce/skins/ui/oxide/content.min.css'
export default function BundledEditor(props) {
const { init, ...rest } = props
// note that skin and content_css is disabled to avoid the normal
// loading process and is instead loaded as a string via content_style
return (
<Editor
init={{
...init,
skin: false,
content_css: false,
content_style: [contentCss, contentUiCss, init.content_style || ''].join('\n')
}}
{...rest}
/>
)
}
我只是将其导入到其他文件中,如下所示:
import BundledEditor from './BundledEditor';
可以,但是如果刷新,页面会显示这个错误:
ReferenceError:导航器未定义
所以我像这样改变导入方式:
// remove ssr, otherwise will cause ReferenceError: navigator is not defined
const BundledEditor = dynamic(() => import('../BundledEditor'), {
ssr: false
})
然后解决问题。
但是当我尝试输入
yarn build
来检查项目时。
显示错误:
info - Collecting page data ...ReferenceError: navigator is not defined
at /Users/motogod19/4iDPS/Sphere/sphere-brand-dev/node_modules/tinymce/tinymce.js:961:23
at Object.<anonymous> (/Users/motogod19/4iDPS/Sphere/sphere-brand-dev/node_modules/tinymce/tinymce.js:31525:3)
at Module._compile (node:internal/modules/cjs/loader:1254:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
at Module.load (node:internal/modules/cjs/loader:1117:32)
at Module._load (node:internal/modules/cjs/loader:958:12)
at Module.require (node:internal/modules/cjs/loader:1141:19)
at require (node:internal/modules/cjs/helpers:110:18)
at 3868 (/Users/motogod19/4iDPS/Sphere/sphere-brand-dev/.next/server/pages/apps/campaign/BundledEditor.js:28:33)
at __webpack_require__ (/Users/motogod19/4iDPS/Sphere/sphere-brand-dev/.next/server/webpack-runtime.js:25:42)
> Build error occurred
Error: Failed to collect page data for /apps/campaign/BundledEditor
我不熟悉 Next.js,我应该做任何动态导入吗?
navigator is not defined
表示您正在捆绑服务器端的代码,服务器端没有与浏览器相关的 API。您需要为客户端(即浏览器)捆绑它。
问题是因为
tinymce
不支持ssr(从https://github.com/tinymce/tinymce/issues/3709可以明显看出)
由于您使用的是 Next.js 13 和
pages
路由器,请将您的 BundleEditor.jsx
更改为以下内容:
// ** https://www.tiny.cloud/docs/tinymce/6/react-pm-bundle/ **//
import React from 'react';
import { Editor } from '@tinymce/tinymce-react';
if (typeof window !== 'undefined') {
require('tinymce/tinymce');
require('tinymce/models/dom/model');
require('tinymce/themes/silver');
require('tinymce/icons/default');
require('tinymce/plugins/advlist');
require('tinymce/plugins/anchor');
require('tinymce/plugins/autolink');
require('tinymce/plugins/autoresize');
require('tinymce/plugins/autosave');
require('tinymce/plugins/charmap');
require('tinymce/plugins/code');
require('tinymce/plugins/codesample');
require('tinymce/plugins/directionality');
require('tinymce/plugins/emoticons');
require('tinymce/plugins/fullscreen');
require('tinymce/plugins/help');
require('tinymce/plugins/image');
require('tinymce/plugins/importcss');
require('tinymce/plugins/insertdatetime');
require('tinymce/plugins/link');
require('tinymce/plugins/lists');
require('tinymce/plugins/media');
require('tinymce/plugins/nonbreaking');
require('tinymce/plugins/pagebreak');
require('tinymce/plugins/preview');
require('tinymce/plugins/quickbars');
require('tinymce/plugins/save');
require('tinymce/plugins/searchreplace');
require('tinymce/plugins/table');
require('tinymce/plugins/template');
require('tinymce/plugins/visualblocks');
require('tinymce/plugins/visualchars');
require('tinymce/plugins/wordcount');
require('tinymce/plugins/emoticons/js/emojis')
}
// Editor styles
import 'tinymce/skins/ui/oxide/skin.min.css';
// Content styles, including inline UI like fake cursors
/* eslint import/no-webpack-loader-syntax: off */
// import contentCss from 'tinymce/skins/content/dark/content.css'
import contentCss from '!!raw-loader!tinymce/skins/content/default/content.min.css';
import contentUiCss from '!!raw-loader!tinymce/skins/ui/oxide/content.min.css';
export default function BundledEditor(props) {
const { init, ...rest } = props;
// note that skin and content_css is disabled to avoid the normal
// loading process and is instead loaded as a string via content_style
return (
<Editor
init={{
...init,
skin: false,
content_css: false,
content_style: [contentCss, contentUiCss, init?.content_style || ''].join('\n'),
}}
{...rest}
/>
);
}
npm run dev
和 npm run build
都可以正常工作。
$ npm run build
> [email protected] build
> next build
- info Linting and checking validity of types
- info Creating an optimized production build
- info Compiled successfully
- info Collecting page data
- info Generating static pages (4/4)
- info Finalizing page optimization
Route (pages) Size First Load JS
┌ ○ / 3.39 kB 80.3 kB
├ └ css/00a586533d958f8e.css 1.73 kB
├ /_app 0 B 76.9 kB
├ ○ /404 182 B 77.1 kB
├ λ /api/hello 0 B 76.9 kB
└ ○ /BundledEditor 473 kB 550 kB
└ css/3191dae2698bb014.css 11.8 kB
+ First Load JS shared by all 77.6 kB
├ chunks/framework-63157d71ad419e09.js 45.2 kB
├ chunks/main-c6c319de9f7d0316.js 29.4 kB
├ chunks/pages/_app-5fbdfbcdfb555d2f.js 296 B
├ chunks/webpack-1f3c29ac3f0dceed.js 2.06 kB
└ css/876d048b5dab7c28.css 706 B
λ (Server) server-side renders at runtime (uses getInitialProps or getServerSideProps)
○ (Static) automatically rendered as static HTML (uses no initial props)
您收到该错误是因为
tinymce
似乎对服务器端渲染不友好,因为在组件主体或导入时执行浏览器特定代码。也就是说,根本不建议使用其他答案中建议的typeof window !== 'undefined'
,因为它很可能会导致水合错误。
使用
dynamic
导入和 ssr:false
应该可以。在您的情况下,它没有,因为您在 BundledEditor
目录中有 pages
,所以它被视为 /BundledEditor
的页面,导致另一个错误。
只需将
BundledEditor
移到 pages
之外,例如在 components
文件夹中,与 pages
处于同一级别,然后像这样导入它:
// pages/index.tsx
import dynamic from "next/dynamic";
const DynamicBundledEditor = dynamic(() => import("../../components/BundledEditor"), {
ssr: false,
});