小程序研究报告(一)

因为公司需求开始入手小程序,会分好几篇文章对小程序做个简单的探讨和研究。这篇文章主要关注以下点:

  1. 小程序运行周期
  2. 小程序视图层
  3. 自定义小程序组件

一、小程序运行周期

小程序的开启有多种场景,且有冷启动和热启动之分,为此我画了一张图

小程序运行周期

以上基本描绘了小程序的运行周期

二、小程序视图层

和web前端技术一样,小程序的视图层同样需要三种技术的配合,标记语言(WXML),样式语言(WXSS),脚本(WXS/js)。

先来说说WXML自带的模板语法,我们类比着Vue来

模板语言

1. 数据绑定

vue

<div>{{message}}</div>

new Vue({
data: {
message: 'Hello'
}
})

小程序

<view>{{message}}</view>

Page({
data: {
message: 'Hello'
}
})

2. 列表渲染

vue

<div v-for="(item, index) in array" :key="item">{{item}}	</div>

new Vue({
data: {
array: ['1', '2', '3', '4']
}
})

小程序

<view wx:for="{{array}}" wx:key="*this">{{item}}</view>

默认的遍历项是item,索引是index,也可以自己定义

<view wx:for="{{array}}" wx:for-item="i" wx:for-index="idx"></view>

Page({
data: {
array: ['1', '2', '3', '4']
}
})

3. 条件渲染

vue

<div v-if="view == 'WEBVIEW'">WEBVIEW</div>
<div v-else-if="view == 'APP'">APP</div>
<div v-else-if="view === 'MINA'">MINA</div>

new Vue({
data: {
view: 'MINA'
}
})

小程序

<view wx:if="{{view == 'WEBVIEW'}}">WEBVIEW</view>
<view wx:elif="{{view == 'APP'}}">APP</view>
<view wx:else="{{view == 'MINA'}}">MINA</view>

Page({
data: {
view: 'MINA'
}
})

4. 模板(组件)

vue

定义组件

<template>
<div>{{firstName}}</div>
</template>
<script>
export default {
name: 'user',
props: {
data: Object
}
}
</script>

使用组件

<user :data="userData"></user>

import user from 'path'
new Vue({
component: { user },
data: {
userData: {
firstName: 'jeff'
}
}
})

小程序

<template name="user">
<view>{{firstName}}</view>
</template>
<template is="user" data="{{...userData}}"></template>

Page({
data: {
userData: {
firstName: 'jeff'
}
}
})

对于模板功能,Vue和小程序还是差别比较大的,这里做个类比只是想显示同等的功能Vue是怎么编写的。这个模板功能说实话和AngularJS比较像。

5. 事件

vue

<div v-on:click="add"></div>

new Vue({
data: {
count: 0
},
methods: {
add () {
this.count += 1
}
}
})

小程序

<view bindtap="add"></view>

Page({
data: {
count: 0
},
add () {
this.setData({
count: this.data.count + 1
})
}
})

上述的比较大家可能发现,小程序还是借鉴了挺多主流框架的特点,所以这个的学习成本其实不高。

事件机制

小程序的事件绑定也参照了浏览器标准,有捕获和冒泡的过程,但事件绑定方式略有不同,我们来做个简单的探讨。

<view>
<text bindtap="handleTap2">Event Test</text>
</view>

小程序通过bindtap属性监听tap事件,也可以表示成bind:tap,事件的传递分捕获和冒泡两个阶段,我们可以在两个过程中事件经过的元素上监听或是阻断事件 ,如下图

小程序事件机制

capture-bind:tap可以监听到捕获阶段的事件,capture-catch:tap则将事件的阻断在第二个view中(catch理解为卡住)事件将不会传递到text组件;bindtap则可以监听冒泡阶段的事件,而catchtap则阻断了事件的冒泡过程,顶层view组件并不能接收到冒泡事件。个人认为这个事件机制的设计还是不错的。

脚本(WXS&JS)

微信中的脚本语言有两种,一就是大家熟悉的JS,一是微信自创的WXS。WXS其实和JS差别不大,那为什么微信还要自创呢?

由于运行环境的差异,在 iOS 设备上小程序内的 wxs 会比 javascript 代码快 2 ~ 20 倍。在 android 设备上二者运行效率无差异

我估计和这个原因有一定关系,但是我还是不明白为什么要定义一套这样的语言。

