大多数Web应用的富文本内容都是以HTML字符串的形式存储的,所以通过HTML文档显示HTML内容自然是没有问题的。但是,在微信小程序(以下简称“小程序”)中,我们应该如何渲染这部分内容呢?
解决办法
wxParse
小程序启动时,无法直接呈现HTML内容,于是一个名为“wxParse”的库诞生了。其原理是将HTML代码解析成树形结构的数据,然后通过小程序的模板来呈现数据。
富文本
后来,小程序添加了一个“富文本”组件来显示富文本内容。然而,这个组件有一个很大的限制:所有节点的事件都被屏蔽在组件中。也就是说,在这个组件中,即使是“预览图片”这样的简单功能也无法实现。
网络视图
后来,小程序允许通过“网页视图”组件嵌套网页,通过网页显示HTML内容是最好的兼容解决方案。但是,因为需要加载一个页面,所以性能很差。
当“WePY”遇到“wxParse”时
考虑到用户体验和功能交互,我们放弃了两个原生组件“富文本”和“web-view”,选择了“wxParse”。然而,当我使用它时,我发现“wxParse”不能很好地满足需求:
我们的小程序是基于“WePY”框架开发的,而“wxParse”是基于原生小程序编写的。为了使它们兼容,必须修改“wxParse”的源代码。“wxParse”只是通过图像组件显示和预览原始img元素的图片。在实际使用中,可以利用云存储界面对图像进行缩小,从而达到“小图显示、原图预览”的目的。“wxParse”直接使用小程序的视频组件来显示视频,但是视频组件的层次问题往往会导致UI异常(例如,阻塞了一个固定位置的元素)。另外,如果你环顾一下“wxParse”的代码仓库,你会发现它已经两年没有迭代了。因此,基于“WePY”组件模式重写富文本组件的想法应运而生,其结果就是“WePY HTML”项目。
实施程序
解析HTML
首先还是要把HTML字符串解析成树形结构的数据,我用的是“特殊字符分离法”。HTML中的特殊字符是“”和“”,前者是起始符,后者是终止符。
如果要解析的内容以starter开始,那么starter和terminator之间的内容将被截取作为解析的节点。如果要解析的内容不是以起始符开始的,则从起始符开始到开始符之前的内容(如果起始符不存在,则为结尾)将被截取并解析为纯文本。剩余的内容进入下一轮解析,直到没有剩余的内容。如下图所示:
为了形成树结构,应该在解析期间维护上下文节点(默认情况下是根节点):
如果截取的内容是开始标记,则根据匹配的标记名称和属性在当前上下文节点下创建子节点。如果标签不是自结束标签(br、img等)。),上下文节点被设置为新节点。如果截取的内容是结束标签,则根据标签名称关闭当前上下文节点(将上下文节点设置为其父节点)。如果是纯文本,则在当前上下文节点下创建文本节点,并且上下文节点保持不变。该过程如下表所示:
在上述过程之后,HTML字符串被解析成一个节点树。
对比
将上述算法与其他类似的解析算法进行比较(性能通过“解析长度为10000的HTML代码”来衡量):
可以看出,在不考虑容错(产生错误结果而不是抛出异常)的情况下,这个组件的算法相比其他两个有着压倒性的优势,满足了小程序的需求。一般来说,富文本编辑器生成的代码不会有语法错误。因此,即使容错性差,问题也不大(但需要改进)。
模板渲染
树结构的绘制不可避免地涉及到子节点的递归处理。但是小程序的模板不支持递归,似乎陷入了一个大坑。
看一下“wxParse”模板的实现,它以一种简单粗暴的方式解决了这个问题:嵌套调用是通过13个几乎完全相同的模板进行的(1调用2,2调用3,…,12调用13),这意味着它最多可以支持12次嵌套。一般来说,这个深度就够了。
因为“WePY”框架本身有构造机制,所以不需要手工编写十几个几乎相同的模板,通过一个内置的插件来生成。
下面的模板需要重复嵌套(简化),在代码开始前后插入特殊注释来标识它们,还有另一个特殊注释("!-下一个模板- ") id:
!-wepyhtml-repeat start-template name=' wepyhtml-0 ' block wx : if=' { { content } } ' wx : for=' { { content } } ' block wx : if=' { { item . type==' node ' } ' view class=' wepyhtml-tag-{ { item . name } } '!-下一个模板-/查看/阻止块wx : else { { item . text } }/阻止/阻止/模板!- wepyhtml-repeat end -以下是相应的构建代码(“wepy-plugin-replace”需要安装):
//wepy . config . js { plugins : { replace : { filter :/。wxml$/,config: { find: /!-wepyhtml-重复开始- ([Ww]?)!- wepyhtml-repeat end - /,replace(match,TPL){ let result=' ';//反正不用付钱,就写一个20层嵌套的for(让I=0;i=20i ) {结果='n' tpl。替换(' wepyhtml-0 ',' wepyhtml-I ')。替换(/!-下一个模板- /g,()={ return i===20?' : ` template is=' wepyhtml-$ { I 1 } ' wx : if=' { { item . children } } ' data=' { { content : item . children '/template `;});}返回结果;}}}}}但是运行后发现第二层和更深层的节点没有渲染,说明嵌套失败。查看dist目录中生成的wxml文件,我们可以发现变量名与组件的源代码不同:
在生成组件代码时,如果=' { $ HTMl content $ WePYHTMl $ content } } ' wx:为=' { $ HTMl content $ WePYHTMl $ content } } ' ' wepy '则阻塞wx :为了避免组件数据和页面数据的变量名之间的冲突,请按照一定的规则为组件的变量名加上前缀(如上代码所示)因此,在生成嵌套模板时,还必须使用带前缀的变量名。
在组件代码中添加变量“thisIsMe”来标识前缀:
!-wepyhtml-repeat start-template name=' wepyhtml-0 ' { ThisMe } }块wx : if=' { { content } } ' wx : for=' { { content } } '块wx : if=' { { item . type==' node ' } ' view class=' wepyhtml-tag-{ { item . name } } '!-下一个模板-/查看/阻止块wx : else { { item . text } }/阻止/阻止/模板!-wepyhtml-重复结束-然后修改构建代码:
replace(match,TPL){ let result=' ';让前缀=' ';//匹配前缀TPL=TPL . replace(/ { { s } *( $)。*? $)this Me s * } }/,(match,p)={ prefix=p;返回“”;});for(设I=0;i=20i ) {结果='n' tpl。替换(' wepyhtml-0 ',' wepyhtml-I ')。替换(/!-下一个模板- /g,()={ return i===20?' : ' template is=' wepyhtml-$ { I 1 } ' wx : if=' { { item . children } } ' data=' { $ { prefix } content : item . children } } '/template `;});}返回结果;}至此,渲染问题解决。
画
为了节省流量,提高加载速度,在显示富文本内容时,通常会根据需要的大小缩小里面的图片,点击小图片进行预览后才会显示原始图片。这主要涉及节点属性的修改:
将原始图像路径(src属性值)保存到自定义属性(如“data-src”)中,并将其添加到预览图像数组中。将图片的src属性值修改为缩小图片的URL(一般云服务提供商都提供了这样的URL规则)。当您单击图片时,使用自定义属性的值预览它。为了满足这一要求,该组件在解析节点时提供了一个钩子:
onNodeCreate(name,attrs){ if(name==' img '){ attrs[' data-src ']=attrs . src;//预览图像数组this . previewmgs . push(attrs . src);//缩略图attrs.src=resize img (attrs.src,640);}}对应的模板和事件处理逻辑如下:
模板名称=' wepyhtml-img ' image class=' wepyhtml-tag-img ' mode=' width fix ' src=' http3360 { { elem . attrs . src } } Data-src=' http: { { elem . attrs[' Data-src ']| | elem . attrs . src } } ' @ tap=' img tap '/image/template//单击小图像查看大图像imgTap(e){ we}视频
在小程序中,视频组件的级别更高(不能降低)。如果页面设计中有可能阻挡视频的元素,需要一些技巧来处理:
隐藏视频成分,用图像成分占据(视频封面);点击图片时,让视频全屏播放;如果退出全屏,播放将暂停。相关代码如下:
模板名=' wepyhtml-video ' view class=' wepyhtml-tag-video ' @ tap=' video AP ' data-nodeid=' { { elem . nodeid } } '!-视频封面-image class=' wepyhtml-tag-img wepyhtml-tag-video _ _ poster ' mode=' width fix ' src=' http 3360 { { elem . attrs . poster } } '/image!-play icon-image class=' wepyhtml-tag-img wepyhtml-tag-video _ _ play ' src=' http :/imgs/icon-play.png'/image!-视频组件-视频样式=' display: none'src=' http : { { elem . attrs . src } } ' id=' wepyhtml-video-{ { elem . nodeid } } ' @ full screenchange=' video full screenchange ' @ play=' video play '/video/view/Template {//点击封面图片播放视频点击(e){ const nodeid=e . current target . dataset . nodeid;const context=wepy . createvideocontext(' wepyhtml-video-' nodeId);context . play();//安卓微信下,如果视频不可见,就不能通过调用play()播放。/您需要调用全屏方法if (wepy.getsysteminfosync()。platform===' Android '){ context . request full screen();}},//视频水平高。为防止其他特殊定位元素遮挡造成界面异常//strong制视频播放(e) {wepy。create video context(e . current target . id)。request full screen();},//退出全屏暂停videoFullscreenChange(e) {if(!e . detail . full screen){ wepy . createvideocontext(e . CurrentTarget . id)。pause();}}}本文分享到此结束。
以上就是本文的全部内容。希望对大家的学习有帮助,支持我们。