相关组件版本:avalon 1.3.6、masonry 3.1.5

最近,在公司的项目中,要开发一个使用瀑布流的前台,衡量了各种解决方案后,还是觉得masonry最成熟,所以就选用了它。而在之前开发后台的过程中,对avalon也用得挺熟手的了,所以这次做前台也用上。由于avalon是管dom的,masonry也是管dom的,所以实现兼容的重点就是,让它们管同一份dom,而不是各管各的。

我的avalon相关代码是这样的:

    avalon.define({
      $id: 'masonry',
      article_list: <?php echo $articleList; ?>,
    });

其中的article_list便是存放瀑布流数据的数组,用php生成json格式的字符串输出,赋给article_list作为初值(第一版数据)。然后,调用avalon的ms-repeat指令来循环渲染瀑布流的dom:

  <!-- 瀑布流位置 -->
  <div id="masonry_container" ms-controller="masonry">
    <div id="masonry_content">
        <div class="portfolio" ms-repeat-article="article_list">
          <div class="portfolio-wrapper" ms-click="open_article_modal(article.id)">
              <a> 
                <img ms-attr-src="article.cover_img_url" ms-css-height="article.cover_img_height"> 
              </a>
              <div class="article_follow side_btn"></div>  
              <div class="article_collect side_btn"></div>  
              <div class="article_body">
                <div class="article_meta">
                    <h2>{{article.title}}</h2>
                    <p></p>
                </div>
              </div>
          </div>
        </div>
    </div>
  </div>

在avalon.scan()以后,实例化masonry,第一版数据就算是出来了,一切都很正常。

但是在后面继续加载数据的时候,就出问题了。我的设计是,判断当滚动条拉到最下,就触发事件用ajax读取第二、三、四……版的数据。

一开始我想得很简单:不就是把ajax获取到的数据直接添加到avalon的vm里,让它自动完成新数据的渲染就好了,然后再重新实例化masonry。这种方案的问题是,由于“重新实例化masonry”需要的是先把masonry对象destroy()掉,所以就会看到很明显的闪烁,而且,可以预想到,当数据越来越多的时候,重新实例化的代价就会越来越大,因此这种方案是不可取的。

接着我仔细阅读了masonry的文档,发现其原来是有addItems/appended这样的方法可供调用的,我读了文档上的示例代码(一个小插曲,由于本人的原生js实在是太不济,就想着用jquery版的,却发现无论如何都调不通,大概是因为用了requireJS来模块化的缘故吧,这里暂且不提),发现这个方法的原理就是先往dom树里添好新的dom节点,然后再将新的dom节点作为参数传入addItems/appended。这就使我犯难了,我的dom树是交给avalon来处理的,又不是自己拼的,哪来dom节点可以传给masonry呀?为了做出一份可以传给masonry的dom节点,我也是拼了,用jquery来生成一份dom节点再传给masonry,可是试了一下,无效呀,masonry根本就没有控制新增dom节点的位置。

这时候我在嘀咕,会不会是avalon还未生成dom节点,masonry就开始“控制”的缘故呢?为了测试这个可能性,我使用了avalon中的data-repeat-rendered指令,这个指令可以指定一个函数,在ms-repeat渲染完后再执行,这样就可以保证avalon已经生成好dom节点后masonry再介入。测试的结果很令人沮丧,masonry依然没有控制dom节点的位置,所以应该不是这个问题。

最终,在我的测试下,正确的做法是:把avalon生成的dom节点传给masonry,怎么实现呢?说起来也很简单,记录下拉取新数据前瀑布流已有多少个文块,也记录下拉取到多少个文块,这样就可以得到新增文块索引的范围是从几到几了;当avalon渲染完ms-repeat后,用jquery获取瀑布流所有文块的dom树,再根据算出来的新增文块索引范围,将新增的dom节点取出来后,传给masonry,就大功告成了!


array_huang
10.4k 声望6.6k 粉丝