微信小程序:你必须知道的component自定义组件

1,243 阅读7分钟

注意

本文针对微信小程序初学者,如果已经开发过,可以直接移步!

本文写于几年前,现在部分东西有些许变化,但是依旧可以帮助你,不想看本文章可以直接参考微信小程序文档:developers.weixin.qq.com/miniprogram…

首先,我们应该明确,如何才能自己做一个自定义组件!

Component概念

Component像页面一样由wxml、wxss、js和json4个文件组成,且需要把这4个文件放在同一个目录中。与页面不一样的是,Component中的构造函数(也可以称构造器)是Component({}),而页面中的构造函数是Page({})

第二,创建两个文件夹,一个定义component,一个引用component!

我建立如下:

在这里插入图片描述

菜鸟将 lunbo 作为组件,使用 button 引用组件:

wxml:

<!--这里的lunbo是json文件引入的component,可以任意取名,但最好是其功能-->
<lunbo min="1"></lunbo>

json:

{
  "usingComponents": {
    "lunbo":"../lunbo/land"
  }
}

页面的数据怎么来?

这个官方文档倒是很清楚,页面的数据分两种

  1. data

    这是component里的私有数据,只用于你组件的渲染,引用该component的页面无法传递过来

  2. properties

    component留给引用页面传递参数的,也是用来直接渲染页面用的。

所以,可以很轻松的将以前的:

Page({
  data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },
  swiperChange:function(e){
     this.setData({
          nowIdx: e.detail.current,
      })
  },

改成:

Component({
  data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },
  swiperChange:function(e){
     this.setData({
          nowIdx: e.detail.current,
      })
  },

这时页面会警告:

在这里插入图片描述

翻看文档,你会发现,原来自定义的函数必须得写道一个叫 methods 的东西里面:

在这里插入图片描述

Component({
  data: {
    nowIdx:0,
    imglist:[
     {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },
 methods:{
    swiperChange:function(e){
      this.setData({
          nowIdx: e.detail.current,
      })
    },
  },
}

生命周期

这样基本上就大致知道了component的用法,但是,最重要的当然是生命周期了,所以菜鸟接下来这样干了:

Component({
   data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },
  methods:{
	swiperChange:function(e){
      this.setData({
          nowIdx: e.detail.current,
      })
    }
  },
  created:{
    function(){
      console.log(aaa)
    }
  },
  attached:{
    function(){
      console.log(bbb)
    }
  }
})

但是这里报错莫名其妙:

在这里插入图片描述

菜鸟一开始看着懵了,我没定义生命 n.apply 呀?为什么会报错,后来才发现关注点错了,因该是后面的一句: [component]:Lifetime Methods Eorror

后来发现,原来是:

生命周期函数不能用 {} 括起来,而且 function 必须写在冒号后面,不然也会报错

千万不要小看这一个,这真的是致命错误,可能大佬也不能很快发现。

所以代码变成了这样:

Component({
//这里的值打印得用this.data.min
  data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },

  methods:{
    swiperChange:function(e){
      this.setData({
          nowIdx: e.detail.current,
      })
    },
  },

  created:function () {
      console.log("created")
    },

  attached:function () {
      console.log("attached")
  }
})

发现运行结果如下:

在这里插入图片描述

这成功的说明了,这两个函数的顺序,还有其它生命周期函数,有兴趣的读者可以去自己试试!

其他生命周期

这里就不拖泥带水了,component代码:

Component({
  created:function () {
      console.log("land created")
  },
  attached:function () {
      console.log("land attached")
  },
  ready:function () {
    console.log("land ready")
  },
  moved:function () {
    console.log("load moved")
  },
  detached:function () {
    console.log("land detached")
  }
})

引用界面代码:

Page({
  gotovar:function () {
    wx.redirectTo({
      url: '../varshiyan/var',
    })
  },
  onLoad:function (params) {
    console.log("dome onload")
  },
  onReady: function () {
    console.log("dome onready")
  },
  onShow: function () {
    console.log("dome onshow")
  },
  onHide: function () {
    console.log("dome onhide")
  },
  onUnload: function () {
    console.log("dome onunload")
  },
})

输出结果:

在这里插入图片描述

这里提醒各位注意一点

那就是跳转用 wx.redirectTo,这样才可以执行页面卸载函数

结论:

component 的创建生命周期在页面之前,卸载生命周期在页面之后,页面切换后台后,不会再次执行 component 生命周期。

现在微信小程序文档写得挺清楚,见:developers.weixin.qq.com/miniprogram…

接收参数:properties

菜鸟又想,这component咋传参数进来的?

可是搜了半天也只有这篇文章讲得比较好:微信小程序自定义组件Component总结

现在微信小程序文档写得挺清楚,见:developers.weixin.qq.com/miniprogram…

在这里插入图片描述

可是这样,菜鸟表示想知道是否传进去了呀?于是我变成了:

Component({
  properties:{
    myProperty: {
      type: String, 
      value: '', 
    }
  },
  data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
    ]
  },

  methods:{
    swiperChange:function(e){     
      this.setData({
          nowIdx: e.detail.current,
      })
    },
  },

  created:function () {
      console.log("created")
      console.log(this.properties.myProperty.value)
    },

  attached:function () {
      console.log("attached")
      console.log(this.properties.myProperty.value)
  }
})

这样只会得到这样的结果:

在这里插入图片描述

为什么是undefined?自然是因为没有得到该值,菜鸟又做了好多改法还是不行,最后终于在小程序文档里面发现了:

原来 properties里面的值其实实质跟data里面的值一样,就是一个对外一个对内,所以打印因该用this.data.XXXX

于是代码变成了:

Component({
  properties:{
    min: {
      type: Number,
      value: 0
    },
  },
  //这里的值打印得用this.data.min
  data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },

  methods:{
    swiperChange:function(e){
      this.setData({
          nowIdx: e.detail.current,
      })
    },
  },

  created:function () {
      console.log("created")
      console.log(this.data.min)
    },

  attached:function () {
      console.log("attached")
      console.log(this.data.min)
  }
})

