小程序及优化小程序

微信小程序开发之wxParse组件化

2018-08-08  本文已影响0人  兔贩子

背景

6月的时候,公司有个小程序项目是展示一些微信公众号文章。因为获取的文章内容都是HTML DOM的结构。在小程序里面如果简单地通过RichText展示,可能会出现一些样式和原文不一致的问题。还有一个需求是要在文章内进行一些选择、标注的操作。这时候就不可避免地要将HTML String转成HTML DOM进行处理、显示。而wxParse就是这样做的。

功能1.gif

思路

既然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的福利,所以这个组件相当于是一个没有状态的死组件,孤单地游离在外面...

上一篇下一篇

猜你喜欢

热点阅读