前面咱们研究了下微信小程序的执行流程,由于拿不到源码,只能算咱们的猜测,咱们须要更加了解小程序还须要作具体的项目,因而咱们将原来那套还算复杂的业务拿出来:html
【组件化开发】前端进阶篇之如何编写可维护可升级的代码(有些晦涩有些乱,可是对于总体了解小程序结构有帮助)前端
咱们用小程序实现这里的代码,看看是个什么样的体验,另外我这里想保证代码最大程度重用,为后续一端代码四端运行作前驱探索。node
页面复杂度仍是比较高的,包括了:web
① 弹出层小程序
② 页面跳转微信小程序
③ 缓存数组
④ 数据请求缓存
⑤ 列表页、滚动分页前端框架
⑥ ......微信
我相信完成了这个例子,咱们对小程序业务代码怎么写会有比较好的了解,因而让咱们开始今天的代码吧。
微信小程序这种平台型的超越Hybrid系统诞生仍是有一些客观条件的,其中一个就是移动端的应用相对来讲简单的多,想一想PC负责的布局,若是要使用小程序实现,那么复杂度会提升不少。
小程序代码编写逻辑层依旧使用JS完成,可是结构层以及样式层推出了:
① WXML,Weixin Markup Language,是微信设计的一套标签语言,与HTML相似,作过React&Vue的同窗会很是熟悉
② WXSS,WeiXin Style Sheets,是一套样式语言,用于定义样式,与CSS相似,通常认为是CSS的子集
由于小程序中UI组件都是Native实现,因此小程序直接手起刀落压根放弃让咱们使用HTML容器,这样作我以为有个好处是:
为了更好的限制,我以前也在作Hybrid乃至前端框架,通常来讲我会限制到View级别的实习,要求必须按照个人规则作,可是由于入口为index.html文件,我甚至将全局控制器App的实例化放到了main.js里面,只提供了建议的作法,事实上HTML仍是太过灵活,有些同事逐渐根本不按照咱们的规则玩,他以为他的作法更好,可是这样一来便会破坏了项目的整体性,后续的工程性的优化或者监控可能就不能帮助他了,从某个角度来讲,我是承认小程序的作法的。
咱们以前在这里研究过自定义标签的作法:从DOM操做看Vue&React的前端组件化,顺带补齐React的demo
1 <article class="cm-page page-list" id="main"> 2 <div class="js_sort_wrapper sort-bar-wrapper"> 3 <mySortBar :entity="sortEntity"></mySortBar> 4 </div> 5 <myList :entity="listEntity" :sort="sort"></myList> 6 </article>
从这个文章以及小程序的实现能够看出基本的概念:
① 标签的出现根本不是作标签用,而是为了让JS捕捉执行相关逻辑,最后生成真正的标签
② 为了作更好的限制,小程序根本不提供入口index.html文件了,因此这里的标签是用做JS作模板解析后生成Native能识别的代码,更具体点说是,Native实现了一个组件,组件有不少规则,可使用JS去调用,正如咱们这里的header组件调用逻辑(JS会设置Native的Header组件展现),这里若是不太清晰能够参考下这个文章:浅谈Hybrid技术的设计与实现第二弹
固然,小程序底层具体是否是这么作,咱们不得而知,若是有小程序的同事,能够指导下:),至此,我以为能够从技术层面说明为何不直接使用HTML&CSS了:更好的业务限制 + 方便JS解析模板被Native执行。
咱们以前作Hybrid应用的时候,事实上只提供了一个真正具备结构的组件Header,其余loading类的提示组件都比较简单,而咱们看看小程序提供了哪些组件呢:
view&scroll-view&swiper等做为容器组件存在,这里官方有基本介绍,咱们这里看看其中一个便可:
这里官方给了一个demo进行说明:
1 <view class="section"> 2 <view class="section__title">flex-direction: row</view> 3 <view class="flex-wrp" style="flex-direction:row;"> 4 <view class="flex-item bc_green">1</view> 5 <view class="flex-item bc_red">2</view> 6 <view class="flex-item bc_blue">3</view> 7 </view> 8 </view> 9 <view class="section"> 10 <view class="section__title">flex-direction: column</view> 11 <view class="flex-wrp" style="height: 300px;flex-direction:column;"> 12 <view class="flex-item bc_green">1</view> 13 <view class="flex-item bc_red">2</view> 14 <view class="flex-item bc_blue">3</view> 15 </view> 16 </view>
1 @import "../lib/weui.wxss"; 2 3 .page-section{ 4 margin-bottom: 20rpx; 5 } 6 .flex-wrp {display: flex;} 7 .bc_green {background: green;width:100px; height: 100px;} 8 .bc_red {background: red;width:100px; height: 100px;} 9 .bc_blue {background: blue;width:100px; height: 100px;}
能够将这个标签理解为div类组件。
通常来讲,Native提供的轮播图体验要好得多,因此这里也提供了一个Native的组件:
1 <view class="container"> 2 <view class="page-body"> 3 <view class="page-section page-section-spacing swiper"> 4 <swiper 5 indicator-dots="{{indicatorDots}}" autoplay="{{autoplay}}" circular="{{circular}}" vertical="{{vertical}}" 6 interval="{{interval}}" duration="{{duration}}" previous-margin="{{previousMargin}}px" next-margin="{{nextMargin}}px"> 7 <block wx:for="{{background}}" wx:key="*this"> 8 <swiper-item> 9 <view class="swiper-item {{item}}"></view> 10 </swiper-item> 11 </block> 12 </swiper> 13 </view> 14 <view class="page-section" style="margin-top: 40rpx;margin-bottom: 0;"> 15 <view class="weui-cells weui-cells_after-title"> 16 <view class="weui-cell weui-cell_switch"> 17 <view class="weui-cell__bd">指示点</view> 18 <view class="weui-cell__ft"> 19 <switch checked="{{indicatorDots}}" bindchange="changeProperty" data-property-name="indicatorDots" /> 20 </view> 21 </view> 22 <view class="weui-cell weui-cell_switch"> 23 <view class="weui-cell__bd">自动播放</view> 24 <view class="weui-cell__ft"> 25 <switch checked="{{autoplay}}" bindchange="changeProperty" data-property-name="autoplay" /> 26 </view> 27 </view> 28 <view class="weui-cell weui-cell_switch"> 29 <view class="weui-cell__bd">衔接滑动</view> 30 <view class="weui-cell__ft"> 31 <switch checked="{{circular}}" bindchange="changeProperty" data-property-name="circular" /> 32 </view> 33 </view> 34 <view class="weui-cell weui-cell_switch"> 35 <view class="weui-cell__bd">竖向</view> 36 <view class="weui-cell__ft"> 37 <switch checked="{{vertical}}" bindchange="changeProperty" data-property-name="vertical" /> 38 </view> 39 </view> 40 </view> 41 </view> 42 43 <view class="page-section page-section-spacing"> 44 <view class="page-section-title"> 45 <text>幻灯片切换时长(ms)</text> 46 <text class="info">{{duration}}</text> 47 </view> 48 <slider value="{{duration}}" min="500" max="2000" bindchange="changeProperty" data-property-name="duration" /> 49 <view class="page-section-title"> 50 <text>自动播放间隔时长(ms)</text> 51 <text class="info">{{interval}}</text> 52 </view> 53 <slider value="{{interval}}" min="2000" max="10000" bindchange="changeProperty" data-property-name="interval" /> 54 <view class="page-section-title"> 55 <text>前边距(px)</text> 56 <text class="info">{{previousMargin}}</text> 57 </view> 58 <slider value="{{previousMargin}}" min="0" max="50" bindchange="changeProperty" data-property-name="previousMargin" /> 59 <view class="page-section-title"> 60 <text>后边距(px)</text> 61 <text class="info">{{nextMargin}}</text> 62 </view> 63 <slider value="{{nextMargin}}" min="0" max="50" bindchange="changeProperty" data-property-name="nextMargin" /> 64 </view> 65 </view> 66 </view>
1 Page({ 2 data: { 3 background: ['demo-text-1', 'demo-text-2', 'demo-text-3'], 4 indicatorDots: true, 5 vertical: false, 6 autoplay: false, 7 circular: false, 8 interval: 2000, 9 duration: 500, 10 previousMargin: 0, 11 nextMargin: 0 12 }, 13 changeProperty: function (e) { 14 var propertyName = e.currentTarget.dataset.propertyName 15 var newData = {} 16 newData[propertyName] = e.detail.value 17 this.setData(newData) 18 }, 19 changeIndicatorDots: function (e) { 20 this.setData({ 21 indicatorDots: !this.data.indicatorDots 22 }) 23 }, 24 changeAutoplay: function (e) { 25 this.setData({ 26 autoplay: !this.data.autoplay 27 }) 28 }, 29 intervalChange: function (e) { 30 this.setData({ 31 interval: e.detail.value 32 }) 33 }, 34 durationChange: function (e) { 35 this.setData({ 36 duration: e.detail.value 37 }) 38 } 39 })
有demo有代码,仍是比较清晰。
提供一个能够移动的区域,暂时没想到应用场景......
图标,小程序这边还扩展了一下,给了不少默认的图标样式,能知足基本需求
文本
富文本,用于展现文章,支持HTML,这里的nodes属性建议使用数组,类型,还不如系统本身解析js算了,由于不会有人像这样写代码(nodes看上去很蠢):
1 Page({ 2 data: { 3 html: '<div class="div_class" style="line-height: 60px; color: red;">Hello World!</div><script>console.log(1)</script>', 4 nodes: [{ 5 name: 'div', 6 attrs: { 7 class: 'div_class', 8 style: 'line-height: 60px; color: red;' 9 }, 10 children: [{ 11 type: 'text', 12 text: 'Hello World!' 13 }] 14 }] 15 }, 16 tap() { 17 console.log('tap') 18 } 19 })
进度条
按钮
选择框
表单相关
输入框,小程序的数据流动是单向的,每次数据更新,动态调用setData改变数据便会触发view更新,底层实现便不知道了;文本框值改变js须要本身去获取
与html一致,用以点击文字操做控件,主要用于文本框
用于级联操做
页面连接,这个组件感受不利于跳转收口,建议少用
其余组件请你们直接到这里来看demo,很是清晰:
https://developers.weixin.qq.com/miniprogram/dev/component/map.html#map
能够看出,小程序Native层是将经常使用的HTML标签分别都实现了一次,使用这些组件能够拼接处任何复杂的组件。至于样式方面,WXSS与CSS大同小异,其中主要区别是小程序没有使用px而是使用的rpx,这个相似于rem的实现,为了解决移动端的适配问题而存在,总而言之,你在iPhone6设计搞上是多少px就写成多少rpx就行,其他系统会帮你完成适配工做,这块透明作的很好,后续样式咱们直接上实例便可。
咱们这里上一张图:
这张图不但真实反映了Page的生命周期,也将咱们以前的猜测作了一个证实,解读这张图大概是这个意思(未必正确,若有错误请指出):
Native层在载入小程序时候,起了两个线程一个的view Thread一个是AppService Thread,我这边理解下来应该就是程序逻辑执行与页面渲染分离,也许是想优化性能,这里更具体一点的解释是(带有猜想了):微信会开一个webview来执行咱们的JS逻辑,而后会开一个Native View UI执行页面渲染;两个部分是彼此独立的,页面点击时候触发事件,View线程会获取APPService服务线程(其实就是获取webview),执行其中的js逻辑;APPService执行js逻辑改变数据经过setData调用,触发一个JSCore通讯,通知view线程执行UI更新,这里结合这张图作下理解:
① 微信打开一个小程序时,主UI线程继续运行,开启一个webview(我认为这里的主线程就是view Thread,webview就是APPService线程,这里可能有误)
② 主View等待构建页面命令,逻辑层开始载入js逻辑(编译过),微信底层应该会将WXML以及WXSS翻译为JS代码,逻辑层执行JS代码作一些初始化工做APP结束后,开始Page逻辑,而他这个图只有Page的逻辑,没有将app囊括进去,这里也引发了我一个疑惑:我在onLoad的时候打了个断点,而页面这个时候事实上已经进行告终构层的渲染,也就是说页面的WXML逻辑已经执行了:
若是要按照我现有的逻辑下作解释的话,我认为实例化Page的时候,执行了一个create事件,可是小程序并无释放onCreate事件让咱们作注册,因此我这里知识体系的基础依旧是:
JS逻辑先于Native UI 执行,页面渲染是由实例化Page时候发出
因此我以为,这里的图好像少了一部分(或者说我理解是有问题的):
③ 业务线程执行实例化Page逻辑,引起onLoad、onShow事件,onShow的时候页面初步渲染已经结束,若是系统有异步数据或者其余再次数据渲染会执行setData,引起Native UI更新,逻辑结束
可是微信给出的图不多是错的,而从图上看,首次异步通知是由View Thread发起的,我这里就非常困惑了😪,由于我认为逻辑发起者必定是逻辑层的js发出通知
今天咱们对小程序进行了基本的了解学习,明天咱们持续完成咱们的demo吧