评论

组件slot动态数据,默认slot实现方案

自定义组件slot动态数据实现方案,编译处理,模板替换,实现动态数据,默认slot。

该文根据之前的一问答整理而来。

目前,小程序的组件和模板相较于之前已经完善了很多,对于组件slot占位只能使用静态数据,无法支持动态数据,确实有些鸡肋。

在此,提供一种间接处理的方案,提供给大家参考。

网上有一种方式,slot的name设置为动态的,比如index1,index2,index3这种,额外的另外一边也对应提供index1,index2,index3对应的片段。这种方式,是一种思路,不算真正的支持动态数据,灵活性也不高,而且对于列表这种有N个的话,估计够呛。

最好的是提供类似这样的写法:

<!--slot占位-->
<slot name="slotOne" data="{{item}}"></slot>

<!--实际的slot内容-->
<view slot="slotOne">{{item.name}}</view>

data为动态传递的数据,但是很可惜,官方并不支持。

本来打算断点调试跟踪下,万一有呢,只是官方没说明而已^_~。代码压缩处理了的,看着头疼,观察了几个地方,应该是没有提供,而且根据官方的实现,因为要处理组件和视图的隔离,估计还不好实现(如果容易实现的话,估计也不用等到现在,之前的帖子是2018年问的),主要跟他实现机制有一定关系。

能否自己实现呢?思考了下这个问题,恰好自己有这样的需求。

小程序本身也是一个编译运行的过程,对于前端开发而已,引入编译处理也十分常见,再加上想到自己很久之前就这么玩过,那时候小程序的组件和模板相关的还不如现在这样完善,自己就通过编译的方式实现过组件的处理。

templage可以传递数据,可以利用这一点。于是,就有了下面这样大概的构想。


==========================================

比如component-a组件中,提供动态slot模板,只需要占位处理如下:

<slot-template name="slotOne" data="{{item}}"></slot-template>

data即为动态传递的数据,name是对应slot可以有多个的情况。这里自定义了一个slot-template的标签,注意这里不是组件,只是一个占位编译替换而已。

最终会编译为两部分:

<!--实际的模板内容-->
<template name="pageA:slotOne" data="{{item}}" >模板内容{{item.name}}</template>
<template name="pageB:slotOne" data="{{item}}" >模板内容{{item.name}}</template>

<!--使用对应的模板-->
<template is="{{ slotName }}"></template>

因为我们的组件不会在一个地方使用,比如这里的pageA和pageB,可以约定为对应页面的目录。


页面如何使用呢?

<!--pageA.wxml-->
<component-a slot-name="pageA"></component-a>

<!--pageB.wxml-->
<component-a slot-name="pageB"></component-a>


slot-name属性,用以区分,是哪个页面使用,就可以对应到具体的模板内容,模板内容也动态注入了变量。


到这里,还缺一个环节,模板内容呢?也就是编译替换到组件的内容在哪里?

按照小程序的约定,使用组件前,都需要先配置,对应有一个pageA.json的文件,大概内容如下:

{
  "usingComponents": {
    "component-a":"../../components/component-a/index"
  }
}

对此,我们也在该文件上添加一个字段,来表示组件占位模板与页面实际模板内容之间的的关系,如下:

{
  "usingComponents": {
    "component-a":"../../components/component-a/index"
  },
  "slotTemplates":{
    "component-a:slotOne":"./slot/slot-one.wxml",
    "component-a:slotTwo":"./slot/slot-two.wxml"
  }
}

这里的slotTemplates即为扩展添加的字段,component-a为组件名字,slotOne和slotTwo为组件内的slot占位,slot/slot-one.wxml和slot/slot-two.wxml为页面实际的模板内容。

当然,由于slotTemplates字段是自定义的,开发工具会得到提示,可以忽略,或者编译处理的时候移除都是可以的。


上述方案,已经简单验证了下,通过gulp插件的方式。当然,从效率上讲,还不如直接在组件内提供A、B、C几种模板,通过一个参数类型之类的控制使用哪个模板。整个过程实现原理,其实就是这样的,只是把这一机制隐藏到编译之后了而已。更多的是从架构,实现机制上考虑,而非解决业务问题。

上述方案,有个不足的地方就是,因为小程序组件和页面之间有隔离,这种隔离机制是小程序内部处理的。使得替换的模板内容必须要在组件内,这样的话,就会出现一个问题,如果有N个页面在使用该组件的话,编译替换后的模板内容那里就会有N个,虽然写的时候,只写了一条<slot-template/>语句而已。如果小程序支持,全局模板的话,就没这么麻烦了。


==========================================

补充内容:

上面说,slot-template属于占位编译替换的标签,并非自定义组件,如果是自定义的组件呢???将其调整为自定义组件之后,再稍微处理下编译处理过程,可以实现我之前想要的默认slot的效果。

<slot-template name="slotA" data="{{item}}">
  <view>默认slot区域</view>
</slot-template>

其中,slot-template中间的就是默认的slot。

我们只需要将slot-template当做一个组件,提供data和name两个properties,内容中定义一个slot占位即可。大概如下:

<!--components/slot-template/index.json-->
properties:{
  name:{
    type:String,
    value:''
  },
  data:{
    type:null
  }
}
<!--components/slot-template/index.wxml-->
<view class="slot-default-template"> 
 <slot></slot>
</view>


不配置slotTemplates的时候,就是普通的自定义组件的是否方法而已。配置了slotTemplates,就会编译处理为template支持动态数据的方式。

一切OVER!

==========================================


吐槽下,微信开发者这个编辑器,难用得要死。

最后一次编辑于  2021-11-30  
点赞 2
收藏
评论

1 个评论

  • 清蒸鱼
    清蒸鱼
    2021-12-01

    -不是,你一开始的问题,为什么要用slot传组件里的数据data呢

    2021-12-01
    赞同
    回复 1
    • 阿白
      阿白
      2022-08-02
      举个例子:一个组件内渲染一个列表,不同页面引用这个组件时想要自定义渲染列表内容,就想到slot,自然就像要slot能将data提供到页面去渲染啊
      2022-08-02
      回复
登录 后发表内容