该文根据之前的一问答整理而来。
目前,小程序的组件和模板相较于之前已经完善了很多,对于组件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!
==========================================
吐槽下,微信开发者这个编辑器,难用得要死。