最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React封裝UEditor富文本編輯器的實現(xiàn)步驟

 更新時間:2025年12月29日 08:27:42   作者:萌新夢  
本文主要介紹了React封裝UEditor富文本編輯器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

UEditor 作為經(jīng)典富文本編輯器,在后臺系統(tǒng)中仍有廣泛應(yīng)用,但原生 UEditor 與 React 生態(tài)適配性差。本文剝離業(yè)務(wù)接口和冗余配置,聚焦核心封裝邏輯,拆解從實例管理到圖片處理的關(guān)鍵實現(xiàn)思路。

一、基礎(chǔ)架構(gòu):核心設(shè)計思路

1. 類型與狀態(tài)管理(核心代碼片段)

import React, { useEffect, useRef, forwardRef, useImperativeHandle, useCallback } from 'react';
 
// 核心狀態(tài)管理(非響應(yīng)式狀態(tài)用useRef避免重渲染)
const UEditor = forwardRef((props, ref) => {
    const editorContainer = useRef<HTMLDivElement>(null); // 編輯器容器
    const ueInstance = useRef<any>(null); // UEditor實例
    const isPlaceholderActive = useRef(false); // 占位符狀態(tài)
    const isUploading = useRef(false); // 上傳狀態(tài)標(biāo)記
    
    // 解構(gòu)核心props(僅保留通用配置)
    const {
        value = '',
        onChange,
        style,
        placeholder = '請輸入內(nèi)容...',
        onBlur
    } = props;
 
    // 對外暴露核心方法(關(guān)鍵:讓父組件能操作編輯器)
    useImperativeHandle(ref, () => ({
        getContent: () => {
            const content = ueInstance.current?.getContent() || '';
            return isPlaceholderActive.current ? '' : content;
        },
        setContent: (content: string) => {
            // 內(nèi)容設(shè)置邏輯(后文詳解)
        },
        forceImageFixedSize: () => {
            // 圖片尺寸修正邏輯(后文詳解)
        }
    }));
});

關(guān)鍵設(shè)計點

  • forwardRef+useImperativeHandle暴露編輯器核心方法,滿足父組件自定義操作需求;
  • useRef管理 UEditor 實例、狀態(tài)標(biāo)記等非響應(yīng)式數(shù)據(jù),避免狀態(tài)變化觸發(fā)組件重渲染;
  • 僅保留通用 Props,剝離業(yè)務(wù)化配置,保證組件通用性。

二、核心功能:關(guān)鍵痛點解決

1. 實例生命周期管理(避免內(nèi)存泄漏)

// 動態(tài)加載UEditor腳本(核心:按需加載,避免重復(fù)加載)
const loadScript = useCallback((src: string) => {
    return new Promise((resolve, reject) => {
        if (document.querySelector(`script[src="${src}"]`)) return resolve(true);
        
        const script = document.createElement('script');
        script.src = src;
        script.onload = () => resolve(true);
        script.onerror = () => reject(new Error(`加載失敗: ${src}`));
        document.body.appendChild(script);
    });
}, []);
 
// 安全銷毀編輯器(關(guān)鍵:卸載時徹底清理)
const safeDestroyEditor = useCallback(() => {
    if (!ueInstance.current) return;
    
    try {
        // 移除所有事件監(jiān)聽器
        const listeners = ['contentChange', 'blur', 'afterUpload'];
        listeners.forEach(event => {
            ueInstance.current.removeAllListeners?.(event);
        });
        // 從UEditor全局實例中移除
        window.UE?.delEditor(ueInstance.current.key);
        ueInstance.current = null;
    } catch (error) {
        console.warn('UEditor銷毀失敗:', error);
    }
}, []);
 
// 生命周期綁定(核心:組件掛載初始化,卸載銷毀)
useEffect(() => {
    // 初始化邏輯(后文詳解)
    initEditor();
    return () => safeDestroyEditor();
}, [initEditor, safeDestroyEditor]);

關(guān)鍵要點

  • 動態(tài)加載腳本:避免 UEditor 資源全局引入,按需加載且防止重復(fù)加載;
  • 銷毀邏輯:不僅銷毀實例,還要移除所有事件監(jiān)聽器,徹底避免內(nèi)存泄漏;
  • 生命周期綁定:通過useEffect將實例創(chuàng)建 / 銷毀與組件生命周期嚴(yán)格同步。

