導語:
微信小程序,小程式的一種,英文名Wechat Mini Program,是一種不需要下載安裝即可使用的應用,它實現了應用程式「觸手可及」的夢想,使用者掃一掃或搜一下即可開啟應用程式。
全面開放申請後,主體類型為企業、政府、媒體、其他組織或個人的開發者,均可申請註冊小程式。微信小程式、微信訂閱號碼、微信服務號碼、微信企業號是平行的系統。
我們這邊最近一直在做基礎服務,這一切都是為了完善技術體系,這裡對於前端來說便是我們需要做一個Hybrid體系,如果做App,React Native也是不錯的選擇,但一定要有完善的分層:
① 底層框架解決開發效率,將複雜的部分做成一個黑盒子,給頁面開發展示的只是固定的三板斧,固定的模式下開發即可
② 工程部門為業務開發者封裝最小化開發環境,最優為瀏覽器,確實不行便為其提供一個類似瀏覽器的調試環境
如此一來,業務便能快速迭代,因為業務開發者寫的程式碼大同小異,所以底層框架配合工程團隊(一般是同一個團隊),便可以在底層做掉很多效率效能問題。
稍微大點的公司,稍微寬裕的團隊,還會同步做很多後續的效能監控、錯誤日誌工作,如此形成一套文檔->開發->調試->構建-> ;發布->監控、分析為一套完善的技術體系
如果形成了這麼一套體系,那麼後續就算是內部框架更改、技術革新,也是在這個體系上改造,但很可惜,很多團隊只會在這個路徑上做一部分,後面由於種種原因不在深入,有可能是感覺沒價值,而最恐怖的行為是,自己的體系沒形成就貿然的換基礎框架,戒之慎之啊!
從第三方應用程式存取來說,微信應該是做的最好的,百度這邊有直達號等類似的產品,但是其體系化感覺還是有待提高的,阿里應該也有類似的科技產品誕生,從我們這層來說,都沒有太多知曉,所以不是營運的不好或是做的不好。
而從小程式誕生以來,我這邊便一直在關注,至今整個小程式體系已經十分完備了,騰訊小程式和騰訊雲深度整合了,如果使用內測的開發者工具,全免費,純js就搞定小程式前後端,不用伺服器、儲存、cdn、服務程式碼,都是免費,開發完後端不用自己運維,大殺器的節奏,我有時候在想,騰訊的技術實力真的是強啊!
小程式的結構追溯
小程式的開發文件還是比較完善的,依舊是帳號申請->demo 流程,等熟悉後便可以走程式碼上架等流程了,前端程式碼用工具建構後上傳,後台服務自己維護,配置位址映射,我們這裡僅關注開發流程,所有使用其測試帳號即可。
1 appid wx0c387cc8c19bdf78 2 appsecret acd6c02e2fdca183416df1269d2e3fb9
經過一年多的發展,小程式形成的文檔已經比較完善了,我們可以從文檔和demo對小程式做出大概的判斷:
這裡就是小程式給業務人員可以看到的程式碼了,我們從這個程式碼以及運行,基本上可以將小程式的梗概猜測一番,這裡首先看看其全域控制器APP:
//app.js App({ onLaunch: function () { // 展示本地存储能力 var logs = wx.getStorageSync('logs') || [] logs.unshift(Date.now()) wx.setStorageSync('logs', logs) // 登录 wx.login({ success: res => { // 发送 res.code 到后台换取 openId, sessionKey, unionId } }) // 获取用户信息 wx.getSetting({ success: res => { if (res.authSetting['scope.userInfo']) { // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 wx.getUserInfo({ success: res => { // 可以将 res 发送给后台解码出 unionId this.globalData.userInfo = res.userInfo // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 // 所以此处加入 callback 以防止这种情况 if (this.userInfoReadyCallback) { this.userInfoReadyCallback(res) } } }) } } }) }, globalData: { userInfo: null } })
一個應用程式只會有一個APP實例,而小程式為這個單例提供了幾個基本的事件定義,我們用的最多的應該是onLaunch、onShow、onHide(我還沒寫小程序,所以猜測):
我們這裡來追溯一下小程式架構層的執行邏輯,從APP到一個view實例化是怎麼做的,這裡首先明確幾個點:
① 微信小程式事實上依舊是提供的webview執行環境,所以我們依舊可以在js環境中存取window、location等屬性
② 微信小程式提供的展示的全部是Native客製化的UI,所以不要去想DOM操作的事
這裡各位可以想像為,小程式介面中有一塊webview在執行真正的程式碼邏輯,只不過這個webview除了裝載js程式什麼都沒做,而所有的頁面渲染全部是js透過URL Schema或JSCore進行的Native通信,叫Native根據設定的規則完成的頁面渲染。
全域控制器App
这里我们重点关注全局控制器App这个类做了什么,因为拿不到源码,我们这里也只能猜测加单步调试了,首先微信容器会准备一个webview容器为我们的js代码提供宿主环境,容器与构建工具会配合产出以下页面:
他在这里应该执行了实例化App的方法:
这一坨代码,在这个环境下便相当晦涩了:
y = function() { function e(t) { var n = this; o(this, e), s.forEach(function(e) { var o = function() { var n = (t[e] || i.noop).bind(this); Reporter.__route__ = "App", Reporter.__method__ = e, (0, i.info)("App: " + e + " have been invoked"); try { n.apply(this, arguments) } catch (t) { Reporter.thirdErrorReport({ error: t, extend: "at App lifeCycleMethod " + e + " function" }) } }; n[e] = o.bind(n) }); for (var r in t) !function(e) { g(e) ? (0, i.warn)("关键字保护", "App's " + e + " is write-protected") : v(e) || ("[object Function]" === Object.prototype.toString.call(t[e]) ? n[e] = function() { var n; Reporter.__route__ = "App", Reporter.__method__ = e; try { n = t[e].apply(this, arguments) } catch (t) { Reporter.thirdErrorReport({ error: t, extend: "at App " + e + " function" }) } return n } .bind(n) : n[e] = t[e]) }(r); this.onError && Reporter.registerErrorListener(this.onError); var l = function() { "hang" === (arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}).mode && (f = !0); var e = (0, a.getCurrentPages)(); e.length && (e[e.length - 1].onHide(), (0, u.triggerAnalytics)("leavePage", e[e.length - 1], !0)), this.onHide(), (0, u.triggerAnalytics)("background") } , h = function() { var e = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : {}; if (0 === e.scene || "0" === e.scene ? e.scene = c : c = e.scene, e.query = e.query || {}, (0, i.hasExitCondition)(e) && (p = !0), this.onShow(e), (0, u.triggerAnalytics)("foreground"), d || e.reLaunch) d = !1; else { var t = (0, a.getCurrentPages)(); t.length && (t[t.length - 1].onShow(), (0, u.triggerAnalytics)("enterPage", t[t.length - 1], !0)) } }; if ("undefined" != typeof __wxConfig && __wxConfig) { var y = __wxConfig.appLaunchInfo || {}; y.query = y.query || {}, c = y.scene, (0, i.hasExitCondition)(y) && (p = !0), this.onLaunch(y), (0, u.triggerAnalytics)("launch"), h.call(this, y) } else (0, i.error)("App Launch Error", "Can not find __wxConfig"); wx.onAppEnterBackground(l.bind(this)), wx.onAppEnterForeground(h.bind(this)), _.call(this, "function" == typeof t.onPageNotFound) } return r(e, [{ key: "getCurrentPage", value: function() { (0, i.warn)("将被废弃", "App.getCurrentPage is deprecated, please use getCurrentPages."); var e = (0, a.getCurrentPage)(); if (e) return e.page } }]), e }();
这里会往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
Page流程
如果有微信小程序的同学,麻烦这里指点一下,是不是猜测正确,顺便可以帮忙说明下这里,这里也是我觉得全局key,被Native调用的点,然后,逻辑上会获取默认view的类开始做实例化,我们这里来到view级别代码:
//index.js //获取应用实例 const app = getApp() Page({ data: { motto: 'Hello Wor11ld', userInfo: {}, hasUserInfo: false, canIUse: wx.canIUse('button.open-type.getUserInfo') }, //事件处理函数 bindViewTap: function() { wx.navigateTo({ url: '../logs/logs' }) }, onLoad: function () { if (app.globalData.userInfo) { this.setData({ userInfo: app.globalData.userInfo, hasUserInfo: true }) } else if (this.data.canIUse){ // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 // 所以此处加入 callback 以防止这种情况 app.userInfoReadyCallback = res => { this.setData({ userInfo: res.userInfo, hasUserInfo: true }) } } else { // 在没有 open-type=getUserInfo 版本的兼容处理 wx.getUserInfo({ success: res => { app.globalData.userInfo = res.userInfo this.setData({ userInfo: res.userInfo, hasUserInfo: true }) } }) } }, getUserInfo: function(e) { console.log(e) app.globalData.userInfo = e.detail.userInfo this.setData({ userInfo: e.detail.userInfo, hasUserInfo: true }) } })
他首先一来便获取了当前app实例:
const app = getApp()
其次开始了view实例化流程,这个是Page的类入口,大家要注意view.js只是定义的类,但是其实例化应该在全局的控制器,其实例化在这里完成的:
总结
我们这里一起瞎子摸象一般对微信小程序架构做了简单的摸索,这里发现事实上小程序流程与自己所想有一些出入,这里初步认为流程是这样的:
① 我们写好小程序代码后,提交代码
② 在发布流程中我们的代码经过构建流程,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找到对应函数的代码,我没有找到。至此,我们对小程序结构的初步探索便结束了,我们本周后面时间继续来对小程序进行深入学习。
相关推荐:小程序开发教程
以上是小程式執行流程分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!