第一篇文章里咱们将Dokit的模块引入到了本身的小程序项目中,那么接下来就是正式的阅读Dokit的源代码了。javascript
Dokit模块是由多个自定义组件构成的,所以在阅读源码以前,咱们须要先简单的了解一下微信小程序自定义组件的基本知识。
微信小程序能够说是由多个page页面构成的,而每一个page页面是由多个组件构成的,其中包括微信的原生组件,也包括了用户的自定义组件。html
自定义组件的构成与Page页面的构成很是相似,都具备4个文件:js、json、wxml、wxss。前端
HTML
,但没有HTML的div、p等标签,取而代之的是微信小程序的各类组件,如view、button等CSS
,但自定义组件内的wxss文件不能使用ID选择器、属性选择器和标签名选择器在想要引用组件的page界面或自定义组件的json文件
里声明:(自定义组件里能够再引用其余的自定义组件)java
{
"usingComponents": {
"dokit": "../../dist/index/index"
}
}
复制代码
在自定义组件的json文件
里声明:json
{
"component": true,
"usingComponents": {
"back": "../../components/back/back"
}
}
复制代码
如图所示,Dokit的项目主体在dist文件夹小程序
咱们先来了解一下该项目的目录分布,有助于咱们对项目有个总体的把控:微信小程序
assets
——里面有img
文件夹,存储icon等图片资源components
——Dokit最核心的部分,包括了八个自定义组件
- apimock —— 数据模拟功能的自定义组件
- appinformation —— App信息功能的自定义组件
- back —— 用来返回的自定义组件,该组件并非Dokit的功能组件,但它被除了index以外全部的自定义组件调用,经过该组件返回到小程序page界面
- debug —— 起到主菜单功能的自定义组件,罗列了Dokit的各类功能
- h5door —— h5任意门功能的自定义组件
- httpinjector ——请求注射功能的自定义组件
- positionsimulation —— 位置模拟功能的自定义组件
- storage —— 存储管理功能的自定义组件
index
—— Dokit入口的自定义组件,将Dokit引入本身项目中时,就是在目标page页面中引入这个componentlogs
—— 与新建微信小程序时的样例程序中的logs
page页面相同,查看程序启动日志,目前不知道Dokit中这个部分有什么用utils
—— 里面有两个文件,imgbase64.js
是用来将Dokit各类图标转换成base64格式的;util.js
内存储了一些经常使用接口函数,包括时间输出、跳转页面、深拷贝等在这里简要解释一下什么是base64格式的图片:api
base64格式是一种将二进制转换成字符的编码格式,而图片的base64编码就是将一副图片的数据编码成字符串;
这样前端(如HTML或WXML)能够直接利用编码后的字符串直接转换成图片。针对各类体积小的图片,使用base64格式能够减小向服务器下载图片的请求;
但要注意的是经过base64编码后字符串的体积大小每每会比图片自己要大,所以仅适用于体积小的,不常常更改的图片。服务器
更详细的base64编码信息能够参考这篇文章
在简单的了解了项目的目录分布后,咱们从Dokit的入口组件index开始,逐步阅读源码。
在阅读源代码以前先肯定一下咱们的阅读顺序,自定义组件有四个文件,咱们先阅读.json
、.wxml
和wxss
文件,将.js
文件穿插其中。微信
还记得咱们最初给本身的小程序项目中引入Dokit模块的时候,在相应页面的page.json
文件中添加了如下语句:
"usingComponents": {
"dokit": "../../dist/index/index"
}
复制代码
咱们当时引入的自定义组件就是dist文件夹下的index组件,这个组件就是Dokit的入口组件。先来看看这个组件的json
文件,肯定它引用了什么组件:
{
"component": true,
"navigationBarTitleText": "",
"usingComponents": {
"debug": "../components/debug/debug",
"appinformation": "../components/appinformation/appinformation",
"positionsimulation": "../components/positionsimulation/positionsimulation",
"storage": "../components/storage/storage",
"h5door": "../components/h5door/h5door",
"httpinjector": "../components/httpinjector/httpinjector",
"apimock": "../components/apimock/apimock"
}
}
复制代码
能够看到这个组件引用了components文件夹中的全部Dokit的功能组件,接着看wxml
文件肯定它的结构/模版,是如何调用到了这么多组件的:
<block wx:if="{{ curCom!= 'dokit' }}">
<debug wx:if="{{ curCom === 'debug' }}" bindtoggle="tooggleComponent"></debug>
<appinformation wx:if="{{ curCom === 'appinformation' }}" bindtoggle="tooggleComponent"></appinformation>
<positionsimulation wx:if="{{ curCom === 'positionsimulation' }}" bindtoggle="tooggleComponent"></positionsimulation>
<storage wx:if="{{ curCom === 'storage' }}" bindtoggle="tooggleComponent"></storage>
<h5door wx:if="{{ curCom === 'h5door' }}" bindtoggle="tooggleComponent"></h5door>
<httpinjector wx:if="{{ curCom === 'httpinjector' }}" bindtoggle="tooggleComponent"></httpinjector>
<apimock wx:if="{{ curCom === 'apimock' }}" bindtoggle="tooggleComponent" projectId="{{ projectId }}"></apimock>
</block>
<block wx:else>
<cover-image bindtap="tooggleComponent" data-type="debug" class="dokit-entrance" src="//pt-starimg.didistatic.com/static/starimg/img/W8OeOO6Pue1561556055823.png" ></cover-image>
</block>
复制代码
须要注意的部分有如下几点:
wx:if
条件渲染bindtoggle
组件间的通讯{{curCom}}
数据绑定咱们先来看条件渲染,条件渲染是只有在wx:if
后面的条件成立时才会视图层才会渲染当前的组件,反之则是渲染wx:else
部分的组件(注意条件渲染是惰性的,默认初始为false,不会进行渲染)。
接着咱们看到wx:if
中的条件是"{{ curCom!= 'dokit' }}"
,这是微信小程序WXML语法中的数据绑定:
被两个大括号括住的语句内能够只放一个变量名或者简单的运算,内部变量的具体的值是动态的,实际来自与该WXML文件同名的.js
文件中的data
中同名属性的值。
查找index.js
中data
属性,能够看到该变量的值,初始化为curCom: 'dokit'
所以当组件第一次初始化时,会渲染<block wx:else>
内的组件,也就是<cover-image>
,经过修改data中的curCom
为debug/appinformation能够测试一下会数据绑定的效果。能够看到根据curCom
字符串的不一样,index会展现不一样的功能窗口。
简单的展现图以下:
咱们经过修改
curCom
的值能够改变index条件渲染的组件是什么,而组件本身工做的时候确定要经过用户的点击动做来改变curCom
的值,这个过程实际上就是组件间发生了通讯。
查看以前的index.wxml
文件,咱们看到了每一个组件都有一个bindXXX
的属性,且这个属性的值都是toggleComponent
,这其实就是该组件改变curCom
值的方式:事件。
组件间的通讯,除了父组件向子组件经过数据绑定传数据外,还有一种称为事件监听的方式从子组件向父组件传递数据:
- 事件系统是组件间通讯的主要方式之一。自定义组件能够触发任意的事件,引用组件的页面能够监听这些事件;事件是视图层到逻辑层的通信方式, 能够将用户的行为反馈到逻辑层进行处理。
- 事件能够绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数,事件对象能够携带额外信息,如 id, dataset, touches。
- 监听自定义组件事件的方法与监听基础组件事件的方法彻底一致。
使用事件来实现组件通讯是经过如下的方式:
先来看父组件对子组件的事件是如何监听的:
在index组件中,为子组件添加bind
属性,具体语法为<组件名 bind事件名="事件处理函数"></组件名>
<debug bindtoggle="toggleComponent"></debug>
复制代码
而后在.js
逻辑层添加事件处理函数,当父组件接收到子组件传递来的事件后应该执行这个方法来处理。
methods: {
toggleComponent(e) {
const componentType = e.currentTarget.dataset.type || e.detail.componentType
this.setData({
curCom: componentType
})
}
}
复制代码
咱们先将这个处理函数的阅读放在后面,先看子组件的触发事件方式:
自定义组件触发事件时,须要使用 triggerEvent
方法,指定事件名、detail对象和事件选项。咱们先随便找一个功能组件,看看里面指定事件名为toggle
的方法,以debug
组件为例:
onGoBack () {
this.triggerEvent('toggle', { componentType: 'dokit'})
}
复制代码
在这个方法中,第一个参数是触发了名为toggle
的事件,第二个参数是构建了一个detail对象:属性为componentType
,值为dokit
字符串。
看完了子组件触发事件的函数,咱们能够回来看父组件是如何响应处理这个事件的了:
e
,event,是包括了子组件传递的信息的对象。componentType
,赋值为从e中获取到的detail对象的componentType值,并调用了组件的setData
方法,将该变量的值传递给视图层,且修改index
组件中data相应的属性。能够注意,响应函数中不止是componentType = e.detail.componentType
而是e.currentTarget.dataset.type || e.detail.componentType
,而||
运算符前一个值的出处是cover-image
<cover-image bindtap="tooggleComponent" data-type="debug" class="dokit-entrance" src="//pt-starimg.didistatic.com/static/starimg/img/W8OeOO6Pue1561556055823.png" ></cover-image>
复制代码
在这个组件里监听的事件不是toggle
,而是tap
,这是由用户点击来触发的事件,此时传递的信息不是detail对象,而是dataset
中属性为type
的值。在此简要的介绍一下dataset
和currentTarget
:
currentTarget
是事件绑定的当前组件,有两个属性:id和dataset。dataset
是当前组件上由data-开头的自定义属性组成的集合,在事件中能够获取这些自定义的节点数据,用于事件的逻辑处理。- 在 WXML 中,
dataset
自定义数据以 data- 开头data-elementType
,最终会在js中呈现为event.currentTarget.dataset.elementtype
更详细的信息请参考小程序官方文档。
至此,咱们已经明白了index组件中数据层的curCom
是经过事件的方式来响应各类事件,如toggle
或tap
,进行修改的。但还有一个问题:onGoBack
函数虽然触发了toggle
事件,但这个函数又是在哪里,何时调用的呢?
咱们如今已经知道了,逻辑层的方法是在视图层wxml
文件中经过事件监听/响应的方式调用的。那么咱们就搜索一下,onGoBack
函数是在哪被调用的。
很快,咱们就能在各个组件里都找到这样一个语句,依然以debug组件为例:
<back bindreturn="onGoBack"></back>
复制代码
与上一部分bindtoggle="toggleComponent"
的工做方式是相同的:debug
组件响应子组件back
的名为return的事件,并经过onGoBack
函数来处理。
再以此类推,寻找return事件的通讯过程:
back.js
与back.wxml
相关代码以下:
methods: {
onbackDokitEntry () {
this.triggerEvent('return')
}
}
复制代码
<cover-image bindtap="onbackDokitEntry" data-type="debug" class="dokit-back" src="//pt-starimg.didistatic.com/static/starimg/img/W8OeOO6Pue1561556055823.png" style="top: {{ top }}" ></cover-image>
复制代码
能够看到back组件触发return事件的方式与index组件内cover-image
类似,都是bindtap+data-type的方式,再也不赘述。
最后以一张图来梳理一下这个通讯过程:
到目前为止,咱们熟悉了Dokit小程序端的总体目录结构,并阅读了入口组件——index组件的源代码。咱们了解了自定义组件是如何通讯的,子组件如何利用事件系统来向父组件传值,父组件是如何响应子组件的。 本次阅读的代码量并很少,但咱们了解了小程序的一些特点功能如条件渲染、数据绑定、事件系统,为咱们继续深刻阅读源代码打下了简单的小程序基础。