微信小程序开发基础知识笔记

41次阅读

共计 5995 个字符,预计需要花费 15 分钟才能阅读完成。

绑定事件的方法:

1.bindtab 和 catchtab,catchtab 可以阻止事件冒泡


<view bindtap='onClick'></view>

<view catchtap='onClick'></view>

2. 互斥事件绑定 mut-bind

一个 mut-bind 触发后,如果事件冒泡到其他节点上,其他节点上的 mut-bind 绑定函数不会被触发,但 bind 绑定函数和 catch 绑定函数依旧会被触发。

在想要规定冒泡区间时可以用到。


<view mut-bind:tap='onClick'></view>
在基础库版本 2.7.1 以上,可以使用 mark 来识别具体触发事件的 target 节点

<view mark:myMark="last" bindtap="bindViewTap">

<button mark:anotherMark="leaf" bindtap="bindButtonTap"> 按钮 </button>

</view>

在上述 WXML 中,如果按钮被点击,将触发 bindViewTap 和 bindButtonTap 两个事件,事件携带的 event.mark 将包含 myMark 和 anotherMark 两项。


Page({bindViewTap: function(e) {

e.mark.myMark === "last" // true

e.mark.anotherMark === "leaf" // true

}

})

mark 和 dataset 很相似,主要区别在于:

1.mark 可以冒泡,如果存在同名的 mark,父节点的 mark 会被子节点覆盖。

2. 在自定义组件中接收事件时,mark 不包含自定义组件外的节点的 mark。

3. 不同于 dataset,节点的 mark 不会做连字符和大小写转换。

.wxs 文件的应用

个人理解 .wxs 文件就相当于 cocos 的 prefab。执行起来比 js 性能要快。但是 wxs 是一门语言,平行于 JavaScript。

wxs 基础语法

有频繁用户交互的效果在小程序上表现是比较卡顿的,这时建议使用 wxs,为什么?

因为小程序分为视图层和逻辑层,比如需要拖动的功能,touchmove 事件从视图层抛到逻辑层,逻辑层经过处理,通过 this.setData 到视图层。

1. 一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信的耗时比较大。

2. 此外 setData 渲染也会阻塞其它脚本执行,导致了整个用户交互的动画过程会有延迟。

WXS 函数的除了纯逻辑的运算,还可以通过封装好的 ComponentDescriptor 实例来访问以及设置组件的 class 和样式,对于交互动画,设置 style 和 class 很方便。

代码示例:


