React中使用UEditor百度富文本的方法
前言
本文將介紹筆者在React的項(xiàng)目中使用百度的富文本編輯器Ueditor的過(guò)程。注意本文不提供一條龍式的使用方法,只是將使用過(guò)程中的一些實(shí)現(xiàn)思路進(jìn)行總結(jié),供以參考。react項(xiàng)目中導(dǎo)入ueditor,會(huì)存在各種不正交的問(wèn)題,需要注意。
引入
首先在ueditor官網(wǎng)下載最新安裝包,然后在項(xiàng)目入口的html中導(dǎo)入(導(dǎo)入方式不一,可以采用import的方式,需要自行度娘。但是無(wú)論哪種引入方式,只要想自定義功能,不正交問(wèn)題就難以避免QAQ)。不管三七二十一先跑起來(lái)再說(shuō)。。
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8"> <title>ueditor demo</title> </head> <body> <!-- 配置文件 --> <script type="text/javascript" src="path/ueditor.config.js"></script> <!-- 編輯器源碼文件 --> <script type="text/javascript" src="path/ueditor.all.js"></script> ······ </body> </html>
在React項(xiàng)目中使用ueditor要注意
- 導(dǎo)入的路徑,筆者使用的是項(xiàng)目經(jīng)webpack打包之后的相對(duì)路徑。
- 導(dǎo)入順序,配置文件要先于源碼。
- 筆者這種引入方式存在緩存問(wèn)題,所以修改ueditor.all.js后需要及時(shí)清理緩存,測(cè)試新的代碼。
封裝
/**
* 封裝UEditor
*/
import React from 'react';
import './index.less';
class UEditor extends React.Component {
constructor(props) {
super(props);
this.editor = {};
this.id = '';
}
······
componentDidMount() {
let UE = window.UE;
let id = this.id;
if (id) {
try {
/* 加載之前先執(zhí)行刪除操作,否則如果存在頁(yè)面切換,
再切回帶編輯器頁(yè)面重新加載時(shí)不刷新無(wú)法渲染出編輯器 */
UE.delEditor(id);
} catch (e) {}
let ueditor = UE.getEditor(id, {
toolbars: [
['bold', 'italic', 'underline', 'kityformula', 'diyimg']
],
initialContent: '',
autoHeightEnabled: false,
autoFloatEnabled: false,
elementPathEnabled: false,
wordCount: false,
enableAutoSave: false,
initialFrameWidth: this.props.width,
initialFrameHeight: this.props.height
});
}
}
render() {
this.id = this.props.id;
return <div styleName="content" id={this.id} />;
}
}
export default UEditor;
筆者在項(xiàng)目中使用了加粗,斜體,下劃線,插入圖片,公式等功能,想要自定義配置均可參照ueditor.config.js修改。具體的將一一介紹,最后實(shí)現(xiàn)效果如下:

問(wèn)題總結(jié):
1. 禁止自動(dòng)增高,改用滾動(dòng)條
autoHeightEnabled: false initialFrameWidth:this.props.width initialFrameHeight:this.props.height
autoHeightEnabled可以阻止自動(dòng)增高,然后再自定義容器寬度和高度。
2. 自定義全局樣式,如容器的padding,p標(biāo)簽的line-height等
解決方法:ueditor.all.js的第6800多行的render方法,在其中可以自定義全局樣式。

