菜鸟读文档-Vant Weapp-4(小程序文档样例对Tabbar组件的处理)

457 阅读6分钟

正文

分析小程序文档提供代码

在上一期的结尾我们发现, 微信小程序文档提供了实现tab选中态代码, 那我们就首先来看一看这段样例代码, 分享一些我自己的理解

index.wxss 部分

.tab-bar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 48px;
  background: white;
  display: flex;
  padding-bottom: env(safe-area-inset-bottom);
}

.tab-bar-border {
  background-color: rgba(0, 0, 0, 0.33);
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 1px;
  transform: scaleY(0.5);
}

.tab-bar-item {
  flex: 1;
  text-align: center;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}

.tab-bar-item cover-image {
  width: 27px;
  height: 27px;
}

.tab-bar-item cover-view {
  font-size: 10px;
}
  1. 使用固定定位 position: fixed; 将整个 tabbar 栏固定在手机小程序视窗底部

  2. 使用 env(safe-area-inset-bottom) 适配 iphoneX 的底部小黑条

  3. 使用 transform: scaleY(0.5);tabbar 栏上边框缩放为原来的一半(这个使用没太看懂...)

  4. 使用 flex 布局布置 tabbar 栏子项和各子项中的内容

    但感觉 tab-bar-itemtext-align: center; 属性的使用不是太有必要, 因为该盒子内部的两个子盒 cover-imagecover-view 已经被 justify-content: center;, align-items: center;flex-direction: column; 控制好了

  5. cover-image 标签和 cover-view 标签的选择原本是使用了元素嵌套的写法

    .tab-bar-item cover-image: 选中嵌套在类 tab-bar-item 内部的 cover-image 标签

    但实际上在微信开发者工具中却发现

    ...

    关于这个问题, 微信小程序的组件模板和样式中提到

    组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器。

    可见在组件中不仅是不能单独使用标签名选择器, 嵌套写法也不能使用. 将嵌套写法改为单独的类写法就可以了

index.wxml 部分

<!--miniprogram/custom-tab-bar/index.wxml-->
<cover-view class="tab-bar">
  <cover-view class="tab-bar-border"></cover-view>
  <cover-view
    wx:for="{{list}}"
    wx:key="index"
    class="tab-bar-item"
    data-path="{{item.pagePath}}"
    data-index="{{index}}"
    bindtap="switchTab"
  >
    <cover-image
      src="{{selected === index ? item.selectedIconPath : item.iconPath}}"
    >
    </cover-image>
    <cover-view
      style="color: {{selected === index ? selectedColor : color}}"
    >
      {{item.text}}
    </cover-view>
  </cover-view>
</cover-view>
  1. 使用 wx:for 循环来渲染各个 tabbar 子项
  2. tabbar 项节点的点击事件处, 使用 dataset 附加了自定义数据: 路径 path 和项编号 index
  3. 使用 selected 变量来控制 tabbar 项节点的选中高亮

index.js 部分

Component({
  data: {
    selected: 0,
    color: "#7A7E83",
    selectedColor: "#3cc51f",
    list: [{
      pagePath: "/index/index",
      iconPath: "/image/icon_component.png",
      selectedIconPath: "/image/icon_component_HL.png",
      text: "组件"
    }, {
      pagePath: "/index/index2",
      iconPath: "/image/icon_API.png",
      selectedIconPath: "/image/icon_API_HL.png",
      text: "接口"
    }]
  },
  attached() {
  },
  methods: {
    switchTab(e) {
      const data = e.currentTarget.dataset
      const url = data.path
      wx.switchTab({url})
      // 此处的 setData 其实是可以删去的
      // 因为在跳转的目标页面中会进行设置
      this.setData({
        selected: data.index
      })
    }
  }
})
  1. tabbar 子项需要的跳转路径 pagePath, 图标路径 iconPath 等封装到组件内部数据 data

  2. 实现 tabbar 栏跳转和高亮功能:

    1. 将组件的生命周期函数 attached 置空

    2. 将所有 tabbar 跳转页面都设置为组件 Component

    3. 将使当前页面对应 tabbar 子项高亮相关操作置于被视为组件的页面pageLifetimes 字段中

      Component({
        pageLifetimes: {
          show() {
            if (typeof this.getTabBar === 'function' &&
              this.getTabBar()) {
              // 在此处设置
              this.getTabBar().setData({
                selected: 0
              })
            }
          }
        }
      })
      

