微信小程序自定义组件

好吧,突然发现学不完了,一下子,那就分开吧,由于时间太久,直接重新大致复习了一下

微信小程序自定义组件
微信小程序支持自定义组件
下方的目录


image

其中,components为组件目录,nodemodules为模块目录,pages为小程序的页面目录,utils为一些基础功能的封装。好比安装的第三方百度统计功能在此。

总说

创建一个组件

一个组件包括json,wxml,wxss,js四个文件组成。
wxml文件如下

<view class="inner">
  {{innerText}}
</view>
<slot></slot>

编写js文件

// components/component.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    innerText: {
      type: String,
      value: 'hello world'
    }
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   */
  methods: {

  }
})

完成对组件的初始化,包括设置属性列表,初始化数据,以及设置相关的方法。

使用自定义组件

需要在json文件中声明。

{
  "usingComponents": {
    "component": "/components/component"
  }
}

在page目录下设置组件的声明,

<view><component></component></view>

好啦,上方的是一个最简单的自定义组件

组件模板和样式

组件模板

组件模板中有一个

<slot>

用于承载组件引用的时候提供的子节点。

ps 在page中引用的组件,必须在json文件中进行声明。否则为无效节点

在wxml文件中

<view>
<component>
  <text>这是文字</text>
</component>
</view>

而模板文件如下

<view class="inner">
  {{innerText}}
</view>
<slot></slot>

这样在page中的text节点会加载在slot节点的位置中。

使用多个slot

如果要使用多个slot需要在js文件中声明

  options: {
    multipleSlots: true // 允许组件中使用多个slot
  }

然后在组件的wxml文件中设置slot的内容

<view class="inner">
  <slot name="before"></slot>
  {{innerText}}
  <text>这是组件的内部内容</text>
  <slot name="after"></slot>
</view>

使用slot的name属性确定组件的内容
在page页面中,使用该组件

<view>
<component>
  <text slot="before">这是文字</text>
  <text slot="after">hello world</text>
</component>
</view>

组件样式

对于组件样式来说,使用

:host

来指定组件的默认样式。
即可指定默认样式

外部样式类

使用外部传入的属性,在component中使用,即使用page中的wxss。
直接在component构造函数中externalClasses属性中,使用数组。

在同一个节点上使用普通样式和外部样式,两个权重是相同的,会造成不可思议的bug产生。

在组件中,使用components构造函数的externalClasses属性确定外部样式表的名称

externalClasses: ['my-class']

然后在组件的wxml文件中,使用该外部样式表

<view class="my-class">
  <slot name="before"></slot>
  {{innerText}}
  <text>这是组件的内部内容</text>
  <slot name="after"></slot>
</view>

然后,在page中使用

<view>
<component class="my-class">
  <text slot="before">这是文字</text>
  <text slot="after">hello world</text>
</component>
</view>

是滴,直接使用page中的样式表,page中的样式表

.my-class {
  color:yellow
}

全局样式表

使用全局样式表设置js文件中的options对象中的addGlobalClass属性为true可以使用全局样式表

component构造器

使用component构造器,进行构造。
该构造函数用于定义组件。调用Component函数能指定组件的数据,属性和方法。

这个和视图层的page构造函数很类似。

在properties定义段中,属性名采用驼峰命名法,wxml采用连字符的命名,之间相互转换。

tips 在网页中,也有这一点。

定义组件的js

Component({
  behaviors: [], // 进行代码复用机制

  properties: {
    myProperty: { // 属性名
      type: String, // 属性的类型(必填)
      value: '',  // 属性的初值
      observer: (newValue, oldValue, changedPath) => {
        console.log(newValue);
        console.log(oldValue)
      } 
    },
    myProperty2: String // 一种简化的定义方式
  },
  data: {},  // 私有数据,用于模板渲染

  lifetimes: {
    // 组件的生命周期函数,用于声明组件的生命周期
    attached: () => {},
    moved: () => {},
    detached: () => {}
  },

  pageLifetimes: {
    // 组件所在页面的生命周期函数
    show: () => {},
    hide: () => {}
  },

  methods: {
    // 组件的方法,其中下划线开头为私有方法
    onMyButtonTap: function() {
      this.setData({
        myProperty: "hello world"
      })
    },
    // 内部方法,使用下划线开头
    _myPrivateMethod: function() {
      this.setData({
        'A[0].B': 'myPrivateData'
      })
    }
  }
})

定义组件的wxml

<custom-component>
  <view>{{myProperty}}</view>
  <button bindtap='onMyButtonTap'>Button</button>
  <view>{{A[0].B}}</view>
  <button bindtap='_myPrivateMethod'>_myPrivateMethod</button>
</custom-component>

然后在page中使用该组件

在这之前json中设置该组件

<my-component />

接着运行如下


image

使用Component构造函数构造页面

小程序的页面可以视为自定义组件,因此,页面同样可以使用Component构造函数构造,此时要求对应的json文件拥有usingComponents定义段
此时组件的属性可以用于接收页面的参数,
在app.json文件中添加一个页面,并在导航栏设置该页面

