最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 小程序-自定义组件

    正文概述 掘金(----影子)   2021-01-22   469

    小程序-自定义组件

    组件模板和样式

    1.组件模板

    • 新建components文件夹存放我们要自定义的组件
    • 在components下新建文件夹index,然后在index文件右击选择新建Component,输入index。这样就创建了index组件,生成对应的组件模板

    组件模板中可以提供一个 <slot> 节点,用于承载组件引用时提供的子节点。

    <view class="wrapper">
      <view>这里是组件的内部节点</view>
      <slot></slot>
    </view>
    
    <!-- 引用组件的页面模板 -->
    <view>
      <index>
        <!-- 这部分内容将被放置在组件 <slot> 的位置上 -->
        <view>这里是插入到组件slot中的内容</view>
      </index>
    </view>
    
    • 在引入组件的页面模板的json文件显式定义,否则会被当作一个无意义的节点
    // home页面--home.json
    {
      "usingComponents": {
        "index": "/components/index/index"
      }
    }
    

    2.组件wxml的slot

    默认情况下,一个组件的 wxml 中只能有一个 slot 。需要使用多 slot 时,可以在组件 js 中声明启用。

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

    此时,可以在index这个组件的 wxml 中使用多个 slot ,以不同的 name 来区分。

    <!-- 组件模板 -->
    <view class="wrapper">
      <slot name="before"></slot>
      <view>这里是组件的内部细节</view>
      <slot name="after"></slot>
    </view>
    

    使用时,用 slot 属性来将节点插入到不同的 slot 上。

    <!-- 引用组件的页面模板 -->
    <view>
      <index>
        <!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 -->
        <view slot="before">这里是插入到组件slot name="before"中的内容</view>
        <!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 -->
        <view slot="after">这里是插入到组件slot name="after"中的内容</view>
      </index>
    </view>
    
    

    3.组件样式

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

    • 组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器。
    • 继承样式,如 fontcolor ,会从组件外继承到组件内。
    • 除继承样式外, app.wxss 中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)。
    #a { } /* 在组件中不能使用 */
    [a] { } /* 在组件中不能使用 */
    button { } /* 在组件中不能使用 */
    .a > .b { } /* 除非 .a 是 view 组件节点,否则不一定会生效 */
    

    4.组件样式隔离

    默认情况下,自定义组件的样式只受到自定义组件 wxss 的影响。

    除非以下两种情况:

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

    styleIsolation它支持以下取值:

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

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

    注意:

    • apply-shared 但想在自己定义的组件中使用全局的样式是设置,自定义组件里的样式不会影响到页面组件,但是页面组件的样式会影响自定义组件样式

    • 还有一种写法是 addGlobalClass: true 这种写法和设置styleIsolation:‘apply-shared’ 一样

      Component({
        options: {
          addGlobalClass: true
        }
      })
        
      Component({
        options: {
          styleIsolation: 'apply-shared'
        }
      })
      
    // 设置了apply-shared 的自定义组件
    // 可以使用app.wxss 和home.wxss的样式
    // 也就是说可以使用外部的所有样式,但是自己的内部的样式,外部是不可以使用的
    
    • shared 这个的话就是相互影响了

    5.外部样式类

    有时,组件希望接受外部传入的样式类。此时可以在 Component 中用 externalClasses 定义段定义若干个外部样式类。

    这个特性可以用于实现类似于 view 组件的 hover-class 属性:页面可以提供一个样式类,赋予 viewhover-class ,这个样式类本身写在页面中而非 view 组件的实现中。

    注意:在同一个节点上使用普通样式类和外部样式类时,两个类的优先级是未定义的,因此最好避免这种情况。

    /* 组件 custom-component.js */
    Component({
      externalClasses: ['my-class']
    })
    
    <!-- 组件 custom-component.wxml -->
    <custom-component class="my-class">这段文本的颜色由组件外的 class 决定</custom-component>
    

    这样,组件的使用者可以指定这个样式类对应的 class ,就像使用普通属性一样。

    <!-- 页面的 WXML -->
    <custom-component my-class="red-text" />
    <custom-component my-class="large-text" />
    <!-- 以下写法需要基础库版本 2.7.1 以上 -->
    <custom-component my-class="red-text large-text" />
    .red-text {
      color: red;
    }
    .large-text {
      font-size: 1.5em;
    }
    

    6. 虚拟化组件节点

    默认情况下,自定义组件本身的那个节点是一个“普通”的节点,使用时可以在这个节点上设置 class style 、动画、 flex 布局等,就如同普通的 view 组件节点一样。

    <!-- 页面的 WXML -->
    <view style="display: flex">
      <!-- 默认情况下,这是一个普通的节点 -->
      <custom-component style="color: blue; flex: 1">蓝色、满宽的</custom-component>
    </view>
    
    

    但有些时候,自定义组件并不希望这个节点本身可以设置样式、响应 flex 布局等,而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定。

    这种情况下,可以将这个自定义组件设置为“虚拟的”:

    Component({
      options: {
        virtualHost: true
      },
      properties: {
        style: { // 定义 style 属性可以拿到 style 属性上设置的值
          type: String,
        }
      },
      externalClasses: ['class'], // 可以将 class 设为 externalClasses
    })
    

    这样,可以将 flex 放入自定义组件内:

    <!-- 页面的 WXML -->
    <view style="display: flex">
      <!-- 如果设置了 virtualHost ,节点上的样式将失效 -->
      <custom-component style="color: blue">不是蓝色的</custom-component>
    </view>
    <!-- custom-component.wxml -->
    <view style="flex: 1">
      满宽的
      <slot></slot>
    </view>
    

    需要注意的是,自定义组件节点上的 class style 和动画将不再生效,但仍可以:

    • 将 style 定义成 properties 属性来获取 style 上设置的值;
    • 将 class 定义成 externalClasses 外部样式类使得自定义组件 wxml 可以使用 class 值。

    Component 构造器

    1.Component 构造器

    Component构造器可用于定义组件,调用Component` 构造器时可以指定组件的属性、数据、方法等。

    Component({
    
      behaviors: [],
    
      properties: {
        myProperty: { // 属性名
          type: String,
          value: ''
        },
        myProperty2: String // 简化的定义方式
      },
      
      data: {}, // 私有数据,可用于模板渲染
    
      lifetimes: {
        // 生命周期函数,可以为函数,或一个在methods段中定义的方法名
        attached: function () { },
        moved: function () { },
        detached: function () { },
      },
    
      // 生命周期函数,可以为函数,或一个在methods段中定义的方法名
      attached: function () { }, // 此处attached的声明会被lifetimes字段中的声明覆盖
      ready: function() { },
    
      pageLifetimes: {
        // 组件所在页面的生命周期函数
        show: function () { },
        hide: function () { },
        resize: function () { },
      },
        methods: {
        onMyButtonTap: function(){
          this.setData({
            // 更新属性和数据的方法与更新页面数据的方法类似
          })
        },
        // 内部方法建议以下划线开头
        _myPrivateMethod: function(){
          // 这里将 data.A[0].B 设为 'myPrivateData'
          this.setData({
            'A[0].B': 'myPrivateData'
          })
        },
        _propertyChange: function(newVal, oldVal) {
    
        }
      }
    
    })
    

    2.使用 Component 构造器构造页面

    事实上,小程序的页面也可以视为自定义组件。因而,页面也可以使用 Component 构造器构造,拥有与普通组件一样的定义段与实例方法。但此时要求对应 json 文件中包含 usingComponents 定义段。

    此时,组件的属性可以用于接收页面的参数,如访问页面 /pages/index/index?paramA=123&paramB=xyz ,如果声明有属性 paramAparamB ,则它们会被赋值为 123xyz

    页面的生命周期方法(即 on 开头的方法),应写在 methods 定义段中。

    代码示例:

    {
      "usingComponents": {}
    }
    Component({
    
      properties: {
        paramA: Number,
        paramB: String,
      },
    
      methods: {
        onLoad: function() {
          this.data.paramA // 页面参数 paramA 的值
          this.data.paramB // 页面参数 paramB 的值
        }
      }
    
    })
    
    

    使用 Component 构造器来构造页面的一个好处是可以使用 behaviors 来提取所有页面中公用的代码段。

    例如,在所有页面被创建和销毁时都要执行同一段代码,就可以把这段代码提取到 behaviors 中。

    代码示例:

    // page-common-behavior.js
    module.exports = Behavior({
      attached: function() {
        // 页面创建时执行
        console.info('Page loaded!')
      },
      detached: function() {
        // 页面销毁时执行
        console.info('Page unloaded!')
      }
    })
    
    // 页面 A
    var pageCommonBehavior = require('./page-common-behavior')
    Component({
      behaviors: [pageCommonBehavior],
      data: { /* ... */ },
      methods: { /* ... */ },
    })
    // 页面 B
    var pageCommonBehavior = require('./page-common-behavior')
    Component({
      behaviors: [pageCommonBehavior],
      data: { /* ... */ },
      methods: { /* ... */ },
    })
    

    组件间通信与事件

    1.组件间通信

    组件间的基本通信方式有以下几种。

    • WXML 数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据
    • 事件:用于子组件向父组件传递数据,可以传递任意数据。
    • 如果以上两种方式不足以满足需要,父组件还可以通过 this.selectComponent 方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。
    父传子

    与普通的 WXML 模板类似,可以使用数据绑定,这样就可以向子组件的属性传递动态数据。

    // components/bill/bill.js 子组件接收父组件传来的值
    Component({
        properties: {
            bill: Object,
            index: Number,
        }
        methods: {
    
        }
    })
    
     <bill bill="{{ billItem }}" index="{{ index }}"/>
    
    子传父

    监听事件

    事件系统是组件间通信的主要方式之一。自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件。

    注意:监听自定义组件事件的方法与监听基础组件事件的方法完全一致

    <!-- 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法 -->
    <component-tag-name bindmyevent="onMyEvent" />
    <!-- 或者可以写成 -->
    <component-tag-name bind:myevent="onMyEvent" />
    Page({
      onMyEvent: function(e){
        e.detail // 自定义组件触发事件时提供的detail对象
      }
    })
    
    

    触发事件

    自定义组件触发事件时,需要使用 triggerEvent 方法,指定事件名、detail对象和事件选项:

    <!-- 在自定义组件中 -->
    <button bindtap="onTap">点击这个按钮将触发“myevent”事件</button>
    Component({
      properties: {},
      methods: {
        onTap: function(){
          var myEventDetail = {} // detail对象,提供给事件监听函数
          var myEventOption = {} // 触发事件的选项
          this.triggerEvent('myevent', myEventDetail, myEventOption)
        }
      }
    })
    

    小程序-自定义组件

    // 页面 page.wxml
    <another-component bindcustomevent="pageEventListener1">
      <my-component bindcustomevent="pageEventListener2"></my-component>
    </another-component>
    // 组件 another-component.wxml
    <view bindtap="anotherEventListener">
      <slot />
    </view>
    // 组件 my-component.wxml
    <view bindtap="myEventListener">
      <slot />
    </view>
    
    // 组件 my-component.js
    Component({
      methods: {
        onTap: function(){
          this.triggerEvent('customevent', {}) // 只会触发 pageEventListener2
          this.triggerEvent('customevent', {}, { bubbles: true }) // 会依次触发 pageEventListener2 、 pageEventListener1
          this.triggerEvent('customevent', {}, { bubbles: true, composed: true }) // 会依次触发 pageEventListener2 、 anotherEventListener 、 pageEventListener1
        }
      }
    })
    

    这个pageEventListener1的内容岂不是会触发两次

    获取组件实例

    可在父组件里调用 this.selectComponent ,获取子组件的实例对象。(插件的自定义组件将返回 null

    在当前页面下选择第一个匹配选择器 selector 的节点。返回一个 NodesRef 对象实例,可以用于获取节点信息。

    调用时需要传入一个匹配选择器 selector,如:this.selectComponent(".my-component")

    // 父组件
    Page({
      data: {},
      getChildComponent: function () {
        const child = this.selectComponent('.my-component');
        console.log(child)
      }
    })
    

    在上例中,父组件将会获取 classmy-component 的子组件实例对象,即子组件的 this

    组件生命周期

    1.组件生命周期

    组件的生命周期,指的是组件自身的一些函数,这些函数在特殊的时间点或遇到一些特殊的框架事件时被自动触发。

    其中,最重要的生命周期是 created attached detached ,包含一个组件实例生命流程的最主要时间点。

    • 组件实例刚刚被创建好时, created 生命周期被触发。此时,组件数据 this.data 就是在 Component 构造器中定义的数据 data此时还不能调用 setData 通常情况下,这个生命周期只应该用于给组件 this 添加一些自定义属性字段。
    • 在组件完全初始化完毕、进入页面节点树后, attached 生命周期被触发。此时, this.data 已被初始化为组件的当前值。这个生命周期很有用,绝大多数初始化工作可以在这个时机进行。
    • 在组件离开页面节点树后, detached 生命周期被触发。退出一个页面时,如果组件还在页面节点树中,则 detached 会被触发。

    2.定义生命周期方法

    组件的的生命周期也可以在 lifetimes 字段内进行声明(这是推荐的方式,其优先级最高)。

    Component({
      lifetimes: {
        attached: function() {
          // 在组件实例进入页面节点树时执行
        },
        detached: function() {
          // 在组件实例被从页面节点树移除时执行
        },
      },
      // 以下是旧式的定义方式,可以保持对 <2.2.3 版本基础库的兼容
      attached: function() {
        // 在组件实例进入页面节点树时执行
      },
      detached: function() {
        // 在组件实例被从页面节点树移除时执行
      },
      // ...
    })
    

    小程序-自定义组件

    3.组件所在页面的生命周期

    还有一些特殊的生命周期,它们并非与组件有很强的关联,但有时组件需要获知,以便组件内部处理。这样的生命周期称为“组件所在页面的生命周期”,在 pageLifetimes 定义段中定义。其中可用的生命周期包括: 小程序-自定义组件

    Component({
      pageLifetimes: {
        show: function() {
          // 页面被展示
        },
        hide: function() {
          // 页面被隐藏
        },
        resize: function(size) {
          // 页面尺寸变化
        }
      }
    })
    ### behaviors
    
    `behaviors` 是用于组件间代码共享的特性,类似于一些编程语言中的 “mixins” 或 “traits”。
    
    每个 `behavior` 可以包含一组属性、数据、生命周期函数和方法。**组件引用它时,它的属性、数据和方法会被合并到组件中,生命周期函数也会在对应时机被调用。** 每个组件可以引用多个 `behavior` ,`behavior` 也可以引用其它 `behavior` 。
    
    ### 组件间关系
    
    ```javascript
    <custom-ul>
      <custom-li> item 1 </custom-li>
      <custom-li> item 2 </custom-li>
    </custom-ul>
    
    

    这个例子中, custom-ulcustom-li 都是自定义组件,它们有相互间的关系,相互间的通信往往比较复杂。此时在组件定义时加入 relations 定义段,可以解决这样的问题。

    数据监听器

    数据监听器可以用于监听和响应任何属性和数据字段的变化。从小程序基础库版本 2.6.1 开始支持。

    1.使用数据监听器

    有时,在一些数据字段被 setData 设置时,需要执行一些操作。

    例如, this.data.sum 永远是 this.data.numberAthis.data.numberB 的和。此时,可以使用数据监听器进行如下实现。

    Component({
      attached: function() {
        this.setData({
          numberA: 1,
          numberB: 2,
        })
      },
      observers: {
        'numberA, numberB': function(numberA, numberB) {
          // 在 numberA 或者 numberB 被设置时,执行这个函数
          this.setData({
            sum: numberA + numberB
          })
        }
      }
    })
    

    2.监听字段语法

    数据监听器支持监听属性或内部数据的变化,可以同时监听多个。一次 setData 最多触发每个监听器一次。

    同时,监听器可以监听子数据字段,如下例所示。

    Component({
      observers: {
        'some.subfield': function(subfield) {
          // 使用 setData 设置 this.data.some.subfield 时触发
          // (除此以外,使用 setData 设置 this.data.some 也会触发)
          subfield === this.data.some.subfield
        },
        'arr[12]': function(arr12) {
          // 使用 setData 设置 this.data.arr[12] 时触发
          // (除此以外,使用 setData 设置 this.data.arr 也会触发)
          arr12 === this.data.arr[12]
        },
      }
    })
    

    如果需要监听所有子数据字段的变化,可以使用通配符 **

    Component({
      observers: {
        'some.field.**': function(field) {
          // 使用 setData 设置 this.data.some.field 本身或其下任何子数据字段时触发
          // (除此以外,使用 setData 设置 this.data.some 也会触发)
          field === this.data.some.field
        },
      },
      attached: function() {
        // 这样会触发上面的 observer
        this.setData({
          'some.field': { /* ... */ }
        })
        // 这样也会触发上面的 observer
        this.setData({
          'some.field.xxx': { /* ... */ }
        })
        // 这样还是会触发上面的 observer
        this.setData({
          'some': { /* ... */ }
        })
      }
    })
    

    特别地,仅使用通配符 ** 可以监听全部 setData 。

    Component({
      observers: {
        '**': function() {
          // 每次 setData 都触发
        },
      },
    })
    

    注意:

    • 数据监听器监听的是 setData 涉及到的数据字段,即使这些数据字段的值没有发生变化,数据监听器依然会被触发。
    • 如果在数据监听器函数中使用 setData 设置本身监听的数据字段,可能会导致死循环,需要特别留意。
    • 数据监听器和属性的 observer 相比,数据监听器更强大且通常具有更好的性能。

    下载网 » 小程序-自定义组件

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元