likes
comments
collection
share

微信小程序自定义组件没法受外部flex影响

作者站长头像
站长
· 阅读数 15

微信小程序场景中,自定义组件内容无法接受外部flex布局影响,包括外部设置的 align-items、justify-content 等属性,以及自身设置的flex:1等flex相关的属性,均无法生效。(注:自定义组件已提前设置 styleIsolation: 'apply-shared' 使组件能受外部样式影响)

通过此文,你能了解到:

  1. 微信小程序自定义组件无法受外部flex影响的原因;
  2. 微信小程序自定义组件设置组件根节点样式的方法;
  3. css样式中 display: contents 详解。

问题描述:

在一个应用场景中,需要给一个元素设置了flex布局,称它为父元素节点,显示如下图一和图二中的灰色区域,它是页面里的第一个view节点(见图三); 子元素则是图一中的红色区域,也就是图三页面节点树里的自定义组件layout-child

在此flex布局中,我们给子元素又设置了 flex:1 样式后,子元素应该会填充满父元素内剩余空间。 也就是如下图一所示,自定义组件(红色区域)应该根据外部父元素(灰色区域)内部的剩余宽度来自动撑开宽度。红色区域应该填充满(除去8像素的内填充区域)灰色区域的内部才对,但是实际上红色区域的宽度为0,也就是如下图二所示,样式 flex:1 未生效。

显示效果如下

图一:

微信小程序自定义组件没法受外部flex影响

图二:

微信小程序自定义组件没法受外部flex影响

页面wxml节点树(图三):

微信小程序自定义组件没法受外部flex影响

相关代码如下:

<!-- 页面wxml内容 -->
<view class="w-100p flex bg-cccccc">
  <layout-child flex1>
    <view class="bg-red w-100p h-100"></view>
  </layout-child>
</view>

<!-- 组件layout-child wxml内容 -->
<view class="inline-flex flex-1">
 <slot />
</view>
<!-- 全局样式 wxss -->
.w-100p { width: 100%; }
.flex { display: flex; }
.h-100 { height: 100rpx; }
.pt-8 { padding-top: 8rpx; }
.pb-8 { padding-bottom: 8rpx; }
.pl-8 { padding-left: 8rpx; }
.pr-8 { padding-right: 8rpx; }
.bg-cccccc { background: #ccc; }
.border-box { box-sizing: border-box; }
.bg-red { background: red; }
.inline-flex { display: inline-flex; }
.flex-1 { flex: 1; }

相关原理:

关于flex布局,首先我们需要知道的是,一个元素被设置了flex布局,那么默认情况下,其内部的子元素就会被赋予成内联flex块元素,即被隐性设置成了 display: inline-flex 。 在这种情况下,这个元素所被添加的各种flex布局属性,都能控制其自身以及其内部子元素的排列方式,同时能通过给子元素添加 flex: 1 等之类的样式控制子元素占据父元素的剩余空间。

但需要注意的是,该元素设置的flex布局无法正常作用到孙级元素,也就是没法影响到其子元素的子元素上面,它只能传递一层,没法向下传递两层。

问题分析:

根据上面的原理,我们可以推测,在小程序自定义组件的渲染机制中,是不是独立拥有一层自己的页面结构节点,导致我们在组件外面设置的flex布局无法影响到组件内?

而如图三所示,我们可以看到,我们自定义的组件在wxml节点树上面确实额外存在一层dom结构,它是我们的自定义组件节点。 由此可以得知,小程序自定义组件默认是独自占用了一层页面节点的。 就是这一层自定义组件自带的节点阻碍了属性继承,导致我们小程序自定义组件内部的结构(也就是上面图三中绿色框选的 <view class="inline-flex flex-1">部分)未能将宽度拉伸满父元素内部剩余宽度。 所以我们即使在外部父元素设置了flex布局属性,自定义组件内的 flex:1 也无法正常生效,无法完成预期的效果。

那么,我们需要探寻的就是怎么消除小程序自定义组件这一层dom结构的影响?

解决方法:

1、将小程序自定义组件自带的节点虚拟化

在小程序自定义组件中,其options里面存在一个属性virtualHost,可以控制将该组件的节点虚拟化,即设置 virtualHost: true ,这样就能将该自定义组件默认的一层节点去除,使外部设置的flex布局能直接作用于该组件内部的节点。

<!-- 组件layout-child js内容 -->
Component({
  options: {
    styleIsolation: 'apply-shared', // 使该组件能受外部样式影响
    virtualHost: true, // 虚拟化组件节点
    ...
  },
  ...
});

 如下图所示,在小程序控制台的wxml显示界面,该自定义组件 layout-child 的节点直接被移除了。

微信小程序自定义组件没法受外部flex影响

(注: 如果后续有需要获取该组件实例,请谨慎使用此方法,设置了virtualHost 的组件节点有反馈无法被 selectComponentgetRelationNodes 选中的情况出现)

2、在小程序自定义组件中通过:host设置组件节点样式为display: contents

首先,需要知道的是,在组件的wxss样式文件中,是可以通过 :host 去直接对组件节点设置一些自定义的样式。

同时,在css中,存在一个 display: contents 属性,能作用在设置该属性的元素节点上。 它的效果是把这个元素的盒子属性(宽、高、背景色、内外边距等)在树型结构中移去,但其节点和内容会保留,元素的子元素与伪元素也仍然存在,元素内的文字照常显示。 同时元素本身设置的一些可继承的属性,比如字体颜色、字体大小、行高等,会被内部的子元素继承。 具体我们可以想象这个元素的本身不再在可视区域内显示,而只有内容留下的场景。

因此,在小程序自定义组件wxss中,添加以下代码,即可:

<!-- 组件layout-child wxss内容 -->
:host {
  display: contents;
}

通过该方法设置以后,跟上面第1种方法相比,显示效果一致,区别在于,页面节点树里的组件节点还是正常显示出来的,不会直接被移除。

微信小程序自定义组件没法受外部flex影响