2. 圖片樣式控制(解決尺寸混亂問題)

// 圖片尺寸統(tǒng)一修正(核心:清除原生樣式,應(yīng)用響應(yīng)式規(guī)則)
const safeImageSizeFix = useCallback((force = false) => {
    if (!ueInstance.current) return;
    
    try {
        const doc = ueInstance.current.document || document;
        const imgElements = doc.body.querySelectorAll('img');
        
        imgElements.forEach((img: HTMLImageElement) => {
            // 清除UEditor原生添加的寬高樣式/屬性
            img.style.width = '';
            img.style.height = '';
            img.removeAttribute('width');
            img.removeAttribute('height');
            
            // 應(yīng)用統(tǒng)一的響應(yīng)式樣式
            img.style.maxWidth = '100%';
            img.style.height = 'auto';
        });
    } catch (error) {
        console.warn('圖片尺寸修正失敗:', error);
    }
}, []);
 
// 監(jiān)聽圖片插入事件(核心:插入/上傳后自動修正)
const setupImageHandlers = useCallback(() => {
    if (!ueInstance.current) return;
    
    // 圖片插入/上傳后觸發(fā)修正
    ueInstance.current.addListener('afterInsertImage', () => {
        setTimeout(() => safeImageSizeFix(true), 100);
    });
    
    // 覆蓋原生插入方法,確保樣式生效
    const originalInsertImage = ueInstance.current.insertImage;
    ueInstance.current.insertImage = function (...args) {
        const result = originalInsertImage.call(this, ...args);
        setTimeout(() => safeImageSizeFix(true), 50);
        return result;
    };
}, [safeImageSizeFix]);

核心解決思路

  • 清除原生樣式:UEditor 插入圖片會自動添加寬高屬性 / 樣式,需徹底清除;
  • 響應(yīng)式樣式:統(tǒng)一設(shè)置maxWidth: 100%+height: auto,保證圖片適配容器;
  • 事件監(jiān)聽:圖片插入 / 上傳后自動觸發(fā)修正,覆蓋原生方法確保無遺漏。

3. 占位符功能(解決上傳沖突)

// 設(shè)置占位符(核心:自定義樣式,標(biāo)記占位狀態(tài))
const setPlaceholderText = useCallback(() => {
    if (!ueInstance.current) return;
    const placeholderHtml = `<p style="color: #999; font-style: italic;">${placeholder}</p>`;
    ueInstance.current.setContent(placeholderHtml);
    isPlaceholderActive.current = true;
    onChange?.('');
}, [placeholder, onChange]);
 
// 上傳前激活編輯器(關(guān)鍵:避免占位符干擾上傳)
const activateEditorForUpload = useCallback(() => {
    if (!ueInstance.current || !isPlaceholderActive.current) return;
    
    // 清除占位符,插入零寬空格(有內(nèi)容但不顯示)
    ueInstance.current.setContent('<p>&#8203;</p>');
    isPlaceholderActive.current = false;
    isUploading.current = true;
    
    // 超時重置上傳狀態(tài)(安全措施)
    setTimeout(() => {
        isUploading.current = false;
    }, 3000);
}, [placeholder]);
 
// 焦點/失焦處理(核心:區(qū)分上傳狀態(tài),避免沖突)
const setupPlaceholderHandlers = useCallback(() => {
    if (!ueInstance.current) return;
    
    // 聚焦時清除占位符(非上傳場景)
    ueInstance.current.addListener('focus', () => {
        if (!isUploading.current && isPlaceholderActive.current) {
            ueInstance.current.setContent('');
            isPlaceholderActive.current = false;
        }
    });
    
    // 失焦時顯示占位符(非上傳場景)
    ueInstance.current.addListener('blur', () => {
        if (isUploading.current) return;
        
        const content = ueInstance.current.getContent();
        if (!content || content === '<p><br></p>') {
            setPlaceholderText();
        }
        onBlur?.();
    });
}, [setPlaceholderText, onBlur]);