注意

1、这里传的是string 和 Number 类型,所以直接传的,其他类型需要用{{}}括起来,不然会被当成字符串传过去,导致类型不对!

2、WXML 不能直接调用组件的 methods 方法,需要在 methods 中封装为可用的数据,或者使用计算属性。菜鸟建议:组件中的数据全部由父组件传递,且在父组件就转成你需要的数据格式!

3、wxml 中,使用 data 中定义的数据只能用于显示,不能用于逻辑!原因:

  • WXML 不支持在模板中调用 includes() ,因为小程序的 WXML 解析器只支持简单的变量访问和 ? : 三元运算符,而不能执行复杂的 JavaScript 代码(如 mapfilterincludes)。

  • 正确的做法:应该在 JS 代码中 提前计算 这个值,并存到 data 里,然后直接在 WXML 里使用。

监听:observers

然后菜鸟还是感觉不行,这知道的太少了,最中方再会一个,于是就将目标锁定在了observers,文档里面说:

在这里插入图片描述

然后我的代码就变成了这样:

Component({
  properties:{
    min: {
      type: Number,
      value: 0
    },
  },
  //这里的值打印得用this.data.min
  data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },

  methods:{
    swiperChange:function(e){
      this.setData({
          nowIdx: e.detail.current,
          min:10
      })
    },
  },

  observers:{
    sja:function(e) {
      console.log("observers")
    }
  },

  created:function () {
      console.log("created")
      console.log(this.data.min)
    },

  attached:function () {
      console.log("attached")
      console.log(this.data.min)
  }
})

然后发现,啥也不会发生,我就一直很奇怪,后来看文档,越看越感觉奇怪

sja:function(e) {
  console.log("observers")
}

最后发现原来这里的sja不是乱写的,而是你要监听的字段的名字

补充讲解:3d轮播图

这里补充讲解一下3d轮播图思路:

3d轮播wxml代码:

<block>
  <swiper
    class="components_swiper" 
    bindchange="swiperChange"
    autoplay="{{true}}"
    circular="{{true}}"
    duration="1000"
    interval="3000"
    previous-margin="150rpx"
    next-margin="150rpx"
  >
    <swiper-item class="components_swiper_item" wx:for="{{imglist}}" wx:key="index">
      <view class="components_swiper_item_view {{nowIdx==index?'act':''}}" bind:tap="gototest">
        <image class="components_swiper_item_view_img" src="{{item.img}}"></image>
        <view style="width:90%;font-size:13px;margin-top:15rpx">
          <text>{{item.text}}</text>
        </view>
      </view>
    </swiper-item>
  </swiper>
</block>

3d轮播wxss代码

.components_swiper{
  padding: 20rpx;
}
.components_swiper_item_view{
  background-color: gainsboro;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  transform: scale(0.7);
  transition: all 1s; //添加动画效果,不然会很突然
  border-radius: 20rpx;
}
.components_swiper_item_view.act{
  transform: scale(1);
  transition: all 1s;
}
.components_swiper_item_view_img{
  width: 90%;
  height: 75%;
  border-radius: 15rpx;
}

3d轮播的json、js代码

Component({
  properties:{
    min: {
      type: Number,
      value: 0
    },
  },
  // 这里的值打印得用this.data.min
  data: {
    nowIdx:0,
    imglist:[
      {
        img:"../../imgs/s1.jpg",
        text: "课程理论"
      },
      {  
        img:"../../imgs/s2.jpg",
        text: "学习视频"
      },
      {
        img:"../../imgs/s3.jpg",
        text: "仪器演示"
      },
      {
        img:"../../imgs/s4.jpg",
        text: "我的考试"
      }
      /*图片自己弄*/
    ]
  },

  methods:{
    swiperChange:function(e){
      this.setData({
          nowIdx: e.detail.current,
          min:10
      })
    },
  },

  observers:{
    "min":function(e) {
      console.log("observers")
    }
  },

  created:function () {
      console.log("created")
      console.log(this.data.min)
    },

  attached:function () {
      console.log("attached")
      console.log(this.data.min)
  }
})
{
  "Component":"true", // 必须写,表示其是组件
  "usingComponents": {}
}

思路

每当 swiper 改变的时候就获取得到这个图片对应的 e.detail.current(就相当于是该图片是数组中的第几张),然后将其与 wxml 里面循环遍历的 index 比较,相等就表示该图片在第一张,就用大的样式。