WXS可以写在WXML中也可以分离出去

  • 写在WXML中

    <wxs module="inner">
    module.exports = { b: 'b' }
    </wxs>
    <view>{{inner.b}}</view>

  • 分离出去

    common.wxs

    var a = 'Give me five'
    module.exports = {
    a: a
    }

    <wxs src="../common.wxs" module="common" />
    <view>{{common.a}}</view>

这种引用方式和浏览器中相差不大

WXS的语法和ES5相似,不支持ES6语法,最大不同估计是dateregex对象的生成,WXS采用函数的方式

var date = getDate()
var regex = getRegExp()

还有通过constructor判断数据类型的方式

var func = function () {};
console.log('function' === func.constructor)

样式(WXSS)

WXSS(WeiXin Style Sheets)也和CSS差不多,最大的不同是尺寸单位,WXSS引入了rpx,即响应式像素,rpx在所有屏幕上的数值相同,所以在不同屏幕上与px的换算比例不同。

三、自定义组件

组件化编程是目前前端开发的主流开发方式,小程序框架也有自定义组件的功能。关于组件化编程我最关心有以下点:

  1. 组件接口
  2. 组件内容分发
  3. 组件自定义事件

我们通过这些点来学习自定义组件。

小程序的组件模板和Vue还是有一些不同的,首先小程序的组件模板并不需要指定一个父节点,这在Vue组件定义中是会出错的,在这一点上我认可小程序的设计,但是内容分发上小程序就不方便多了,小程序和Vue一样支持slot分发内容,且语法和Vue几乎一样,不过小程序默认只支持一个slot,如果需要多个slot,则必须在脚本中指定

Component({
options: {
multipleSlot: true
}
})

而小程序的组件脚本的定义方式也和Vue很相像

Component({

behaviors: [],

properties: {
myProperty: { // 属性名
type: String, // 类型(必填),目前接受的类型包括:String, Number, Boolean, Object, Array, null(表示任意类型)
value: '', // 属性初始值(可选),如果未指定则会根据类型选择一个
observer: function(newVal, oldVal){} // 属性被改变时执行的函数(可选),也可以写成在methods段中定义的方法名字符串, 如:'_propertyChange'
},
myProperty2: String // 简化的定义方式
},
data: {}, // 私有数据,可用于模版渲染

// 生命周期函数,可以为函数,或一个在methods段中定义的方法名
attached: function(){},
moved: function(){},
detached: function(){},

methods: {
onMyButtonTap: function(){
this.setData({
// 更新属性和数据的方法与更新页面数据的方法类似
})
},
_myPrivateMethod: function(){
// 内部方法建议以下划线开头
this.replaceDataOnPath(['A', 0, 'B'], 'myPrivateData') // 这里将 data.A[0].B 设为 'myPrivateData'
this.applyDataUpdates()
},
_propertyChange: function(newVal, oldVal) {

}
}

})

如果我们和Vue进行一一对应

behaviors -> mixins
properties -> props
created -> created
attached -> beforeMount
ready -> mounted
detached -> destroyed
methods -> methods

当然这只是粗略的对应,小程序还有很多不一样的点,比如组件关系,抽象节点这些内容。

我们都知道事件是组件通信的一种方式,Vue中通过this.$emit,小程序中则可以通过this.triggerEvent,但是这种方式显然不能满足所有需求,例如在Vue中,有时候我们还是倾向于使用this.$refs来操作子组件,而小程序则引入了relations来增强组件间的通信。

// components/xul/xul.js
Component({
relations: {
'../xli/xli': {
type: 'child',
linked (target) {
// xli被插入时
},
linkChanged (target) {
// xli移动时
},
unLinked (target) {
// 被销毁时
}
}
},
ready: function () {
var nodes = this.getRelationNodes('../xli/xli')
nodes.forEach((node, index) => {
// 操作xli中的数据
node.setData({ name: index })
})
}
})

子组件中也需注明组件间的管理

// components/xli/xli.js
Component({
relations: {
'../xul/xul': {
type: 'parent',
linked: function (target) {
// xli被插入到xul中时
}
}
}
})

四、小结

个人认为小程序的框架体系还是比较完整的,但是相比Vue比较偏向配置化,有些代码必须在相应的位置配置了才能生效,这样会让编程变得繁琐。

分享