3. 導(dǎo)航條切換后,無(wú)法再次渲染
解決方法:在每次ueditor實(shí)例化之前,先刪除對(duì)應(yīng)的id
UE.delEditor(id);
原因分析:
從實(shí)例化和卸載實(shí)例的源碼來(lái)看:
getEditor:
UE.getEditor = function (id, opt) {
var editor = instances[id];
if (!editor) {
editor = instances[id] = new UE.ui.Editor(opt);
editor.render(id);//渲染編輯器
}
return editor;
};
delEditor:
UE.delEditor = function (id) {
var editor;
if (editor = instances[id]) {
editor.key && editor.destroy();
delete instances[id]
}
};
UE在全局管理了一個(gè)實(shí)例池,每次實(shí)例化都會(huì)根據(jù)id檢索,然后生成實(shí)例。從getEditor的源碼中可以看出,ueditor的一個(gè)實(shí)例在第一次初始化時(shí)存在一個(gè)editor.render(),這是將此id的實(shí)例渲染到對(duì)應(yīng)的id容器上。然而,當(dāng)用戶tab切換編輯器再切回來(lái)時(shí),此時(shí)由于該實(shí)例已在實(shí)例池中存在,于是直接執(zhí)行return editor,所以少了editor.render()這一步,于是不能重新渲染。所以,在Ueditor組件每次實(shí)例化之前,先進(jìn)行delEditor卸載。這里需要注意,從delEditor中可以看出ueditor卸載實(shí)例時(shí)調(diào)用了實(shí)例的destroy方法。從destroy的注釋來(lái)看:銷毀編輯器實(shí)例,使用textarea代替 ,這解釋了為什么在切換編輯器或者卸載編輯器時(shí),會(huì)出現(xiàn)編輯器變?yōu)閠extarea的情況,如圖所示:

4. 模擬placeholder實(shí)現(xiàn)預(yù)置文案
解決方法:在UE的實(shí)例中自定義方法,實(shí)現(xiàn)填充文字模擬placeholder的效果,代碼如下:
//模擬placeholder和控制toolbar顯示隱藏
UE.Editor.prototype.initDiy = function (placeholder) {
var _editor = this;
//獲取焦點(diǎn)
_editor.addListener("focus", function () {
UE.isEditored = true;
var Text = `<p style="color: #CDCDCD">${placeholder}</p>`
var localHtml = _editor.getContent();
if (localHtml === Text) {
_editor.setContent("");//點(diǎn)擊時(shí)清空
_editor.focus(true);
}
//使得其他工具條display置為none
var list = document.querySelectorAll('.edui-editor-toolbarbox');
list.forEach((ele) => {
ele.style.display = 'none';
});
var toolbar = findKey(_editor.key);
toolbar.style.display = 'block';
});
// 插入圖片時(shí)存在問(wèn)題
// _editor.addListener("blur", function () {
// var localHtml = _editor.getContent();
// if (localHtml === '') {
// _editor.setContent(`<p style="color: #CDCDCD">${placeholder}</p>`);
// }
// // window.activeEditor = _editor.key;
// });
_editor.ready(function () {
// _editor.fireEvent("blur");
_editor.setContent(`<p style="color: #CDCDCD">${placeholder}</p>`);//填充預(yù)置文案
});
}
//尋找工具條
function findKey(key) {
let ele = document.querySelector(`#${key}`);
let toolbar = ele.querySelector('.edui-editor-toolbarbox');
return toolbar;
}
原來(lái),筆者實(shí)現(xiàn)的效果是點(diǎn)擊時(shí)清空,失焦時(shí)還原。但是,在做自定義工具條時(shí)產(chǎn)生了bug(在5中我會(huì)細(xì)說(shuō)),因此我采用了另一種方案:初始時(shí)設(shè)置預(yù)設(shè)文案,當(dāng)用戶聚焦時(shí)清空預(yù)設(shè),用戶失焦后不再恢復(fù)該預(yù)設(shè)文案。也就是將blur事件注釋了。。。
5. 工具條顯示在編輯器頭部,顯示為懸浮效果,默認(rèn)隱藏,聚焦時(shí)出現(xiàn)
實(shí)現(xiàn)思路:將themes/default/css/ueditor.css中加入:
.edui-default .edui-editor-toolbarbox {
position: absolute;
······
top: -36px;
}
首先實(shí)現(xiàn)頭部偏移,然后通過(guò)控制toolbar對(duì)應(yīng)dom元素的display來(lái)隱藏工具條。實(shí)現(xiàn)效果如下:

