微信小程序-自定义组件

一、自定义组件介绍

微信小程序提供了自定义组件扩展机制,允许我们使用自定义组件的方式来构建页面。

自定义组件可以使我们更好的复用一些功能。

二、创建自定义组件

在项目的pages文件夹下创建文件夹,例如customComponent文件夹,然后右键这个文件夹,选择新建Component,这时customComponent文件夹出现四个文件,和正常的创建的一样,下面介绍这四个文件和正常的不同之处:

#CusComponent.js:
Component({
  /**
   * 组件的属性列表
   */
  properties: {
  },
  /**
   * 组件的初始数据
   */
  data: {
  },
  /**
   * 组件的方法列表
   */
  methods: {
  }
})
#CusComponent.json
{
  "component": true,  //代表当前文件是一个组件
  "usingComponents": {} //使用组件写在括号里
}

js文件的头有Page变为Component,json文件里面多了"component": true,wxmlwxss文件和原来的一样。

三、如何使用自定义组件

想在主页index页面使用组件,那么打开存放页面的pages文件夹下的index文件夹中的index.json,在usingComponents中添加如下代码:

{
  "usingComponents": {
    "CusComponent":"/pages/customComponent/cusComponent"
  }
}

usingComponents里面是键值对,左边是要使用组件的名字,右边是其路径。

接着在index.wxml中使用<CusComponent></CusComponent>引入组件。

四、自定义组件中.js文件的属性介绍

  • data属性:组件的初始数据
    例如添加如下数据:
#js文件中的data属性:
data: {
    itemList:[
      {id:0,name:"首页"},
      {id:1,name:"类别"},
      {id:2,name:"关于"},
    ]
  }

此时在wxml文件中可以像下面这样写:

<view wx:for="{{itemList}}" wx:key="key">{{item.name}}</view>

结果:首页  类别  关于

因为wx:for中列表遍历默认的是item,所以可以直接用item.name来获取name

  • methods属性:组件的方法列表
    在正常页面中方法是存放在.js文件data属性的同层级下的。而在自定义组件中必须放在.js文件methods里面,放在外面方法无效。
<view bindTap="handleMethod"></view>

methods: {
  handleMethod(e){
    //语句let itemList = this.data.itemList;和下面的相等,解构
    let {itemList} = this.data;
    //[].forEach遍历数组,遍历的时候修改了v,也会导致源数组被修改
    //使用这种写法则相当于重新拷贝了一份数据let items = JSON.parse(JSON.stringify(this.data.itemList));
    itemList.forEach((v,i) => i===index?v.isActive=true:v.isActive=false)
  }
}
  • properties属性:组件的对外属性,是属性名到属性设置的映射表。里面存放的是父组件传递过来的数据
    参见:五、父组件向子组件传值
  • options属性:配置一些功能的选项
    可配置的选项:

multipleSlots:true,在组件定义时的选项中启动多slot支持
styleIsolation:isolated,启动样式隔离,具体配置选项参见:(八)自定义组件的样式
addGlobalClass:true,表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面。这个选项等价于设置 styleIsolation: apply-shared
virtualHost:true,将自定义节点设置成虚拟的,我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等,而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定。

/* 组件 custom-component.js */
Component({
  options: {
    addGlobalClass: true,
  }
})
  • 其他属性:
属性 类型 是否必填 描述
properties Object|Map 组件的对外属性,是属性名到属性设置的映射表
data Object 组件的内部数据,和properties用用于组件的模版渲染
observers Object 组件数据字段监听器,用于监听properties和data的变化
methods Object 组件的方法,包括事件响应函数和任意的自定义方法
created Function 组件生命周期函数,在组件实例刚刚被创建时执行,注意此时不能调用setData
attached Function 组件生命周期函数,在组件实例进入页面节点树时执行
ready Function 组件生命周期函数,在组件布局完成后执行
moved Function 组件生命周期函数,在组件实例被移动到节点树另一个位置时执行
detached Function 组件生命周期函数,在组件实例被从页面节点树移除时执行

五、父组件向子组件传值

直接看代码:

#父组件的wxml文件,在此文件中引入CusComponent组件
//parentData为要传递的数据名称,parent_data为数据内容
<CusComponent parentData="parent_data"></CusComponent>
#自定义组件的.js文件里的properties属性
properties: {
    //要接收的数据名称
    parentData:{
      //要接收的数据类型
      type:String,
      //要接收的数据
      value:''
    }
  }
<!-- 自定义组件的wxml文件 -->
<view wx:for="{{itemList}}" wx:key="key">{{item.name}}</view>
<!-- parentData就是要接收数据的名称 -->
<view class="content">{{parentData}}</view>

过程:
1、父组件通过parentData="parent_data"将数据传到子组件
2、子组件的properties接收到数据后,根据父组件数据名parentData,存到parentDatavalue
3、子组件的wxml文件直接使用{{parentData}}来拿到父组件传递过来的数据。

六、子组件向父组件传值

子组件给父组件传递数据通过自定义组件触发事件来进行传递。先看代码:

#子组件的.js文件
methods: {
  handleActive(e){
    const {index} = e.currentTarget.dataset;
    //triggerEvent("自定义事件名称",要传递的数据),触发事件
    this.triggerEvent("itemChange",{index});
  }
}
#父组件的wxml文件
<!--
当子组件事件触发后,bindbinditemChange会执行其后的方法changeItemList()
binditemChange="changeItemList";
1、binditemChange:其格式是bind+事件名称,事件的名称是子组件中this.triggerEvent("itemChange",{index});的事件名称
2、changeItemList():是父组件.js文件中的方法
-->
< CusComponent itemList="{{itemList}}" binditemChange="changeItemList"></CusComponent>
#父组件.js文件中的方法
changeItemList(e){
  const index = e.detail.index;
  let itemList = this.data.itemList;
  itemList.forEach((v,i) => {
    i===index?v.isActive=true:v.isActive=false
  });
  this.setData({
    itemList:itemList
  })
}

过程:
1、点击子组件后调用子组件的handleActive(e),此方法中有个自定义组件触发事件this.triggerEvent("itemChange",{index});
2、触发父组件wxml文件引用的<CusComponent/>组件中的binditemChange指向的changeItemList()方法
3、在父组件的.js文件中的changeItemList()方法中对子组件传递过来的数据进行处理

  • triggerEvent方法参数:triggerEvent('myevent',{myEventDetail},{myEventOption});

triggerEvent方法三个参数解释:
'myevent':自定义事件的名称
myEventDetail:detail对象,提供给事件监听函数,如上面传递的数据
myEventOption:触发事件的选项,不是必填项。

myEventOption的三个选项解释:
bubbles:事件是否冒泡。默认值false
composed:事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部。默认值false
capturePhase:事件是否拥有捕获阶段。默认值false

七、自定义组件中的 slot 标签

组件的写法和页面相同。组件与组件数据结合后生产的节点树,将被插入到组件的引用位置上。

在组件中提供了一个<slot>节点,用于承载组件引用时提供的子节点。

注意:在模板中引用到的自定义组件及其对应的节点名需要在 json 文件中显式定义,否则会被当作一个无意义的节点。除此以外,节点名也可以被声明为抽象节点。

#父组件wxml中引用的子组件
<cusComponent>
  <view>父组件传递过来的节点,相当于子组件中的slot</view>
</cusComponent>
#子组件wxml中
<view class="content">
  <slot></slot>
</view>

上面的代码就会让父组件传递过来的节点,相当于子组件中的slot在你的自定义组件中显示。

需要多个slot时,如何在组件js中声明:

#子组件的.js文件
Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多slot支持
  },
})

此时就可以在组件的wxml中使用多个slot,以不用的name来区分:

#子组件的.wxml文件
<view class="content">
  <slot name="name1"></slot>
  <slot name="name2"></slot>
</view>

八、自定义组件的样式

1、组件样式

组件对应 wxss 文件的样式,只对组件wxml内的节点生效。编写组件样式时,需要注意以下几点:

  • 请使用class选择器:组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器。
  • 避免使用后代选择器:组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,如遇,请避免使用。
  • 子元素选择器只能用于view及子节点:子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。
  • 继承样式:继承样式,如 font 、 color ,会从组件外继承到组件内。
  • app.wxss样式组件对自定义组件无效:除继承样式外, app.wxss 中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)。
#a { } /* 在组件中不能使用 */
[a] { } /* 在组件中不能使用 */
button { } /* 在组件中不能使用 */
.a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */

除此以外,组件可以指定它所在节点的默认样式,使用 :host 选择器(需要包含基础库 [1.7.2]。

/* 组件 custom-component.wxss */
:host {
  color: yellow;
}
2、组件样式隔离

默认情况下,自定义组件的样式只受到自定义组件 wxss 的影响。除非以下两种情况:

  • app.wxss 或页面的 wxss 中使用了标签名选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件。通常情况下这是不推荐的做法。
  • 指定特殊的样式隔离选项 styleIsolation 。
Component({
  options: {
    styleIsolation: 'isolated'
  }
})

styleIsolation的选项:
isolated 表示启用样式隔离,在自定义组件内外,使用 class 指定的样式将不会相互影响(一般情况下的默认值);
apply-shared 表示页面 wxss 样式将影响到自定义组件,但自定义组件 wxss 中指定的样式不会影响页面;
shared 表示页面 wxss 样式将影响到自定义组件,自定义组件 wxss 中指定的样式也会影响页面和其他设置了 apply-shared 或 shared 的自定义组件。(这个选项在插件中不可用。)

使用后两者时,请务必注意组件间样式的相互影响。

如果这个Component 构造器用于构造页面,则默认值为 shared ,且还有以下几个额外的样式隔离选项可用:

  • page-isolated 表示在这个页面禁用 app.wxss ,同时,页面的 wxss 不会影响到其他自定义组件;
  • page-apply-shared 表示在这个页面禁用 app.wxss ,同时,页面 wxss 样式不会影响到其他自定义组件,但设为 shared 的自定义组件会影响到页面;
  • page-shared 表示在这个页面禁用 app.wxss ,同时,页面 wxss 样式会影响到其他设为 apply-sharedshared 的自定义组件,也会受到设为 shared 的自定义组件的影响

在 Component 的 options中设置addGlobalClass: true 。 这个选项等价于设置 styleIsolation: apply-shared,但设置了 styleIsolation 选项后这个选项会失效。

/* 组件 custom-component.js */
Component({
  options: {
    addGlobalClass: true,
  }
})
3、外部样式类、引用页面或父组件的样式、虚拟化组件节点

参见官方文档:https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html

写在最后:

如果文章中有错误或是表达不准确的地方,欢迎大家评论中指正,以便我完善。
文章我也会根据所学到新的知识不断更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341