核心痛點解決

  • 上傳沖突:占位符狀態(tài)下編輯器無實際內(nèi)容,上傳會失敗,需在上傳前插入零寬空格;
  • 狀態(tài)區(qū)分:標(biāo)記上傳狀態(tài),焦點 / 失焦邏輯中跳過占位符處理,避免干擾上傳;
  • 體驗優(yōu)化:占位符樣式自定義,失焦時自動顯示,聚焦時自動清除。

4. 內(nèi)容同步(React 式狀態(tài)管理)

// 初始化編輯器(核心:內(nèi)容初始化+事件監(jiān)聽)
const initEditor = useCallback(async () => {
    if (!editorContainer.current) return;
    
    // 加載UEditor核心腳本(ueditor.config.js/ueditor.all.min.js等)
    await loadScript('/UEditor/ueditor.config.js');
    await loadScript('/UEditor/ueditor.all.min.js');
    
    // 創(chuàng)建UEditor實例
    ueInstance.current = window.UE.getEditor(editorContainer.current.id, {
        initialFrameHeight: 240,
        autoHeightEnabled: false,
        enableAutoSave: false // 禁用自動保存,避免與React狀態(tài)沖突
    });
    
    // 編輯器就緒后初始化內(nèi)容和事件
    ueInstance.current.addListener('ready', () => {
        // 初始化內(nèi)容:有值則設(shè)置,無值則顯示占位符
        if (value) {
            ueInstance.current.setContent(value);
        } else {
            setPlaceholderText();
        }
        
        // 監(jiān)聽內(nèi)容變化,同步到React狀態(tài)
        ueInstance.current.addListener('contentChange', () => {
            if (isPlaceholderActive.current || isUploading.current) return;
            
            const content = ueInstance.current.getContent();
            onChange?.(content);
        });
        
        // 初始化圖片處理、占位符、上傳等邏輯
        setupImageHandlers();
        setupPlaceholderHandlers();
    });
}, [loadScript, value, onChange, setPlaceholderText]);
 
// 監(jiān)聽外部value變化(核心:同步父組件狀態(tài)到編輯器)
useEffect(() => {
    if (!ueInstance.current?.isReady) return;
    
    const currentContent = ueInstance.current.getContent();
    if (value !== currentContent && !isPlaceholderActive.current) {
        ueInstance.current.setContent(value);
        // 內(nèi)容變化后修正圖片尺寸
        setTimeout(() => safeImageSizeFix(true), 50);
    }
}, [value, safeImageSizeFix]);

核心同步邏輯

  • 編輯器→React:監(jiān)聽contentChange事件,過濾占位符 / 上傳狀態(tài),同步內(nèi)容到父組件;
  • React→編輯器:監(jiān)聽value變化,實時更新編輯器內(nèi)容,同時修正圖片尺寸;
  • 初始化:編輯器就緒后根據(jù)value初始化內(nèi)容,保證初始狀態(tài)一致。

三、封裝總結(jié):核心思路提煉

本次封裝并非重寫 UEditor,而是適配 React 生態(tài)規(guī)則,核心思路可總結(jié)為:

  1. 生命周期同步:實例創(chuàng)建 / 銷毀與組件掛載 / 卸載綁定,避免內(nèi)存泄漏;
  2. 狀態(tài)隔離:非響應(yīng)式狀態(tài)用useRef管理,響應(yīng)式狀態(tài)通過props/onChange同步;
  3. 痛點針對性解決
    • 圖片樣式:清除原生樣式 + 統(tǒng)一響應(yīng)式規(guī)則 + 事件監(jiān)聽自動修正;
    • 占位符:上傳前激活編輯器,區(qū)分上傳狀態(tài)避免沖突;
    • 內(nèi)容同步:雙向綁定,兼顧 React 狀態(tài)和 UEditor 原生內(nèi)容;
  4. 擴(kuò)展與通用:暴露核心方法,剝離業(yè)務(wù)配置,保證組件復(fù)用性。

該思路同樣適用于其他原生 JS 編輯器(如 CKEditor、KindEditor)向 React 的適配,核心是遵循 React 的開發(fā)范式,將原生庫能力封裝為符合 React 習(xí)慣的組件。