下面解釋一下為什么編輯器失焦的時(shí)候不恢復(fù)預(yù)置文案:
從4中的代碼可以看出,我們是通過(guò)觸發(fā)focus和blur事件分別清空和填充編輯器的內(nèi)容。但是當(dāng)我們點(diǎn)擊工具條時(shí),編輯器就會(huì)觸發(fā)blur事件??!于是就會(huì)出現(xiàn)各種bug。以百度官網(wǎng)的ueditor為例,控制臺(tái)輸入:

為該編輯器注冊(cè)點(diǎn)擊事件,當(dāng)點(diǎn)擊加粗按鈕時(shí),控制臺(tái)輸出:

為了避免點(diǎn)擊工具條時(shí)觸發(fā)blur事件,筆者將自定義的blur事件全部注釋了。
6. 自定義按鈕和七牛云圖片上傳
首先,在ueditor.config.js中找到toolbars數(shù)組,增加一個(gè)diyimg字符串,然后在zh-cn.js找到labelMap數(shù)組,在末尾加上'diyimg': '插入圖片' 。最后,在ueditor.all.js中找到btnCmds數(shù)組,加入diyimg字符串。初始化時(shí)使用這個(gè)字符串,工具條上就會(huì)顯示一個(gè)按鈕,但是我們發(fā)現(xiàn)他顯示的是這樣的:

這是因?yàn)閡editor默認(rèn)使用加粗的icon作為自定義按鈕的默認(rèn)icon,所以為了使用默認(rèn)的插入圖片的圖標(biāo),我們需要到themes/default/css/ueditor.css中,在最后一行加入:
/*自定義圖片上傳按鈕 */
.edui-default .edui-toolbar .edui-for-diyimg .edui-icon {
background-position: -380px 0px;//這個(gè)位置是“插入圖片”的icon,其他圖標(biāo)可自行調(diào)整
}
添加后,顯示效果如下:

