微信小程序开发之wxParse组件化
背景
6月的时候,公司有个小程序项目是展示一些微信公众号文章。因为获取的文章内容都是HTML DOM的结构。在小程序里面如果简单地通过RichText展示,可能会出现一些样式和原文不一致的问题。还有一个需求是要在文章内进行一些选择、标注的操作。这时候就不可避免地要将HTML String转成HTML DOM进行处理、显示。而wxParse就是这样做的。
思路
既然wxParse能够显示富文本而且它的思路是将整个HTML String转换成一个Node数组,数组按照传入的HTML String生成HTML DOM结构。所以既然它能够处理每一个DOM,那么我们也可以在其中的一环上做一些事情,例如标注。然而实际开发的时候是在显示的时候进行处理的。
分析wxParse
wxParse的使用文档里写的很清楚。
WxParse.wxParse('article', 'html', article, that, 5);
在对应的Component或者page组件中调用其这句话,其实就是将article处理成node数组,存在that的‘article’字段里,而that就是一个component组件。
接下来
// 引入模板
<import src="你的路径/wxParse/wxParse.wxml"/>
//这里data中article为bindName
<template is="wxParse" data="{{wxParseData:article.nodes}}"/>
通过这个template来进行展示,基本思路就是这样子。
这个wxParse.wxml目前是一个拥有11层的template嵌套,也就是最多支持11层的DOM树了。
然而这是无法满足我们的需求的,因为标注功能需要操作DOM,也就是通过点击,你要知道你点了哪一个DOM,而template在目前的版本是无法传递方法的。所以有了接下来的故事,也就是把wxParse组件化,将其变为组件不就可以传递方法到最内层,然后做任何事情了嘛。
组件化
组件化之后的结构长这样,其实就是把template中的每一层都抽出来写成组件,而转node数组的那部分,还是用之前的方法,这里只是负责显示的部分。
组件化后的结构.png
wxEmojiView、wxParseBr、wxParseImg、wxParseVideo等等都是简单的组件,负责显示传入的DOM就可以了,而wxParse比较复杂一些,根据node的类型,来指定渲染哪些组件。直接贴代码大家感受一下吧。
这里出现了组件自身又引用自身的情况(第5行),这个在微信小程序原生开发中是可行的。
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<custom-parse item="{{item}}" notes="{{notes}}"></custom-parse>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li" style="{{item.styleStr}}">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<custom-parse item="{{item}}" notes="{{notes}}"></custom-parse>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<custom-parse-video item="{{item}}"></custom-parse-video>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<custom-parse-img item="{{item}}"></custom-parse-img>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<custom-parse item="{{item}}" notes="{{notes}}"></custom-parse>
</block>
</view>
</block>
<block wx:elif="{{item.tag == 'table'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<custom-parse item="{{item}}" notes="{{notes}}"></custom-parse>
</block>
</view>
</block>
<block wx:elif="{{item.tag == 'br'}}">
<custom-parse-br item="{{item}}"></custom-parse-br>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<custom-parse item="{{item}}" notes="{{notes}}"></custom-parse>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<custom-parse item="{{item}}" notes="{{notes}}"></custom-parse>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<custom-emoji-view item="{{item}}" bind:onMark="onMark" notes="{{notes}}"></custom-emoji-view>
</block>
完成之后,只需要在你需要的地方调用这个组件就可以了。
<html-view id="htmlView" wxParseData="{{nodes}}"
notes="{{notes}}"
bind:onMark="onMark"
bind:unMark="unMark"
bind:wxParseImgTap="wxParseImgTap" bind:wxParseImgLoad="wxParseImgLoad" ></html-view>
需要哪些方法,就可以像其他组件一样,往内部传递下去就行了。只需要在最内层设置一下冒泡和跨越组件边界就可以了
onMark: function (event) {
var myEventOption = { bubbles: true, composed:true} // 触发事件的选项
this.triggerEvent('onMark', event, myEventOption);
}
在Taro(bate3)中使用
因为开发中遇到了全局状态管理的的问题,第二版采用Taro重构。在这个功能上又遇到了问题,我试图用同样的思路来组件化。然后Taro目前的版本还没有解决组件嵌套自身的问题,大家可以看我箭头,这是在CustomParse组件中,在第二个箭头的位置,又引用了它自身。这种写法,会导致编译的时候循环引用。我猜想是编译转成小程序原生结构的时候,导致了死循环。所以这个方法就行不通了。
CustomParse.png
然后,产品是不会管怎么实现的,明天上线!
所以为了顺利上线,我用了最土鳖的办法,直接把上一个版本的这部分代码直接复制到了Taro打包好的dist文件夹中,经过复杂的调试,因为还要相关用到的组件一起copy,最终搞定,留下的一个问题就是在copy进来的组件中没有享受到了Taro Redux的福利,所以这个组件相当于是一个没有状态的死组件,孤单地游离在外面...