使用react開發前端項目,我們常會遇到需要在一個dom里直接篩入一段純html(后端給的)的需求,比如我們的編輯器,文章的初始內容是一段通過ajax獲取的html。當然這么一個常見的需求,react當然幫你想好了解決方案,那就是dangerouslySetInnerHTML
,但是就是使用上有點丑陋,需要這樣寫dangerouslySetInnerHTML={{ __html: content }}
。
dangerouslySetInnerHTML
有時候也會顯得有點捉襟,我們看這樣一個需求:我不但需要直接在dom里插入純html,我在插入前需要對這段html的元素做些處理,比如需要在每一個元素里再插入一個元素,或者需要給里面的元素綁定事件。
這種需求用dangerouslySetInnerHTML
來實現的話,我們就需要在插入html前對html手動處理下,寫一個原生的方法,遍歷每一個元素并插入我們需要的元素,最后返回一段處理好的新的html,然后每一次變化都重新處理重新設置和更新整塊html,這樣的弊端有兩個:
- 就是對于一個需求我需要實現一個處理dom的工具方法,n個需求我可能需要實現n方法。
- 沒有利用到react的局部更新的優勢,每次都是整塊更新。
對于需要綁定事件的需求來說,dangerouslySetInnerHTML
方式就顯得更加拙劣了。首先我們在 componentDidMount
時候,使用findDomNode的方式給指定dom綁定原生事件,或者通過事件代理的方式,給父元素綁定事件,通過target來識別指定元素。這還是在使用react么?這樣弊端也很明顯:
- 原生事件機制與react事件機制混用,不易維護、把握不好容易出岔子。
- 通過target綁定,有時候并不是這么好判定。
我們使用react的話,肯定是希望:1. 盡量少些原生代碼(寫的話也應該是寫個工具或者lib之類的),2. 充分利用react的優勢,3. dom和事件都交給react來管理,不需要另搞一套。
所以我們當時就在想,如果想要實現需求,又不另外搞一套,那就需要把html轉成react element,最后通過jsx語法渲染出來,這樣就完全融入到你react整個框架中,很清爽,也很靈活啊。
后來我發現早有高人寫好了這樣的庫,讓html直接主換成react element(component),比如htmr這個庫。其實這樣的庫實現也很簡單,就是利用React.createElement
方法把一個個的dom轉成了react element。
這里貼一下怎么使用這個庫吧
import convert from 'htmr';
render() {
return (
// react一樣可以玩dom啊
convert(html).map((reactElement, k) => {
// 不需要處理的元素就可以直接返回
if (element.type === 'hr') {
return element;
}
// 需要處理的元素,通過jsx語法展開渲染,方便添加事件和子元素
return (
<element.type {...element.props}
key={k}
>
{element.props.children}
{/* 以下就是 新增加的子元素,并給他綁定了事件 */}
<span className={maskItemClass}
onClick={this.onMaskItemClickReact}
data-index={k}
>
<em></em>
</span>
</element.type>
);
})
);
}
這樣使用后就完全不需要寫額外的原生代碼,整個dom和事件都交給了react,處理上要靈活許多哈。
標題問的還能怎么辦?其實可以總結一句話:我們還能把html轉成react element并用jsx語法渲染。