咱們這邊最近一直在作基礎服務,這一切都是爲了完善技術體系,這裏對於前端來講即是咱們須要作一個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找到對應函數的代碼,我沒有找到。至此,咱們對小程序結構的初步探索便結束了,咱們本週後面時間繼續來對小程序進行深刻學習。