var wxsFunction = function(event, ownerInstance) {var instance = ownerInstance.selectComponent('.classSelector') // 返回组件的实例

instance.setStyle({"font-size": "14px" // 支持 rpx})

instance.getDataset()

instance.setClass(className)

// ...

return false // 不往上冒泡,相当于调用了同时调用了 stopPropagation 和 preventDefault

}
自定义组件

上边在分析复杂交互时我们知道,频繁的调用 this.setData 会使页面卡顿,甚至导致小程序僵死。那么不想写或者说不会写 wxs 的开发者该怎么办呢?

此时可以通过将页面的 setData 改为 自定义组件 中的 setData 来提升性能。

原因:自定义组件中的 setData 不会进行深复制。(深复制会在这个值被组件间传递时才发生)

自定义组件的规范

1. 在组件 wxss 中不应使用 ID 选择器、属性选择器和标签名选择器,就只使用 class 选择器准没错。

2. 在自定义组件的 js 文件中,需要使用 Component() 来注册组件。

3. 使用已注册的自定义组件前,首先要在页面的 json 文件中进行引用声明。

4. 自定义组件和页面所在项目根目录名不能以“wx-”为前缀,否则会报错。

5.<slot></slot> 相当于 react 的 this.props.children。


<!-- component-tag-name 组件 -->

<view class="wrapper">

<view> 这里是组件的内部节点 </view>

<slot></slot>

</view>

<!-- 引用组件的页面模板 -->

<view>

<component-tag-name>

<!-- 这部分内容将被放置在组件 <slot> 的位置上 -->

<view> 这里是插入到组件 slot 中的内容 </view>

</component-tag-name>

</view>

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


Component({

options: {multipleSlots: true // 在组件定义时的选项中启用多 slot 支持},

properties: {/\* ... \*/},

methods: {/\* ... \*/}

})

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


<!-- 组件模板 -->

<view class="wrapper">

<slot name="before"></slot>

<view> 这里是组件的内部细节 </view>

<slot name="after"></slot>

</view>

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


<!-- 引用组件的页面模板 -->

<view>

<component-tag-name>

<!-- 这部分内容将被放置在组件 <slot name="before"> 的位置上 -->

<view slot="before"> 这里是插入到组件 slot name="before" 中的内容 </view>

<!-- 这部分内容将被放置在组件 <slot name="after"> 的位置上 -->

<view slot="after"> 这里是插入到组件 slot name="after" 中的内容 </view>

</component-tag-name>

</view>

7. 设置自定义组件的捕获和冒泡机制需要使用 triggerEvent 方法。


// 组件 my-component.js

Component({

methods: {onTap: function(){this.triggerEvent('customevent', {}, {bubbles: true, composed: true})

}

}

})
组件中的 behaviors

个人理解:多个页面可能会共用一个功能,这个功能抽象后称为组件。

多个组件共用一个方法或者多个方法,这类方法的集合称为 behaviors。就 tm 理解成高阶组件就完了。

组件间关系 relations

官方说:有时需要实现这样的组件:


<custom-ul>

<custom-li> item 1 </custom-li>

<custom-li> item 2 </custom-li>

</custom-ul>

说 custom-ul 和 custom-li 都是自定义组件,它们有相互间的关系,相互间的通信往往比较复杂。具体怎么个复杂需要单独拎出来一个 ralations 属性来处理,咱也不知道。

使用方法:


// path/to/custom-ul.js

Component({

relations: {

'./custom-li': {

type: 'child', // 关联的目标节点应为子节点

linked: function(target) {// 每次有 custom-li 被插入时执行,target 是该节点实例对象,触发在该节点 attached 生命周期之后},

linkChanged: function(target) {// 每次有 custom-li 被移动后执行,target 是该节点实例对象,触发在该节点 moved 生命周期之后},

unlinked: function(target) {// 每次有 custom-li 被移除时执行,target 是该节点实例对象,触发在该节点 detached 生命周期之后}

}

},

// path/to/custom-li.js

Component({

relations: {

'./custom-ul': {

type: 'parent', // 关联的目标节点应为父节点

linked: function(target) {// 每次被插入到 custom-ul 时执行,target 是 custom-ul 节点实例对象,触发在 attached 生命周期之后},

linkChanged: function(target) {// 每次被移动后执行,target 是 custom-ul 节点实例对象,触发在 moved 生命周期之后},

unlinked: function(target) {// 每次被移除时执行,target 是 custom-ul 节点实例对象,触发在 detached 生命周期之后}

}

}

})

注意:必须在两个组件定义中都加入 relations 定义,否则不会生效。

还有一种情况,如果你两个自定义组件都用了相同的 behaviors,你可以使用这个 behavior 来代替组件路径作为关联的目标节点。


// path/to/custom-form.js

var customFormControls = require('./custom-form-controls')

Component({

relations: {

'customFormControls': {

type: 'descendant', // 关联的目标节点应为子孙节点

target: customFormControls

}

}

})

组件中的 observers

官方定义他叫数据监听器,呵呵。

使用方法:


Component({attached: function() {

this.setData({

numberA: 1,

numberB: 2,

})

},

observers: {'numberA, numberB': function(numberA, numberB) {

// 在 numberA 或者 numberB 被设置时,执行这个函数

this.setData({sum: numberA + numberB})

}

}

})

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


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({

options: {pureDataPattern: /^_/ // 指定所有 _ 开头的数据字段为纯数据字段},

data: {

a: true, // 普通数据字段

_b: true, // 纯数据字段

},

methods: {myMethod() {

this.data._b // 纯数据字段可以在 this.data 中获取

this.setData({

c: true, // 普通数据字段

_d: true, // 纯数据字段

})

}

}

})
抽象节点

又一个新名词,呵呵呵。

说白了就是有一个父容器组件 A,因为条件不同有可能 A 中会渲染组件 B,也可能渲染组件 C。举个例子,当页面需要单选和多选组件的时候, 方法 1 是按条件引用两个封装好的组件(< 单选 />,</ 多选 >),方法 2 是你也可以只引用一个组件 < 啦啦啦 />,只不过这个 < 啦啦啦 /> 组件去帮你按需渲染 < 单选 /> 或者 < 多选 />。

需要在父容器组件 A 的.json 文件声明:


{

"componentGenerics": {"selectable": true}

}

在使用组件时,必须指定父组件具体是渲染哪个子组件:

< 啦啦啦 generic:selectable="单选" />

< 啦啦啦 generic:selectable="多选" />

在页面的.json 文件 < 啦啦啦 />,< 单选 />,< 多选 /> 都要引用。代码


//page 下页面的.json 文件中

{

"usingComponents": {

"啦啦啦": "path/*/*",

"多选": "*/checkbox",

"单选": "*/radio"

}

}

当然,你也可以在容器组件.json 中指定默认用哪个组件:


{

"componentGenerics": {

"selectable": {"default": "*/checkbox"// 多选}

}

}
计算属性

今天真是开眼了,学到了这么多新词汇。。

(计算属性的作用):是为了解决 HTML 代码中复杂的 js 代码(HTML 代码中可以嵌套 js 代码),把复杂的 js 代码通过计算属性来解决

这是计算属性的应用???听着词这么厉害干这事真是大才小用了。

计算属性会使用缓存机制,如果这个数据的值没有改变,则计算属性将不会调用方法

这点应该是它实际有价值的地方。

实现原理很简单,就是对已有的 setData 进行二次封装,在每次 setData 的时候计算出 computed 里各字段的值,这期间可以增加缓存机制,属性值没有变化的复用。

自定义组件拓展

在 react 中想拓展一个组件怎么办,会用高阶组件。

小程序中,自然是使用 behaviors。


// behavior.js

module.exports = Behavior({definitionFilter(defFields) {defFields.data.from = 'behavior'},

})

// component.js

Component({

data: {from: 'component'},

behaviors: [require('behavior.js')],

ready() {console.log(this.data.from) // 此处会发现输出 behavior 而不是 component

}

})

Behavior() 构造器提供了新的定义段 definitionFilter,用于支持自定义组件扩展。

正文完
 0