圖標(biāo)正常顯示后,需要為該圖標(biāo)添加相應(yīng)的點(diǎn)擊事件,在ueditor.all.js中加入:
//圖片上傳
UE.commands['diyimg'] = {
execCommand : function(){
const upload = async(e) => {
······//完成圖片上傳的代碼
}
const fileInput = document.getElementById('diyimg');//獲取dom上隱藏的一個(gè)input標(biāo)簽
fileInput.onchange = upload;
fileInput.click();//觸發(fā)input標(biāo)簽實(shí)現(xiàn)文件上傳
return true;
},
queryCommandState:function(){
}
};
筆者這里不贅述圖片上傳的代碼,度娘上很多,我簡(jiǎn)單說(shuō)說(shuō)實(shí)現(xiàn)的思路:
先實(shí)現(xiàn)一個(gè)插入圖片的按鈕,然后為該按鈕注冊(cè)相應(yīng)的事件diyimg,然后在頁(yè)面中添加一個(gè)input file標(biāo)簽并隱藏,diyimg事件會(huì)觸發(fā)該標(biāo)簽的點(diǎn)擊事件,彈出文件上傳彈窗,此時(shí)選擇文件點(diǎn)擊后會(huì)觸發(fā)onchange事件,執(zhí)行相應(yīng)的圖片上傳代碼。上傳成功到服務(wù)器后,服務(wù)器會(huì)返回圖片對(duì)應(yīng)的url,此時(shí)拿到該url填入對(duì)應(yīng)編輯器實(shí)例,執(zhí)行編輯器的插入圖片的代碼:
this.execCommand('insertimage', {
src: res.data.downloadUrl,//回調(diào)傳來(lái)的url
width:'60'
// height:'45'
});
7. 給在編輯器內(nèi)部的img等標(biāo)簽添加內(nèi)聯(lián)樣式
ueditor默認(rèn)存在xss過(guò)濾?。?!這里以給img標(biāo)簽添加style=“vertical-top”為例。
首先要找到ueditor.config.js,在其中搜索xss,在第403行左右有代碼:
img: [src', 'alt', 'title', 'width', 'height', 'id', '_src', 'loadingclass', 'class', 'data-latex'],
往數(shù)組里加入style字符串,然后在ueditor.all.js中搜索UE.commands['insertimage'] ,在第約11172行找到str,往里面加入內(nèi)聯(lián)樣式即可。
一些吐槽:
1. 在react項(xiàng)目里使用script形式引入,感覺(jué)格格不入
2. 為了實(shí)現(xiàn)placeholder,各個(gè)事件之間存在不正交的現(xiàn)象。諸如點(diǎn)擊按鈕,卻觸發(fā)了編輯器的失焦事件
3. 在使用自定義的字?jǐn)?shù)限制功能時(shí),筆者使用ueditor的contentChange去檢測(cè)內(nèi)容字?jǐn)?shù),但是contentChange事件是定時(shí)的,所以計(jì)算字?jǐn)?shù)會(huì)有問(wèn)題。
實(shí)現(xiàn)中的問(wèn)題及解決方法
1.上傳圖片時(shí)的跨域問(wèn)題
在源碼里 有 header['X_Requested_With'] = 'XMLHttpRequest';
后端需要配置 header('Access-Control-Allow-Headers', 'AccessToken, Content-Type, WebOrigin,X-Requested-With,X_Requested_With');
2. 進(jìn)入文本編輯界面編輯器沒(méi)有加載出來(lái)
可能原因: 放置編輯器的容器id, 容器下的編輯器已經(jīng)存在
解決方法如代碼所示
this.editor.ready(function (ueditor) {
if (!ueditor) {
// 如果初始化后ueditor不存在刪除后重新調(diào)用
UE.delEditor(self.props.id);
self.initEditor();
}
})
3.注意這兩個(gè)參數(shù)配置
'UEDITOR_HOME_URL': '/react/dist/ueditor/', // 編輯器實(shí)例路徑,即ueditor文件包所放置的位置
serverUrl: window.api_host + '/innerMessage/uploadImage', // 后端提供加載圖片接口,這是個(gè)共同接口接口包括了后端配置的config.json文件 通過(guò)url中action
值不同來(lái)區(qū)分(config|uploaimage 等)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
可定制react18 input otp 一次性密碼輸入組件
這篇文章主要為大家介紹了可定制react18 input otp 一次性密碼輸入組件,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
React自定義視頻全屏按鈕實(shí)現(xiàn)全屏功能
這篇文章主要介紹了React自定義視頻全屏按鈕實(shí)現(xiàn)全屏功能,通過(guò)繪制全屏按鈕,并綁定點(diǎn)擊事件,編寫(xiě)點(diǎn)擊事件,通過(guò)實(shí)例代碼給大家詳細(xì)講解,需要的朋友可以參考下2022-11-11
React中映射一個(gè)嵌套數(shù)組實(shí)現(xiàn)demo
這篇文章主要為大家介紹了React中映射一個(gè)嵌套數(shù)組實(shí)現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
使用hooks寫(xiě)React組件需要注意的5個(gè)地方
這篇文章主要介紹了使用hooks寫(xiě)React組件需要注意的5個(gè)地方,幫助大家更好的理解和學(xué)習(xí)使用React組件,感興趣的朋友可以了解下2021-04-04
react-native 實(shí)現(xiàn)購(gòu)物車滑動(dòng)刪除效果的示例代碼
這篇文章主要介紹了react-native 實(shí)現(xiàn)購(gòu)物車滑動(dòng)刪除效果的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
react render的原理及觸發(fā)時(shí)機(jī)說(shuō)明
這篇文章主要介紹了react render的原理及觸發(fā)時(shí)機(jī)說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02