小程序样例实现 tabbar 栏跳转高亮的思路

我们来进一步分析样例为实现 tabbar 栏跳转高亮功能进行的处理:

  1. 将组件的生命周期函数 attached 置空

    我对于这一步的理解是这样的:

    组件的这一生命周期在小程序文档组件部分中是这样描述的:

    在组件完全初始化完毕、进入页面节点树后, attached 生命周期被触发。此时, this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。

    组件的这一生命周期看上去似乎可以用于使当前页面对应的 tabbar 子项高亮, 但由于 tabbar 栏是在 app.json 中注册, custom-tab-bar 下相应文件接管渲染的, 而不是编写于对应的页面, 无法用父元素来向该子组件进行通信, 我们只能放弃在这一生命周期进行高亮设置的想法

  2. 将所有 tabbar 跳转页面都设置为组件 Component

  3. 将使当前页面对应 tabbar 子项高亮相关操作置于被视为组件的页面pageLifetimes 字段中

    当我首先看到第二步的时候, 我其实并不是十分迷惑的, 因为在小程序文档组件构造器部分中, 专门提到了可以用 Component 构造器构造页面, 但是看到第三步中 pageLifetimes 字段中的 show() 的时候, 我确实是有些困惑了, 这一生命周期的含义是:

    组件所在的页面被展示时执行

    结合代码的含义是: 被视为组件的页面将在该组件所在页面被展示时, 执行使 tabbar 对应子项高亮的相关操作...

    这就产生了一个问题, 被视为组件的页面的所在页面被展示的时刻究竟是什么时候?

    对此, 我的初步猜测是: 在使用 Component 构造器构造页面之前, 微信会使用一个空的 Page 构造器来构造一个空的 Page 页面, 然后再用 Component 构造器构造页面中的内容, 若页面内容中使用到了别的组件, 微信将依次调用对应的 Component 构造器.

    但在测试中发现: 若是将用于构造页面的 Component 构造器注释掉, 开发者工具将会报错:

    可见, 用于构造页面的 Component 构造器将会直接替代之前的 Page 构造器

    如果不存在一个先创造的空页面来为 Component 构造器构造的页面传递页面被展示信息, 那么我的第二个猜测是: 此处的 pageLifetimes 中的 show 字段就相当于原来 Page 中的 onShow 字段

    但在测试中却发现: pageLifetimes 中的 show 字段和 Page 中的 onShow 字段可以同时使用, 甚至 PageComponent 的生命周期字段都是可以同时使用, 且彼此之间存在一定的顺序

    Component({
    
      lifetimes: {
        attached: function() {
          // 在组件实例进入页面节点树时执行
          console.log("This is attached in lifetimes");
        }
      },
    
      pageLifetimes: {
        show() {
          if (typeof this.getTabBar === 'function' &&
            this.getTabBar()) {
            this.getTabBar().setData({
              selected: 0
            })
          }
    
          console.log("This is show in pageLifetimes");
        }
      },
    
      methods: {
        onLoad: function () {
          console.log("This is onLoad in Page");
        },
        onShow: function () {
          console.log("This is onShow in Page");
        }
      }
    })
    

预告

小程序中具有生命周期的对象

既然我们发现了用于构造页面的 Component 构造器中, 组件的生命周期和页面的生命周期是可以同时使用的, 那么, 他们之间的顺序是怎样的呢? 而 tabbar 栏组件又是在其中的哪一生命周期被加入的呢? 这些问题我们只能下次再聊了:)

菜鸟读文档-Vant Weapp-5(小程序中的生命周期-基础)