小程序 自定义组件Component

1、创建组件
	(1)一个自定义组件由json、wxml、wxss、js这4个文件组成
	(2)在对应json文件中声明为自定义组件
		{
		  "component": true
		
		}
		
2、组件配置
	Component({
	
	  类似mixins的复用机制
	  behaviors: [],	
	  
	  传递给组件的属性,是属性名到属性设置的映射表,必须定义后才能使用
	  properties: {		
	  	使用:this.data/this.properties,页面中:{{属性名}}
	  	
	    属性名: { 	属性名采用驼峰写法(propertyName),在wxml中,指定属性值时则对应使用连字符写法(component-tag-nameproperty-name="attrvalue"),应用于数据绑定时采用驼峰写法(attr="")
	      type: String,
	      optionalTypes: [String, Object],	指定多个类型
	      value: '默认值',
	      observer: function(newVal, oldVal) {
	        属性值变化时执行,在新版本建议使用外部的observers字段代替,它更加强大且性能更好。

	      }
	      
	    },
	    属性名: String 	简化的定义方式
	    
	  },
	  
	  私有数据,搭配properties模板渲染
	  data: {},
	  
	  组件生命周期
	  lifetimes: {
	  	可以为函数,或一个在methods段中定义的方法名
	    created:fn(){}	 	在组件实例刚刚被创建时执行,组件数据this.data就是在Component构造器中定义的数据 data,此时还不能调用setData ,通常情况下,这个生命周期只应该用于给组件this添加一些自定义属性字段。 	
		attached:fn(){} 	在组件实例进入页面节点树时执行 	
		ready:fn(){} 	 	在组件在视图层布局完成后执行 	
		moved:fn(){} 	 	在组件实例被移动到节点树另一个位置时执行 	
		detached:fn(){} 	在组件实例被从页面节点树移除时执行 	
		error:fn(err){} 	每当组件方法抛出错误时执行 
	  },
	  此处的声明会被lifetimes字段中的声明覆盖
	  attached: function () { },
	  ready: function() { },
	  
	  组件所在页面的生命周期函数
	  pageLifetimes: {
		show:fn(){} 	 	组件所在的页面被展示时执行 	
		hide:fn(){} 	 	组件所在的页面被隐藏时执行 	
		resize:fn(size){} 	组件所在的页面尺寸变化时执行 	
	  },
	
	  methods: {
	    _myPrivateMethod: function(){},	内部方法建议以下划线开头
	  }
	
	})


3、组件样式
	.组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器。
	.组件和引用组件的页面中使用后代选择器(.a .b)在一些极端情况下会有非预期的表现,如遇,请避免使用。
	.子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况。
	.继承样式,如font 、 color会从组件外继承到组件内。
	.除继承样式外,app.wxss中的样式、组件所在页面的的样式对自定义组件无效(除非更改组件样式隔离选项)。
	
	:host{
		指定当前组件节点的默认样式
	}
	
	(1)组件样式隔离
		方式一:
			Component({
			  options: {
			    styleIsolation: 'isolated'	页面的wxss中使用了标签名选择器(或一些其他特殊选择器)来直接指定样式,这些选择器会影响到页面和全部组件,设置styleIsolation: 'isolated'来进行隔离
			    addGlobalClass: true,	等价于设置styleIsolation:apply-shared,该选项优先级低于styleIsolation
			  }
			})
	  	方式二:也可以在页面或自定义组件的json文件中配置styleIsolation
		  	{
			  "styleIsolation": "isolated"
			}

		    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-shared 或 shared 的自定义组件,也会受到设为 shared 的自定义组件的影响。
	
	(2)使用外部样式类
		组件内:
			Component({
			  externalClasses: ['my-class']
			})
			<text class="my-class">pages/comp.wxml</text>
			
		组件外:
			<component-tag-name my-class='类名'></<component-tag-name>
			
	(3)引用页面或上层组件的样式
		<view class="~blue-text"> 这段文本是蓝色的 </view>	 ~引用页面的样式类
		<view class="^red-text"> 这段文本是红色的 </view>	 ^引用上层组件的样式类,连续使用多个 ^ 来引用更上层组件中的样式
	
	(4)让自定义组件上设置的样式失效,由内部决定样式
		默认情况下,自定义组件本身的那个节点是一个“普通”的节点,使用时可以在这个节点上设置 class style 、动画、 flex布局等,就如同普通的 view 组件节点一样。
		希望自定义组件内部的第一层节点由自定义组件本身完全决定。
		Component({
		  options: {
		    virtualHost: true
		  }
		})
		<custom-component style="color: blue"></custom-component> 
			自定义组件上的样式将失效,但仍可以:
			    .将style定义成properties属性来获取style上设置的值;
				.将class定义成externalClasses外部样式类使得自定义组件wxml可以使用class值。
		

	
	
