1、服务端渲染时 总是会 报Element is not defined 在网上找一堆资料 说是要这样
<ClientOnly>
<rich-editor @changeHtml="changeHtml" />
</ClientOnly>
rich-editor 是我封装的一个wangeditor5 的组件 但是仍然保错,后面去nuxt3看文档 在RichEditor.vue 文件名 改为RichEditor.client.vue 就没有再报错了
2、 以下是我的rich-editor 组件的代码
<template>
<div class="editor" v-loading.fullscreen.lock="fullLoading">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
mode="default"
/>
<Editor
style="height: 300px; overflow-y: hidden"
v-model="valueHtml"
:defaultConfig="editorConfig"
mode="default"
@onCreated="handleCreated"
@onChange="returnHtml"
/>
</div>
</template>
<script setup>
// import { DomEditor } from '@wangeditor/editor'
import { i18nChangeLanguage } from '@wangeditor/editor'
import '@wangeditor/editor/dist/css/style.css' // 引入 css
const { Editor, Toolbar } = await import('@wangeditor/editor-for-vue')
const lang = useCookie('lang').value
if (lang !== 'cn') {
i18nChangeLanguage('en')
}
console.log('lang', lang)
const emit = defineEmits(['changeHtml'])
// 上传显示loading
const fullLoading = ref(false)
// 获取token
const token = useCookie('token').value
// 工具栏
const toolBarComponent = ref(null)
// 编辑组件
const editorComponent = ref(null)
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef(null)
// 内容 HTML
const valueHtml = ref()
// 工具栏默认配置
const toolbarConfig = {
excludeKeys: [
'headerSelect',
'italic',
'insertVideo',
'deleteVideo',
'uploadVideo',
'video',
'linkImage',
'uploadImageLink',
'insertLink',
'viewImageLink',
'group-more-style' // 排除菜单组,写菜单组 key 的值即可
]
}
const {
public: { api }
} = useRuntimeConfig()
// 输入框配置
const editorConfig = {
placeholder: lang === 'cn' ? '请输入内容...' : 'Please input',
readOnly: false,
showLinkImg: false,
excludeMenus: ['emoticon', 'video'],
MENU_CONF: {
showLinkImg: false,
// 上传图片
uploadImage: {
showLinkImg: false,
fieldName: 'multipartFile',
// 上传地址
server: `${api}`,
// 超时时间,默认为 10 秒
timeout: 30 * 1000, // 5s
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: false,
// 自定义上传参数。
meta: {
},
// 跨域是否传递 cookie ,默认为 false
withCredentials: true,
// 自定义增加 http header
headers: {
},
// 限制图片大小
maxFileSize: 10 * 1024 * 1024, // 10M
// 如果文件大小小于5kb,请插入base64格式
base64LimitSize: 5 * 1024,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ['image/*'],
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: 1,
// 上传之前触发
onBeforeUpload(file) {
// console.log("onBeforeUpload", file);
// 显示loading
fullLoading.value = true
return file
},
// 上传进度的回调函数 progress 是 0-100 的数字
onProgress(progress) {
// console.log("onProgress", progress);
},
// 单个文件上传成功之后
onSuccess(file, res) {
// console.log("onSuccess", file, res);
},
// 单个文件上传失败
onFailed(file, res) {
// console.log("onFailed", file, res);
},
// 上传错误,或者触发 timeout 超时
onError(file, err, res) {
// console.error("onError", file, err, res);
},
// 自定义插入图片 res 服务器返回的数据 insertFn 方法
customInsert(res, insertFn) {
// 隐藏loading
fullLoading.value = false
insertFn(res.data.url)
}
},
uploadVideo: false
// 上传视频
// uploadVideo: {
// fieldName: 'file',
// // 上传地址
// server: `${baseUrl}file/upload`,
// // 超时时间,默认为 30 秒
// timeout: 3000 * 1000, // 15 秒
// // 将 meta 拼接到 url 参数中,默认 false
// metaWithUrl: false,
// // 自定义上传参数。
// meta: {
// type: 3
// },
// // 跨域是否传递 cookie ,默认为 false
// withCredentials: true,
// // 自定义增加 http header
// headers: {
// Authorization: `Bearer ${token}`
// },
// // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
// allowedFileTypes: ['video/*'],
// // 最多可上传几个文件,默认为 5
// maxNumberOfFiles: 1,
// // 单个文件的最大体积限制,默认为 10M
// maxFileSize: 1200 * 1024 * 1024, // 1200M
// // 上传之前触发
// onBeforeUpload(file) {
// // console.log("onBeforeUpload", file);
// // 显示loading
// fullLoading.value = true
// return file
// },
// // 上传进度的回调函数
// onProgress(progress) {
// // console.log("progress", progress);
// },
// // 单个文件上传成功之后
// onSuccess(file, res) {
// // console.log(`${file.name} 上传成功`, res);
// },
// // 单个文件上传失败
// onFailed(file, res) {
// // console.log(`${file.name} 上传失败`, res);
// },
// // 上传错误,或者触发 timeout 超时
// onError(file, err, res) {
// // console.log(`${file.name} 上传出错`, err, res);
// },
// // 自定义插入视频 res 服务器返回的数据 insertFn 方法
// customInsert(res, insertFn) {
// // 隐藏loading
// fullLoading.value = false
// insertFn(res.url)
// }
// }
}
}
// console.log('editorRef', editorRef.value, 'editorConfig', editorConfig)
// 动态组件赋值
if (process.client) {
const { Editor, Toolbar } = await import('@wangeditor/editor-for-vue')
editorComponent.value = Editor
toolBarComponent.value = Toolbar
// console.log('toolBarComponent', toolBarComponent.value)
}
// 记录 editor 实例,重要!
const handleCreated = (editor) => {
editorRef.value = Object.seal(editor)
// console.log(editorRef.value, 'editorComponent.value=', editorComponent.value)
// const toolbar = DomEditor.getToolbar(editorRef.value)
// const curToolbarConfig = toolbar.getConfig()
// console.log(124, curToolbarConfig.toolbarKeys) // 当前菜单排序和分组
}
// 返回内容
const returnHtml = async () => {
emit('changeHtml', valueHtml.value)
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
</script>
<style scoped lang="scss">
.editor {
border: 1px solid #ccc;
}
:deep(.w-e-text-placeholder) {
top: 10px;
}
</style>