{
  "pages":[
    "pages/helloWorld/hello",
    "pages/index/index",
    "pages/logs/logs",
    "components/component"
  ],
  "window":{
    "backgroundTextStyle":"dark",
    "navigationBarBackgroundColor": "#c7dbc8",
    "navigationBarTitleText": "小小",
    "navigationBarTextStyle":"whiter",
    "enablePullDownRefresh": true,
    "backgroundColor": "#fceaea"
  },
  "tabBar": {
    "list": [{
      "text": "ming",
      "pagePath": "pages/logs/logs"
    }, {
      "text":"home",
      "pagePath": "pages/index/index"
    }, {
      "text": "hello",
      "pagePath": "pages/helloWorld/hello"
    }, {
      "text": "ing",
      "pagePath": "components/component"
    }],
    "color": "#f8fcea",
    "backgroundColor": "#ff9999",
    "selectedColor": "#c5ff99",
    "borderStyle": "white",
    "position": "top"
  },
  "functionalPages": true
}

添加的参数为pages参数,以及tabBar参数

并设置编译模式,设置页面的启动参数

组件的js文件如下

// components/component.js
Component({
  /**
   *  组件配置 
   **/
  options: {
    multipleSlots: true // 允许组件中使用多个slot
  },
  /**
   * 组件的属性列表
   */
  properties: {
    innerText: {
      type: String,
      value: 'hello world'
    },
    paramA: Number,
    paramB: Number,
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   */
  methods: {
    onLoad: function() {
      console.log(this.data.paramA);
      console.log(this.data.paramB);
    }
  },

  /**
   *  使用外部样式表 
   **/
  externalClasses: ['my-class']
})

添加了一个properties,并添加一个methods事件,在页面加载的时候,触发该事件。
启动编译,控制台输出当前页面参数,参数获取完成。

组件间通信和事件

通信的几种方法

WXML数据绑定,用于父组件,向子组件指定的属性设置数据。此方法仅仅能设置JSON数据。
事件,用于子组件向父组件传递数据,可以传递任意数据。

监听事件

用于监听子组件,如果子组件被触发,则父组件将会触发该事件。

<view>
<component class="my-class" bindmyevent="onMyEvent">
  <text slot="before">这是文字</text>
  <text slot="after">hello world</text>
</component>
</view>

当子组件触发了myevent事件时候,将会调用onMyEvent方法。

    onMyEvent: function(e) {
      console.log(e.detail);  // 自定义组件触发的时候。提供的detail事件。将会返回当前点击的鼠标坐标
    }

触发事件

使用triggerEvent方法,完成对父组件事件的触发
wxml中添加

<button bindtap="onTap">点击按钮</button>

然后在js中添加

onTap: function() {
      var myEventDetail = {}; // 提供给事件的监听函数
      var myEventOption = {}; // 触发事件的选项
      this.triggerEvent('myevent', myEventDetail, myEventOption)
    },

确保该组件将会有myevent供父组件进行触发

类似于网页中的自定义组件

完成绑定以后,由于上一节,父组件以及完成事件的监听,此时点击组件内的按钮,将会完成父组件绑定的事件的触发

由于冒泡和传播的存在,父组件依旧可以通过冒泡和传播来进行获取

triggerEvent方法详细解释

有三个参数,第一个参数为暴露给父节点的事件类型。第二个参数为向父组件传递的数据,第三个参数为选项,传入对象进去

向父组件传递数据

组件的js文件中

    onTap: function() {
      var myEventDetail = {
        a:3,
        b:4,
        c:5
      }; // 提供给事件的监听函数
      var myEventOption = {}; // 触发事件的选项
      this.triggerEvent('myevent', myEventDetail, myEventOption)
    },

将对象传入第二个选项中
接收数据,在父组件的e.detail中接收子传给父的内容
完成了数据从子传递到父

上上上节介绍了父传递到子的过程

第三个参数

bubbles

该选型确定的是是否冒泡
由于composed默认为false则该事件只在主树上触发,不会进入任何其他组件的内部。
编写两个嵌套的组件
在components目录下继续新建一个目录为body

原谅自己的命名技术,component命名╮(╯▽╰)╭

配置页面的json文件信息

{
  "usingComponents": {
    "component": "/components/component",
    "body": "/components/body/body"
  }
}

接着继续配置
书写页面的wxml文件

<body>
  <component/>
</body>

由于是在原有基础上添加,继续书写body组件的内容,确定挂载点

<view>
  <slot/>
</view>

编译一个最基本的组件嵌套完成。为body组件,嵌套一个component组件
接着进行事件的绑定
书写内层组件的bubbles,为允许进行冒泡

 var myEventOption = {
        bubbles: true
      }; // 触发事件的选项

即完成允许冒泡。
接着,继续,书写pages的wxml文件,完成事件和节点的绑定

<body bindmyevent="list2">
  <component bindmyevent="list1"/>
</body>

接着单击按钮完成事件的触发


image

至此,完成了事件的冒泡。

componse

确定事件是否进入内部,即,是否触发组件内部
接着,在原来的代码的基础上继续添加内容。
由于内部组件依旧是一个事件类型为myevent
接着,修改内部组件的配置

onTap: function() {
      var myEventDetail = {
        a:3,
        b:4,
        c:5
      }; // 提供给事件的监听函数
      var myEventOption = {
        bubbles: true,
        composed: true
      }; // 触发事件的选项
      this.triggerEvent('myevent', myEventDetail, myEventOption)
    },
    onMyEvent: function(e) {
      console.log(e.detail);  // 自定义组件触发的时候。提供的detail事件。
    }

修改composed的值为true。
绑定body的事件

<view bindmyevent="list3">
  <slot/>
</view>

绑定body事件完成以后,接着单击按钮,触发事件
触发过程为1,3,2说明事件是先进入父组件,触发父组件的事件完成以后,在继续触发引用组件的节点树上的事件。

capturePhase

为事件是否有捕获阶段。
由于事件是先冒泡,后捕获,所以必须要先进行冒泡
修改配置如下

      var myEventOption = {
        bubbles: false,
        composed: true,
        capturePhase: true
      }; // 触发事件的选项

然后触发事件,由于是捕获,将会触发回调myevent的list1的回调函数进行处理。

behaviors

一种代码复用机制

类似于C++的模板 ?? 确定吗? 木有学习过c++,其实我一直在思考css文件如何实现复用。因为我不想写大段代码呀,(@ο@) 哇~

每个behavior都会包含一组属性,数据,生命周期函数和方法。组件引用的时候,上述将会合并

类似于深拷贝,不过js中的深拷贝是直接开辟了一块新的储存空间,浅拷贝属于直接进行引用,js进行赋值操作执行的是浅拷贝

使用Behavior()构造函数,进行构造出代码的复用
文件目录如下


image

微信的路径太坑了。

模板文件如下

module.exports = Behavior({
  behaviors: [],

  properties: { // 外部数据
    myBehaviorProperty: {
      type: String,
      value: 'hello world'
    }
  }
})

属于配置项目,接着继续书写内部组件

// components/component.js
var myBehavior = require('/behaviors/behavior')
Component({
  // 代码复用
  behaviors: [myBehavior],
  /**
   *  组件配置 
   **/
  options: {
    multipleSlots: false // 允许组件中使用多个slot
  },
  /**
   * 组件的属性列表
   */
  properties: {
    innerText: {
      type: String,
      value: 'hello world'
    },
    paramA: Number,
    paramB: Number,
  },

  /**
   * 组件的初始数据
   */
  data: {

  },

  /**
   * 组件的方法列表
   */
  methods: {
    onLoad: function() {
      console.log(this.data.paramA);
      console.log(this.data.paramB);
    },
    onTap: function() {
      var myEventDetail = {
        a:3,
        b:4,
        c:5
      }; // 提供给事件的监听函数
      var myEventOption = {
        bubbles: false,
        composed: true,
        capturePhase: true
      }; // 触发事件的选项
      this.triggerEvent('myevent', myEventDetail, myEventOption)
    },
    onMyEvent: function(e) {
      console.log(e.detail);  // 自定义组件触发的时候。提供的detail事件。
    }
  },

  /**
   *  使用外部样式表 
   **/
  externalClasses: ['my-class']
})

在最上方引入文件,(微信小程序的路径一个大坑),接着在behaviors引入即可。可以behaviors引入behaviors即包的相互依赖。

构建一个复杂的程序很有必要进行分包

内置的behaviors

wx://form-field

使得自定义组件有类似表单控件的功能,将会在页面触发submit事件的时候将会直接附带提交
演示
先创建一个组件
此时目录结构


image

好吧。是有一点复杂了
添加内置组件,并设置data数据中的value的值

// components/custom-form-field/custom-form-field.js
Component({
  /**
   *  代码复用 .让其拥有表单属性
   **/
  behaviors: ['wx://form-field'],
  /**
   * 组件的属性列表
   */
  properties: {
    value: {
      type: String,
      value: "hello world"
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
  },


  /**
   * 组件的方法列表
   */
  methods: {

  }
})

接着书写page页面中的内容
引入组件

{
  "usingComponents": {
    "component": "/components/component",
    "body": "/components/body/body",
    "custom-form-field": "/components/custom-form-field/custom-form-field"
  }
}

完后接着继续书写wxml中添加该表单组件,并添加提交按钮

<form bindsubmit="formSubmit">
  <custom-form-field name="custom-name">hello world</custom-form-field>
  <button form-type="submit">提交</button>
</form>

添加事件的处理程序

  formSubmit: function (e) {
    console.log('form', e.detail.value)
    console.log(333)
  }

单击按钮,控制台输出键值对,到此完成。

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

推荐阅读更多精彩内容