在日常开发中,经常需要用到富文本编辑器来编辑业务内容,例如新闻、论坛等,然后在用到的地方进行渲染,一般在web端可以直接在html中载入编辑好的内容(一般是一段html),不过在react-native的app中不能直接把html渲染进去,接下来我介绍几种渲染html的方案。
正文1、使用已有的组件既然已经有人造好了轮子,我们就直接拿来用就可以了。可以在网上搜react-native富文本组件,可以有几个选择方案,我这边以react-native-render-html,npm地址:https://www.npmjs.com/package/react-native-render-html,
使用方法:importRenderHTML,{IMGElementContainer,useIMGElementProps,useIMGElementState,IMGElement}from"react-native-render-html";//其他代码..._previewImg=(src)=>{constimages=[{uri:src}];Overlay.show((<Overlay.PopViewcontainerStyle={{flex:1}}overlayOpacity={1}ref={v=>this.fullImageView=v}><AlbumViewstyle={{flex:1}}control={true}images={images}defaultIndex={0}onPress={()=>{this.fullImageView&&this.fullImageView.close()}}/></Overlay.PopView>));};_imagesNode=(props)=>{constimgElementProps=useIMGElementProps(props);return(<IMGElementonPress={this._previewImg.bind(this,imgElementProps.source.uri)}source={imgElementProps.source}contentWidth={imgElementProps.contentWidth-15}/>);};render(){conststyles=this.styles;constProps=this.props;return(<RenderHTMLsource={{html:this._handleContent(Props.data)}}renderers={{img:this._imagesNode}}tagsStyles={{p:{marginVertical:5}}}{...Props}/>);}//其他代码...这个组件的原理就是把富文本的html标签一个个的解析出来,转换成react-native的标签,再进行渲染就可以了。
注意:如果需要点击放大预览的话,图片需要单独处理。
2、使用webview我们除了直接使用已有的组件之外,还有可以使用webview进行渲染。webview就相当于嵌在App里面的浏览器一样,可以直接访问和渲染html代码。其中webview载入html资源还有两种方法:
(1)直接在source上写html,然后把内容(data)放进去:importWebViewfrom'react-native-webview'//其他代码...render(){constProps=this.props;const{webHeight}=this.state;letdata=this._handleContent(Props.data);return<WebViewsource={{html:`<html><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/><style>html,body,*{margin:0;padding:0;}p,pre{margin:1em0;}bodyimg{max-width:100%!important;}</style></head><body>${data}</body></html>`,baseUrl:Platform.OS==="ios"?undefined:''}}style={{height:webHeight}}contentInset={{top:0,left:0}}></WebView>}//其他代码...(2)使用资源文件载入:具体实现是,先新建一个html文件,在里面写好初始化方法init,提供入参data(html内容)和fn(需要执行的方法),代码如下:
<htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/><metahttp-equiv="X-UA-Compatible"content="ie=edge"><title>测试富文本</title></head><style>html,body,*{margin:0;padding:0;}p,pre{margin:1em0;}bodyimg{max-width:100%!important;height:auto!important}</style><bodyid="height-wrapper"><script>functioninit(data,fn){varwrapper=document.getElementById('height-wrapper');wrapper.innerHTML=data;fn&&eval(fn);}</script></body></html>然后再webview引入这个html,注意ios和安卓平台引入的路径问题,代码如下:
//其他代码...bootstrapJS(){letdata=this._handleContent(this.props.data);letfun=`(function(){console.log("我是预留的方法");}())`;return`init(${JSON.stringify(data)},${fun})`}render(){constProps=this.props;const{webHeight}=this.state;constsource=(Platform.OS=='ios')?require('../../../html/renderHtml.html'):{uri:'file:///android_asset/html/renderHtml.html'};return<View>{!!Props.data&&<WebViewsource={source}style={{height:webHeight}}contentInset={{top:0,left:0}}injectedJavaScript={this.bootstrapJS()}scalesPageToFit={false}/>}</View>}//其他代码...(3)webview高度自适应问题以上两种方法都能满足App渲染富文本html的需求,但是高度得需要写死,这样肯定是没有达到预期的,所以我们需要根据内容自适应。webview有个参数是onNavigationStateChange:当导航状态发生变化的时候调用。
我们可以在html里面写个方法,把body的高度写到html的title标签上,这样就会触发导航状态变化,然后再把高度设置到webview样式上就可以了。具体实现:
importWebViewfrom'react-native-webview'//其他代码...render(){constProps=this.props;const{webHeight}=this.state;letdata=this._handleContent(Props.data);return<WebViewsource={{html:`<html><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/><style>html,body,*{margin:0;padding:0;}p,pre{margin:1em0;}bodyimg{max-width:100%!important;}</style></head><body>${data}<script>window.onload=function(){document.title=document.body.scrollHeight;}</script></body></html>`,baseUrl:Platform.OS==="ios"?undefined:''}}style={{height:webHeight}}contentInset={{top:0,left:0}}onNavigationStateChange={(event)=>{if(event.title&&!Props.webHeight){if(this.uuid===event.target){this.setState({webHeight:((isNaN(parseInt(event.title))?0:parseInt(event.title)))})}}}}></WebView>}//其他代码...html的高度不确定多数来源于图片的加载,如果有多个图片的话,onload方法里面拿高度并不一定能得到最后的高度,因为可能有图片没有加载出来。所以需要不断的监听body高度的变化,再设置回去,所以我们可以在onload方法中写,每加载完一个图片就执行一次changeHeight方法:
varheight=null;functionchangeHeight(){if(document.body.scrollHeight!=height){document.title=document.body.scrollHeight;}}setTimeout(function(){letimages=document.querySelectorAll("img");for(leti=0;i<images.length;i++){images[i].onload=function(){changeHeight();}}},300)(4)图片预览及链接跳转问题使用webview进行渲染html的话,就不能直接操作里面的图片及链接(a标签)了,不过webview提供了一个与App通信的功能,也就是onMessage:
onMessage:在webview内部的网页中调用window.postMessage方法时可以触发此属性对应的函数,从而实现网页和RN之间的数据交换。
所以我们可以在html上的onload方法写图片的点击事件,然后发送给react-native这边,react-native在用预览图片的方法进行预览或者对链接的跳转。
html代码:
//图片处理letimgArr=document.querySelectorAll("img");for(leti=0;i<imgArr.length;i++){imgArr[i].onclick=function(){window.postMessage(JSON.stringify({type:"img",url:imgArr[i].getAttribute("src")}));}}//a标签处理letaArr=document.querySelectorAll("a");for(leti=0;i<aArr.length;i++){letelem=aArr[i];leturl=elem.getAttribute("href");elem.onclick=function(){window.postMessage(JSON.stringify({type:"a",url:url}));};elem.setAttribute("href","javascript:void(0)");}react-native代码:
//其他代码..._onLinkPress=(url)=>{Linking.openURL(url);};_previewImg=(url)=>{letimages=[];if(url){images=[{uri:url}];}Overlay.show((<Overlay.PopViewcontainerStyle={{flex:1}}overlayOpacity={1}ref={v=>this.fullImageView=v}><AlbumViewstyle={{flex:1}}control={true}images={images}defaultIndex={0}onPress={()=>{this.fullImageView&&this.fullImageView.close()}}/></Overlay.PopView>));};_onMessage=(event)=>{letdata=JSON.parse(decodeURIComponent(decodeURIComponent(event.nativeEvent.data)))||{};data.type==="img"&&this._previewImg(data.url);data.type==="a"&&this._onLinkPress(data.url);};//其他代码...(5)通过postMessage设置高度所以我们也可以使用postMessage来发送html的高度,html中的changeHeight方法可以改成以下代码:
functionchangeHeight(){if(document.body.scrollHeight!=height){height=document.body.scrollHeight;window.postMessage(JSON.stringify({type:'setHeight',height:height,}))}}可以监听设置高度的react-native代码:
_onMessage=(event)=>{letdata=JSON.parse(decodeURIComponent(decodeURIComponent(event.nativeEvent.data)))||{};data.type==="img"&&this._previewImg(data.url);data.type==="a"&&this._onLinkPress(data.url);try{if(data.type==='setHeight'&&data.height>0){this.setState({webHeight:data.height})}}catch(error){//...}};至此,react-native渲染富文本的方案介绍完了,有写的不好以及错误的地方欢迎大家指出。
原文:https://juejin.cn/post/7111330597507792927logo设计
创造品牌价值
¥500元起
APP开发
量身定制,源码交付
¥2000元起
商标注册
一个好品牌从商标开始
¥1480元起
公司注册
注册公司全程代办
¥0元起
查
看
更
多