到此這篇關(guān)于React封裝UEditor富文本編輯器的實現(xiàn)步驟的文章就介紹到這了,更多相關(guān)React封裝UEditor 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • React中實現(xiàn)編輯框自動獲取焦點與失焦更新功能

    React中實現(xiàn)編輯框自動獲取焦點與失焦更新功能

    在React應(yīng)用中,編輯框的焦點控制和數(shù)據(jù)回填是一個常見需求,本文將介紹如何使用useRef和useEffect鉤子,在組件中實現(xiàn)輸入框自動獲取焦點及失焦后更新數(shù)據(jù)的功能,文中通過代碼示例給大家講解的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • 淺談React Native 中組件的生命周期

    淺談React Native 中組件的生命周期

    本篇文章主要介紹了淺談React Native 中組件的生命周期,非常具有實用價值,需要的朋友可以參考下
    2017-09-09
  • React組件的應(yīng)用介紹

    React組件的應(yīng)用介紹

    React組件分為函數(shù)組件與class組件;函數(shù)組件是無狀態(tài)組件,class稱為類組件;函數(shù)組件只有props,沒有自己的私有數(shù)據(jù)和生命周期函數(shù);class組件有自己私有數(shù)據(jù)(this.state) 和 生命周期函數(shù)
    2022-09-09
  • 淺談React 服務(wù)器端渲染的使用

    淺談React 服務(wù)器端渲染的使用

    本篇文章主要介紹了淺談React 服務(wù)器端渲染的使用,React是最受歡迎的客戶端 JavaScript 框架,在本教程中,我們將逐步向您介紹服務(wù)器端的渲染示例
    2018-05-05
  • 解決antd的Table組件使用rowSelection屬性實現(xiàn)多選時遇到的bug

    解決antd的Table組件使用rowSelection屬性實現(xiàn)多選時遇到的bug

    這篇文章主要介紹了解決antd的Table組件使用rowSelection屬性實現(xiàn)多選時遇到的bug問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • TypeScript在React項目中的使用實踐總結(jié)

    TypeScript在React項目中的使用實踐總結(jié)

    這篇文章主要介紹了TypeScript在React項目中的使用總結(jié),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • react性能優(yōu)化useMemo與useCallback使用對比詳解

    react性能優(yōu)化useMemo與useCallback使用對比詳解

    這篇文章主要為大家介紹了react性能優(yōu)化useMemo與useCallback使用對比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • react國際化化插件react-i18n-auto使用詳解

    react國際化化插件react-i18n-auto使用詳解

    這篇文章主要介紹了react國際化化插件react-i18n-auto使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • React中Context的用法總結(jié)

    React中Context的用法總結(jié)

    Context?提供了一種在組件樹中共享數(shù)據(jù)的方式,而不必通過?props?顯式地逐層傳遞,主要用于共享那些對于組件樹中許多組件來說是"全局"的數(shù)據(jù),下面小編就來和大家簡單講講Context的用法吧
    2025-01-01
  • React Router 中實現(xiàn)嵌套路由和動態(tài)路由的示例

    React Router 中實現(xiàn)嵌套路由和動態(tài)路由的示例

    React Router 是一個非常強(qiáng)大和靈活的路由庫,它為 React 應(yīng)用程序提供了豐富的導(dǎo)航和 URL 管理功能,能夠幫助我們構(gòu)建復(fù)雜的單頁應(yīng)用和多頁應(yīng)用,這篇文章主要介紹了React Router 中如何實現(xiàn)嵌套路由和動態(tài)路由,需要的朋友可以參考下
    2023-05-05

最新評論

兴山县| 沈阳市| 黑山县| 长海县| SHOW| 洞口县| 依兰县| 崇信县| 寻乌县| 平利县| 孙吴县| 安义县| 麦盖提县| 临清市| 莒南县| 宿松县| 德清县| 缙云县| 慈利县| 舟曲县| 通辽市| 连山| 通州区| 吉木萨尔县| 双峰县| 灌南县| 千阳县| 汝州市| 正镶白旗| 潞西市| 湘潭市| 南郑县| 平邑县| 汉中市| 鸡东县| 海盐县| 湖南省| 安丘市| 定日县| 曲靖市| 辽阳市|