3、使用组件
	(1)在使用组件的对应json文件中引入组件
		{
		  "usingComponents": {
		    "小写字母-分割的组件名": "/必须'/'开头从顶层目录开始查找"
		  }
		}

4、组件监听事件,组件内部必须使用triggerEvent触发
	页面:
		<component-tag-name bindmyevent="onMyEvent" />
		Page({
		  onMyEvent: function(e){
		    e.detail 	自定义组件触发事件时提供的detail对象
		  }
		})
	组件:
		Component({
		  properties: {},
		  methods: {
		    onTap: function(){
		    
		      var myEventDetail = {} 	detail对象,提供给事件监听函数
		      var myEventOption = {		触发事件的选项
		      	bubbles			事件是否冒泡
				composed 	事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部触发相同名称的事件
				capturePhase	事件是否拥有捕获阶段
		      } 	
		      this.triggerEvent('myevent', myEventDetail, myEventOption)
		      
		    }
		  }
		})
		
4.5、组件双向绑定
	(1)定义传入的属性格式
		Component({
		  properties: {
		    myValue: String
		  }
		})
	
	(2)组件中使用
		<input model:value="{{myValue}}" />
	
	(3)引用该组件的地方
		<custom-component model:my-value="{{属性}}" />	my-value可以为任意字段,在组件中要写成驼峰的形式
		
5、组件behaviors实现minxins等复用
	        	
	(1)创建behaviors
		module.exports = Behavior({
		  behaviors: [],	子behaviors
		  properties: {},	同组件的属性
		  data: {},			子组件数据
		  created 	 	 	生命周期函数 	
		  attached 	 	 	生命周期函数 	
		  ready 	 	 	生命周期函数 	
		  moved 	 	 	生命周期函数 	
		  detached 	 	 	生命周期函数		  
		  methods: {}
		})
	(2)使用
		Component({
		  behaviors: [require('...')],
		  内置behaviors:
		    "wx://form-field"
			"wx://form-field-group"		详见表单文章
			"wx://form-field-button"	详见表单文章
			"wx://component-export"		详见表单文章
				使自定义组件支持export定义段,这个定义段可以用于指定组件被selectComponent调用时的返回值。
		})
	
	使用规则
	    .如果有同名的属性 (properties) 或方法 (methods):
	        若组件本身有这个属性或方法,则组件的属性或方法会覆盖 behavior 中的同名属性或方法;
	        若组件本身无这个属性或方法,则在组件的 behaviors 字段中定义靠后的 behavior 的属性或方法会覆盖靠前的同名属性或方法;
	        在 2 的基础上,若存在嵌套引用 behavior 的情况,则规则为:父 behavior 覆盖 子 behavior 中的同名属性或方法。
	    .如果有同名的数据字段 (data):
	        若同名的数据字段都是对象类型,会进行对象合并;
	        其余情况会进行数据覆盖,覆盖规则为:组件 > 父 behavior > 子 behavior 、 靠后的 behavior > 靠前的 behavior。(优先级高的覆盖优先级低的,最大的为优先级最高)
	
	    .生命周期函数不会相互覆盖,而是在对应触发时机被逐个调用:
	        对于不同的生命周期函数之间,遵循组件生命周期函数的执行顺序;
	        对于同种生命周期函数,遵循如下规则:
	            behavior 优先于组件执行;
	            子 behavior 优先于 父 behavior 执行;
	            靠前的 behavior 优先于 靠后的 behavior 执行;
	        如果同一个 behavior 被一个组件多次引用,它定义的生命周期函数只会被执行一次。
	        	    1.[my-behavior] created
				    2.[my-component] created
				    3.[my-behavior] attached
				    4.[my-component] attached
				    5.[my-behavior] ready
				    6.[my-component] ready
	
	(3)通过behaviors扩展组件能力
		// behavior3.js
		module.exports = Behavior({
		    definitionFilter(defFields, definitionFilterArr) {},
		})
		
		// behavior2.js
		module.exports = Behavior({
		  behaviors: [require('behavior3.js')],
		  definitionFilter(defFields, definitionFilterArr) {
		    // definitionFilterArr[0](defFields)
		  },
		})
		
		// behavior1.js
		module.exports = Behavior({
		  behaviors: [require('behavior2.js')],
		  definitionFilter(defFields, definitionFilterArr) {
		  	defFields.data.from = 'behavior'  修改拓展组件中的内容
		  },
		})
		
		// component.js
		Component({
		  behaviors: [require('behavior1.js')],
		})
		
		1、当进行behavior2的声明时就会调用behavior3的definitionFilter函数, 其中defFields参数是behavior2的定义段,
		  definitionFilterArr参数即为空数组, 因为behavior3没有使用其他的behavior。
		2 当进行behavior1的声明时就会调用behavior2的definitionFilter函数, 其中defFields参数是behavior1的定义段, definitionFilterArr参数是一个长度为1的数组,
		  definitionFilterArr[0]即为behavior3的definitionFilter函数, 因为behavior2使用了behavior3。用户在此处可以自行决定在进行behavior1的声明时要不要
		  调用behavior3的definitionFilter函数, 如果需要调用, 在此处补充代码definitionFilterArr[0](defFields)即可, definitionFilterArr参数会由基础库补充传入。
		3、同理,在进行component的声明时就会调用behavior1的definitionFilter函数。
		
		简单概括, definitionFilter函数可以理解为当A使用了B时, A声明就会调用B的definitionFilter函数并传入A的定义对象让B去过滤。
		此时如果B还使用了C和D, 那么B可以自行决定要不要调用C和D的definitionFilter函数去过滤A的定义对象

	
