最新情况(截至2024-03)
经确认,3.8.12+以后,你不再需要按照本文内容手动处理“--=”my-“操作了,可以放心使用了,这个版本编译出来的wxml文件已经自动生成了上述内容。
本文所述方法经验证:
<template> <view> 。。其他内容。。。。 <CommonList> <MyComponent slot="itemView" slot-scope="{item}" :item="item">MyComponent> CommonList> view> template> ...... script>
或者
<template> <view> <CommonList> <view slot="itemView" slot-scope="{item}"> 只显示ID:{{item.id}} view> CommonList> view> template> ...... script>
它们都可以正确地编译成这种形式:--。
PS: 为什么要用 Vue2 那种老套的写法呢?因为我发现用 v-slot 或者 Vue3 都会有各种奇怪的问题,这里就不细说了,大家自己考虑怎么用吧。
背景
众所周知,大部分开发微信小程序的同学肯定会遇到这样的问题:用Vue开开心心的写一个v-for,然后在v-for里打一个插槽,结果用的时候,H5没问题,但是微信小程序只渲染了第一个。
例如,我有一个名为的组件
这个组件内部有一个v-for,v-for内部有一个slot,通过循环的方式来处理渲染。
<template> 。。其他内容。。。。 <view class="lists"> <view class="data-item" v-for="(item, index) in products" :key="index"> <text>标题text> <slot name="itemView" :item="item"> <view> <text>{{item}}text> view> slot> view> view> 。。其他内容。。。。 template>
现在我有一个页面需要使用此组件
<template> <view> 。。其他内容。。。。 <CommonList> <view slot="itemView" slot-scope="{item}"> 只显示ID:{{item.id}} view> CommonList> view> template > import CommonList from './commonlist' export default { components: { CommonList } } script>
然后不出意外的话,程序在H5里是正常的,但是在小程序里,不管上面有多少个,都只有第一个会被渲染出来(因为不是槽位,所以会正常渲染)
如果 v-for 中不能使用 slot,那么开发就会很痛苦!这个大家应该都知道。
先说结论吧(有兴趣可以看下面的过程)
=================
微信小程序中v-for+slot需要使用小程序虚拟组件进行slot渲染
=================
例如,在 中,v-for 作用于原生组件视图
提供v-for+slot组件,正常写就行,比如在使用组件的页面(或者组件)中,如果内容不变,就使用微信虚拟节点完成渲染,那么我们需要先做一个虚拟节点组件,假设调用(注意这个要有接受slot的参数),然后在H5中正常使用,如果是微信小程序,就使用:xxxx方法,搭配专门的slot即可完成渲染
:这是使用组件修改后的页面
<template> <view> 。。其他内容。。。。 <CommonList generic:scoped-slots-itemView="my-component"> <view slot="itemView" style="display: none"> <MyComponent slot="itemView" slot-scope="{item}" :item="item">MyComponent> CommonList> view> template > import CommonList from './commonlist' import MyComponent'./my-component' export default { components: { CommonList, MyComponent} } script>
:这是
<template> <view> 。。。。其他内容。。。。 <view>只显示ID:{{item.id}}view> 。。。。其他内容。。。。 view> template > 。。。。其他内容。。。。 export default { name: 'FeeListItem', props: ['item'], //特别注意这个,这是接受slot参数的属性 } 。。。。其他内容。。。。 script>
重点是
1. 在组件上使用 `:--= "my-"
2.使用
在上述内容中:
:表示使用虚拟节点,这样写就可以了
-slos-是编译后的具体内容,也就是--+v-for中的slot的名字
my- 是编译后的虚拟组件,对应json文件中的组件声明,注意写法(做vue的就明白)
主要目的是让wx:if="{{$slot.}}"在编译出来的wxml中生效(任何控件都可以)
===========================折腾的过程===========================
分析
如果你在网上搜索“小程序v-for槽位”,你会发现,官方没有回应。
这么大的院子难道处理不了这种事??
怀着这个想法,我决定看看哪里出了问题。
看看微信小程序本身
通过查阅官方资料,我们发现:
在小程序中,槽点的灵活性远不如h5,例如
可以看到,组件中要使用多个插槽,必须进行配置。小程序中,wx:for 本身不支持插槽的循环渲染。可以搜索到很多类似的问题,比如插槽能和 wx:for 一起使用吗?
我也问过自己类似的问题:规模这么大,微信难道不知道不能循环分发代码带来的效率和耦合问题吗?
编译的小程序代码
从我截取的片段来看
<view class="data-v-282ea8d0"> <block wx:if="{{$slots.itemView}}"> <slot name="itemView">slot> <scoped-slots-itemView item="{{item.$orig}}" class="scoped-ref">scoped-slots-itemView> block> <block wx:else> <view class="data-v-282ea8d0"><text class="data-v-282ea8d0">{{item.$orig}}text>view> block> view>
这里我们可以看到slot确实是编译通过了,但是由于上面的情况,微信本身不支持在wx:for中渲染N个slot,所以我们的组件在使用的时候就会出现问题
但我们发现它还有一个
<scoped-slots-itemView item="{{item.$orig}}" class="scoped-ref">scoped-slots-itemView>
这是什么?
微信小程序虚拟节点
从官方文档这里:抽象节点可以知道,微信其实对组件的控制相当死板,每个组件的插槽都是只为组件本身量身定制的(可以说给设备打的只能是物理的洞,不允许打逻辑的洞),所以创造了一个虚拟组件。
虚拟组件本身就是一个组件,不会受到 v-for + slot 的影响。所以如果在 v-for 中将数据传递给虚拟组件,然后在虚拟组件中使用 slot,就能完成之前的工作。
微信小程序中使用虚拟节点+自定义组件
查看了抽象节点的主要逻辑后,我们可以得出以下结论:
1.如果使用微信原生开发,必须在组件中声明虚拟节点,如上图
。。。 <block wx:for="products" > <itemView>。。。。itemView> block> 。。。
对应的.json需要声明虚拟节点
{ 。。。 "componentGenerics": { "itemView": true } }
2. 使用组件时需要注意
。。。 <CommnList generic:itemView="你的组件名"> 。。。
现在我看到刚才那个 - 它不可能是一个虚拟节点吗?
通过查看编译后的 .json,你可以看到
"component": true, "componentGenerics": { "scoped-slots-itemView": true }
看起来在编译期间,v-for 中的插槽变成了虚拟节点。
===========================================
正确渲染虚拟节点
有了以上官方技术支持,我们可以考虑做如下处理:
在Vue代码中,直接使用组件的地方写:=
那么页面代码就变成:
。。其他内容。。。。 <CommonList generic:scoped-slots-itemView="my-component"> CommonList > 。。其他内容。。。。 <script> import CommonList from './commonlist' import MyComponent'./my-component' export default { components: { CommonList, MyComponent} } script>
这里的my-是虚拟组件,--是根据编译后的wxml中的虚拟节点名获取的,其实观察一下就可以发现,它的规则是:--+v-for中slot的名字
写完之后编译并查看编译后的页面,会发现在对应的wxml中,-list组件确实有属性:--="my-",并且在json中也可以看到对应的my-声明
OK,直接编译运行到小程序模拟器上,结果发现my-没有被加载。
有了上述结论,为什么我们在小程序中看不到正确的结果呢??
显然所有条件都满足:
1. 在编译后的.json文件中声明虚拟节点