咱们这边最近一直在作基础服务,这一切都是为了完善技术体系,这里对于前端来讲即是咱们须要作一个Hybrid体系,若是作App,React Native也是不错的选择,可是必定要有完善的分层:html
① 底层框架解决开发效率,将复杂的部分作成一个黑匣子,给页面开发展现的只是固定的三板斧,固定的模式下开发便可前端
② 工程部门为业务开发者封装最小化开发环境,最优为浏览器,确实不行便为其提供一个相似浏览器的调试环境web
如此一来,业务便能快速迭代,由于业务开发者写的代码大同小异,因此底层框架配合工程团队(通常是同一个团队),即可以在底层作掉不少效率性能问题。json
稍微大点的公司,稍微宽裕的团队,还会同步作不少后续的性能监控、错误日志工做,如此造成一套文档->开发->调试->构建->发布->监控、分析 为一套完善的技术体系小程序
若是造成了这么一套体系,那么后续就算是内部框架更改、技术革新,也是在这个体系上改造,但很惋惜,不少团队只会在这个路径上作一部分,后面因为种种缘由不在深刻,有多是感受没价值,而最恐怖的行为是,本身的体系没造成就贸然的换基础框架,戒之慎之啊!后端
从第三方应用接入来讲,微信应该是作的最好的,百度这边有直达号等相似的产品,可是其体系化感受仍是有待提升的,阿里应该也有相似的技术产品诞生,从咱们这层来讲,都没有太多知晓,因此要么是运营的很差要么是作的很差。微信小程序
而从小程序诞生以来,我这边便一直在关注,至今整个小程序体系已经十分完备了,腾讯小程序和腾讯云深度整合了,若是使用内测的开发者工具,全免费,纯js就搞定小程序先后端,不用服务器、存储、cdn、服务代码,都是免费,开发完后端不用本身运维,大杀器的节奏,我有时候在想,腾讯的技术实力真的是强啊!数组
小程序的开发文档仍是比较完善的,依旧是 帐号申请->demo 流程,等熟悉后即可以走代码上架等流程了,前端代码用工具构建后上传,后台服务本身维护,配置地址映射,咱们这里仅关注开发流程,全部使用其测试帐号便可。浏览器
1 appid wx0c387cc8c19bdf78 2 appsecret acd6c02e2fdca183416df1269d2e3fb9
通过一年多的发展,小程序造成的文档已经比较完善了,咱们能够从文档和demo对小程序作出大概的判断:服务器
这里就是小程序给业务人员能够看到的代码了,咱们从这个代码以及运行,基本能够将小程序的梗概猜想一番,这里首先看看其全局控制器APP:
1 //app.js 2 App({ 3 onLaunch: function () { 4 // 展现本地存储能力 5 var logs = wx.getStorageSync('logs') || [] 6 logs.unshift(Date.now()) 7 wx.setStorageSync('logs', logs) 8 9 // 登陆 10 wx.login({ 11 success: res => { 12 // 发送 res.code 到后台换取 openId, sessionKey, unionId 13 } 14 }) 15 // 获取用户信息 16 wx.getSetting({ 17 success: res => { 18 if (res.authSetting['scope.userInfo']) { 19 // 已经受权,能够直接调用 getUserInfo 获取头像昵称,不会弹框 20 wx.getUserInfo({ 21 success: res => { 22 // 能够将 res 发送给后台解码出 unionId 23 this.globalData.userInfo = res.userInfo 24 25 // 因为 getUserInfo 是网络请求,可能会在 Page.onLoad 以后才返回 26 // 因此此处加入 callback 以防止这种状况 27 if (this.userInfoReadyCallback) { 28 this.userInfoReadyCallback(res) 29 } 30 } 31 }) 32 } 33 } 34 }) 35 }, 36 globalData: { 37 userInfo: null 38 } 39 })
一个应用只会有一个APP实例,而小程序为这个单例提供了几个基本的事件定义,咱们用的最多的应该是onLaunch、onShow、onHide(我还没写小程序,因此猜想):
咱们这里来追溯一下小程序架构层的执行逻辑,从APP到一个view实例化是怎么作的,这里首先明确几个点:
① 微信小程序事实上依旧是提供的webview执行环境,因此咱们依旧能够在js环境中访问window、location等属性
② 微信小程序提供的展现的所有是Native定制化的UI,因此不要去想DOM操做的事
这里各位能够想象为,小程序界面中有一块webview在执行真正的代码逻辑,只不过这个webview除了装载js程序什么都没作,而全部的页面渲染所有是js经过URL Schema或者JSCore进行的Native通讯,叫Native根据设置的规则完成的页面渲染。
这里咱们重点关注全局控制器App这个类作了什么,由于拿不到源码,咱们这里也只能猜想加单步调试了,首先微信容器会准备一个webview容器为咱们的js代码提供宿主环境,容器与构建工具会配合产出如下页面:
他在这里应该执行了实例化App的方法:
这一坨代码,在这个环境下便至关晦涩了:
1 y = function() { 2 function e(t) { 3 var n = this; 4 o(this, e), 5 s.forEach(function(e) { 6 var o = function() { 7 var n = (t[e] || i.noop).bind(this); 8 Reporter.__route__ = "App", 9 Reporter.__method__ = e, 10 (0, 11 i.info)("App: " + e + " have been invoked"); 12 try { 13 n.apply(this, arguments) 14 } catch (t) { 15 Reporter.thirdErrorReport({ 16 error: t, 17 extend: "at App lifeCycleMethod " + e + " function" 18 }) 19 } 20 }; 21 n[e] = o.bind(n) 22 }); 23 for (var r in t) 24 !function(e) { 25 g(e) ? (0, 26 i.warn)("关键字保护", "App's " + e + " is write-protected") : v(e) || ("[object Function]" === Object.prototype.toString.call(t[e]) ? n[e] = function() { 27 var n; 28 Reporter.__route__ = "App", 29 Reporter.__method__ = e; 30 try { 31 n = t[e].apply(this, arguments) 32 } catch (t) { 33 Reporter.thirdErrorReport({ 34 error: t, 35 extend: "at App " + e + " function" 36 }) 37 } 38 return n 39 } 40 .bind(n) : n[e] = t[e]) 41 }(r); 42 this.onError && Reporter.registerErrorListener(this.onError); 43 var l = function() { 44 "hang" === (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}).mode && (f = !0); 45 var e = (0, 46 a.getCurrentPages)(); 47 e.length && (e[e.length - 1].onHide(), 48 (0, 49 u.triggerAnalytics)("leavePage", e[e.length - 1], !0)), 50 this.onHide(), 51 (0, 52 u.triggerAnalytics)("background") 53 } 54 , h = function() { 55 var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; 56 if (0 === e.scene || "0" === e.scene ? e.scene = c : c = e.scene, 57 e.query = e.query || {}, 58 (0, 59 i.hasExitCondition)(e) && (p = !0), 60 this.onShow(e), 61 (0, 62 u.triggerAnalytics)("foreground"), 63 d || e.reLaunch) 64 d = !1; 65 else { 66 var t = (0, 67 a.getCurrentPages)(); 68 t.length && (t[t.length - 1].onShow(), 69 (0, 70 u.triggerAnalytics)("enterPage", t[t.length - 1], !0)) 71 } 72 }; 73 if ("undefined" != typeof __wxConfig && __wxConfig) { 74 var y = __wxConfig.appLaunchInfo || {}; 75 y.query = y.query || {}, 76 c = y.scene, 77 (0, 78 i.hasExitCondition)(y) && (p = !0), 79 this.onLaunch(y), 80 (0, 81 u.triggerAnalytics)("launch"), 82 h.call(this, y) 83 } else 84 (0, 85 i.error)("App Launch Error", "Can not find __wxConfig"); 86 wx.onAppEnterBackground(l.bind(this)), 87 wx.onAppEnterForeground(h.bind(this)), 88 _.call(this, "function" == typeof t.onPageNotFound) 89 } 90 return r(e, [{ 91 key: "getCurrentPage", 92 value: function() { 93 (0, 94 i.warn)("将被废弃", "App.getCurrentPage is deprecated, please use getCurrentPages."); 95 var e = (0, 96 a.getCurrentPage)(); 97 if (e) 98 return e.page 99 } 100 }]), 101 e 102 }();
这里会往App中注册一个事件,咱们这里注册的是onLaunch事件,这里对应的是当小程序初始化时候会执行这个回调,因此原则上应该是Native装在成功后会执行这个函数,这里再详细点说明下H5与Native的交互流程(这里是我以前作Hybrid框架时候跟Native同事的交互约定,小程序应该大同小异):
咱们通常是在全局上会有一个对象,保存全部须要Native执行函数的对象,好比这里的onLaunch,Native在执行到一个状态时候会调用js全局环境该对象上的一个函数
由于咱们js注册native执行是以字符串key做为标志,因此Native执行的时候多是window.app['onLauch...']('参数')
而咱们在window对象上会使用bind的方式将对应的做用域环境保留下来,这个时候执行的逻辑即是正确的
这里在小程序全局没有找到对应的标识,这里猜想是直接在app对象上,Native会直接执行APP对象上面的方法,可是我这里有个疑问是View级别若是想注册个全局事件该怎么作,这个留到后面来看看吧,这里是Native载入webview时,会执行对象定义的onLaunch事件,在下面的代码看获得:
这里会结合app.json获取首先加载页面的信息,默认取pages数组第一个,可是具体哪里获取和设置的代码没有找到,也跟主流程无关,咱们这里忽略......而后咱们看到代码执行了onShow逻辑:
而后流转到注册微信容器层面的事件,我以为,不管如何,这里应该是像微信容器注册事件了吧,可是我找不到全局的key😔
若是有微信小程序的同窗,麻烦这里指点一下,是否是猜想正确,顺即可以帮忙说明下这里,这里也是我以为全局key,被Native调用的点,而后,逻辑上会获取默认view的类开始作实例化,咱们这里来到view级别代码:
1 //index.js 2 //获取应用实例 3 const app = getApp() 4 5 Page({ 6 data: { 7 motto: 'Hello Wor11ld', 8 userInfo: {}, 9 hasUserInfo: false, 10 canIUse: wx.canIUse('button.open-type.getUserInfo') 11 }, 12 //事件处理函数 13 bindViewTap: function() { 14 wx.navigateTo({ 15 url: '../logs/logs' 16 }) 17 }, 18 onLoad: function () { 19 if (app.globalData.userInfo) { 20 this.setData({ 21 userInfo: app.globalData.userInfo, 22 hasUserInfo: true 23 }) 24 } else if (this.data.canIUse){ 25 // 因为 getUserInfo 是网络请求,可能会在 Page.onLoad 以后才返回 26 // 因此此处加入 callback 以防止这种状况 27 app.userInfoReadyCallback = res => { 28 this.setData({ 29 userInfo: res.userInfo, 30 hasUserInfo: true 31 }) 32 } 33 } else { 34 // 在没有 open-type=getUserInfo 版本的兼容处理 35 wx.getUserInfo({ 36 success: res => { 37 app.globalData.userInfo = res.userInfo 38 this.setData({ 39 userInfo: res.userInfo, 40 hasUserInfo: true 41 }) 42 } 43 }) 44 } 45 }, 46 getUserInfo: function(e) { 47 console.log(e) 48 app.globalData.userInfo = e.detail.userInfo 49 this.setData({ 50 userInfo: e.detail.userInfo, 51 hasUserInfo: true 52 }) 53 } 54 })
他首先一来便获取了当前app实例:
const app = getApp()
其次开始了view实例化流程,这个是Page的类入口,你们要注意view.js只是定义的类,可是其实例化应该在全局的控制器,其实例化在这里完成的:
Page实例化后会本身执行onLoad以及onShow,可是这里的onLoad以及onShow就看不出来分别了
咱们这里一块儿瞎子摸象通常对微信小程序架构作了简单的摸索,这里发现事实上小程序流程与本身所想有一些出入,这里初步认为流程是这样的:
① 咱们写好小程序代码后,提交代码
② 在发布流程中咱们的代码通过构建流程,app.json以及入口的index.html(伪造页面),从新组装为一个只有js代码的空页面
③ 这里开始载入流程,用户点击一个微信按钮,进入小程序
④ 微信容器开启Hybrid容器,webview载入入口页面(我感受应该有个规则能够经过url去打开固定一个小程序页面,这里后续碰到开发案例再说)
⑤ webview执行环境实例化App,其后自动装载默认Page(这里默认是index)
PS:这里我有个很疑惑的点,微信Native容器的各个事件点何时执行,由谁执行?
⑥ 进入页面渲染逻辑
⑦ ......
这里我还比较在乎,执行事件后,对应Native页面是如何进行更新的,因此咱们这里关注下这段代码:
1 debugger; 2 this.setData({ 3 userInfo: app.globalData.userInfo, 4 hasUserInfo: true 5 })
这里出现了一段很是关键的代码:
能够看到,咱们这里往微信容器注册了一个appDataChange的异步事件,而这个时候就将全部的逻辑交给了Native自己,Native执行结束后会根据webviewIds找到后续要执行的回调继续执行。
至于,容器如何使用webviewId找到对应函数的代码,我没有找到。至此,咱们对小程序结构的初步探索便结束了,咱们本周后面时间继续来对小程序进行深刻学习。