6、获取自定义组件实例
	<my-component class="my-component"></my-component>
	this.selectComponent('.my-component');
	
	获取指定内容:
		组件内:
		Component({
		  behaviors: ['wx://component-export'],
		  export() {
		    return { myField: 'myValue' }
		  }
		})

6.5、获取/关联当前组件内的其他组件实例,relations
	必须在两个组件定义中都加入relations定义,否则不会生效。
	组件中:this.getRelationNodes('使用到的组件的绝对路径')可以获得nodes数组,包含所有已关联的组件,且是有序的
	
	方式一:
		<custom-ul>
		  <custom-li> item 1 </custom-li>
		  <custom-li> item 2 </custom-li>
		</custom-ul>
		ul:
			Component({
			  relations: {
			    './custom-li': {	基于当前ul组件的子组件路径
			      type: 'child', 	关联的目标节点应为子节点
			      linked: function(target) {
			        每次有custom-li被插入时执行,target是custom-li实例对象,触发在custom-li的attached生命周期之后
			      },
			      linkChanged: function(target) {
			        每次有custom-li被移动后执行,target是custom-li实例对象,触发在custom-li的moved生命周期之后
			      },
			      unlinked: function(target) {
			        每次有custom-li被移除时执行,target是custom-li实例对象,触发在custom-li的detached生命周期之后
			      }
			    }
			  },
			  methods: {
			    _getAllLi: function(){
			      使用getRelationNodes可以获得nodes数组,包含所有已关联的custom-li,且是有序的
			      var nodes = this.getRelationNodes('path/to/custom-li')
			    }
			  },
			  ready: function(){
			    this._getAllLi()
			  }
			})
		li:
			Component({
			  relations: {
			    './custom-ul': {	基于当前组件的父组件的相对路径
			      type: 'parent', 	联的目标节点应为父节点
			      linked: function(target) {
			        每次被插入到custom-ul时执行,target是custom-ul节点实例对象,触发在attached生命周期之后
			      },
			      linkChanged: function(target) {},
			      unlinked: function(target) {}
			    }
			  }
			})
			
	方式二:通过behaviors只关联有behaviors的组件
		<custom-form>
		  <view>
		    input
		    <custom-input></custom-input>
		  </view>
		  <custom-submit> submit </custom-submit>
		</custom-form>
		
		custom-input:
			Component({
			  behaviors: [customFormControls],	得和上层关联的组件使用同一个behaviors
			  relations: {
			    './custom-form': {
			      type: 'ancestor', 关联的目标节点应为祖先节点即custom-form
			    }
			  }
			})
			
		custom-form:
			Component({
			  relations: {
			    'customFormControls': {
			      type: 'descendant', 		  关联的目标节点应为子孙节点
			      target: customFormControls  上层组件使用target指明behaviors
			    }	
			  }
			}
		
	
7、组件充当页面
	好处:
		是可以使用 behaviors 来提取所有页面中公用的代码段。
		
	(1)要求对应json文件中包含usingComponents定义段。
		{
		  "usingComponents": {}
		}

	(2)页面的生命周期方法(即on开头的方法),应写在methods定义段中,且路由等传递参数需要properties声明使用
		如访问页面 /pages/index/index?paramA=123&paramB=xyz 
		Component({
		  properties: {
		    paramA: Number,
		    paramB: String,
		  },
		  methods: {
		    onLoad: function() {
		      this.data.paramA 	页面参数 paramA 的值
		      this.data.paramB 	页面参数 paramB 的值
		    }
		  }
		})

	
8、插槽
	非具名插槽
		<component-tag-name>
			<view>这里是插入到组件slot中的内容</view>
		</component-tag-name>
		组件中
			<slot></slot>
			
	具名插槽
	  <component-tag-name>
		    <view slot="before">这里是插入到组件slot name="before"中的内容</view>
		    <view slot="after">这里是插入到组件slot name="after"中的内容</view>
	  </component-tag-name>
	 组件中
		Component({
		 options: {
		   multipleSlots: true  在组件定义时的选项中启用多slot支持
		 },
		})
		
	  <slot name="before"></slot>
	  <slot na1
	  me="after"></slot>

8.5、nextTick
	延迟一部分操作到下一个时间片再执行(类似setTimeout)
	因为自定义组件中的setData和triggerEvent等接口本身是同步的操作,当这几个接口被连续调用时,都是在一个同步流程中执行完的,因此若逻辑不当可能会导致出错。
	Component({
	  doSth() {
	    this.setData({ number: 1 }) 	直接在当前同步流程中执行
	
	    wx.nextTick(() => {
	      this.setData({ number: 3 })	在当前同步流程结束后,下一个时间片执行
	    })
	
	    this.setData({ number: 2 }) 	直接在当前同步流程中执行
	  }
	})
	  
9、组件监听
	支持监听属性或内部数据的变化,可以同时监听多个,一次 setData 最多触发每个监听器一次。
	即使这些数据字段的值没有发生变化,数据监听器依然会被触发
	
	Component({
	  observers: {
	  	''numberA, numberB'':function(numberA, numberB) {
	    	同时监听多个属性
	    },
	    'some.subfield': function(subfield) {
		 	监听属性对象中的字段
	    },
	    'arr[12]': function(arr12) {
			监听数组的某个元素
	    },
        'some.field.**': function(field) {
			**通配符监听对象的所有属性
	    },
	    '**':function() {
	    	监听每次setData
	    },
	  }
	})

10、组件定义纯数据字段,不用于界面渲染的 data 字段,可以用于提升页面更新性能
	既不会展示在界面上,也不会传递给其他组件,仅仅在当前组件内部使用,若在界面上使用无效
	Component({
	  options: {
	    pureDataPattern: /^_/ 	为一个正则表达式
	    或在页面或自定义组件的json文件中配置
	  },
	  data: {
	    _b: true,  纯数据字段
	  },
	    properties: {
	    _b: {
	      type: Boolean,
	      observer() {
	        这个observer永远不会被触发,应该使用外部的observers进行监听
	      }
	    },
	  }
	})

11、组件中定义抽象节点,抽象节点也是组件
	(1)在组件中定义
		<selectable disabled="{{false}}"></selectable>
	
	(2)在组件json中配置
		{
		  "componentGenerics": {
		    "selectable": true
		    "selectable": {
		      "default": "/路径"	若设置抽象节点默认组件
		    }
		  }
		}
	
	(3)在使用到该组件的组件中定义抽象节点渲染内容
		<组件 generic:selectable="yyy" />	yyy只能是静态值,不能包含数据绑定。
		在json文件中定义yyy抽象节点的组件路径
		{
		  "usingComponents": {
		    "yyy": "/路径",
		  }
		}

12、占位组件
	在使用如分包异步化、用时注入、wx-if等特性时,自定义组件所引用的其他自定义组件,在刚开始进行渲染时可能处于不可用的状态。
		使用分包异步化特性的情况下,引用了其他分包的组件,而对应分包还未下载;
		使用用时注入特性的情况下,该组件还未注入
	此时,为了使渲染过程不被阻塞,不可用的自定义组件需要一个「占位组件」(Componentplaceholder)。
	基础库会用占位组件替代不可用组件进行渲染,在该组件可用后再将占位组件替换回该组件。

	(1)页面或自定义组件对应的 JSON 配置中的componentPlaceholder字段用于指定占位组件:
		{
		  "usingComponents": {
		    "comp-a": "../comp/compA",
		    "comp-b": "../comp/compB",
		    "comp-c": "../comp/compC"
		  },
		  "componentPlaceholder": {
		    "comp-a": "view",	使用内置组件
		    "comp-b": "comp-c"	使用上面的指定组件,组件被声明为占位组件后,不能再为其指定占位组件
		  }
		}
		其中:
		  如果一个组件不可用,且其占位组件不存在,则渲染时会报错并抛出;
		  如果一个组件不存在,但为其指定了可用的占位组件,则占位组件可以被正常渲染,但后续尝试准备替换时会报错并抛出。
	
	有占位组件参与的渲染流程:
		基础库尝试渲染一个组件时, 会首先递归检查 usingComponents, 收集其将使用到的所有组件的信息;
		在这个过程中, 如果某个被使用到的组件不可用, 基础库会先检查其是否有对应的占位组件。
		如果没有, 基础库会中断渲染并抛出错误;
		如果有, 则会标记并在后续渲染流程中使用占位组件替换该不可用的组件进行渲染。
		不可用的组件会在当前渲染流程结束后尝试准备(下载分包或注入代码等);
		等到准备过程完成后, 再尝试渲染该组件(实际上也是在执行这个流程), 并替换掉之前渲染的占位组件。
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值