若無小程序開發經驗,可先閱讀 玩轉微信小程序 一文。
微信小程序正式上線已有幾周時間,相信它的開發模式你已爛熟於胸,可能你也有所疑問,我竟能用 web 語言開發出如此流暢的幾乎原生體驗的應用。可能你又會猜這不就是 h5 么,厲害點的想不就是 hybrid 么。但是在我們的開發旅途中至始至終都沒有使用過 webview ,為啥呢?開發時用的 view 一類的標簽,瀏覽器又是怎么解析成頁面的呢?帶着重重疑惑,進入微信小程序源碼分析吧!
開發平台
這個 IDE 是如何保證我們小程序的開發和預覽的?簡要分析兩點。
1. 文件目錄
打開 微信web開發者工具目錄
,進入 package.nw
,嗯?熟悉的味道來了。里面就3個文件:app
,node_modules
,package.json
。顯然我們開發時構建階段所用資源來自於 node_modules ,於是我嘗試找下 react模塊
,結果沒有收獲...
進入 app 目錄下,呈現的四個文件夾分別是:html
,style
,images
,dist
。而你開發時使用的 IDE 的實現正是通過這些文件,不妨用瀏覽器打開其中一個 html 看看。
這不就是從桌面打開后看到的效果嗎(其中 nodeWebkit
提供了 web 到桌面應用的轉換)。並且在 index.html
中找到我們的主腳本文件 ../dist/app.js
,整個 IDE 從編輯,開發,預覽,發布等一系列操作都在 app.js 及其引用的腳本中。
2. 邏輯關系
接下來開始分析下 dist
下有什么鬼。 不要怕,也就幾十行的源碼。
不過每行都是壓縮過后的而已...好吧,丟去反壓縮一下。在 Sublime Text3 中裝一個 jsFormat
的插件,對要格式化的代碼進行 Ctrl + Alt + F 即可,接下來對我們的文件進行一個邏輯划分。
顯然,微信小程序 IDE 本身是用 React 組件並且以 Flux 的架構來構建的,那我們所編寫的小程序又是如何運行起來的呢?首先從 ./app/dist/components/sidebar/sidebar.js 開始看起,找到 React Render 出的 restart
按鈕(編譯
按鈕的上方)。
// sidebar.js a.createElement('div', { className: 'sidebar-item sidebar-item-sep' }), a.createElement('div', { className: 'sidebar-item-toolbar', style: p }, a.createElement(g, null), a.createElement('div', { title: `${'darwin'===process.platform?'Command':'Ctrl'} + b`, onClick: this.handleAppRestart, className: 'sidebar-item', style: { paddingBottom: 0 } }, a.createElement('i', { className: 'sidebar-item-icon sidebar-item-icon-reset' }));
每當點擊這個按鈕時,IDE 都會重新展現當前 app 。所以這個 handleAppRestart
就是關鍵之處了。
頁面構建
1. 構建流程
觸發 handleAppRestart 的 200ms 后會調用 ./actions/projectActions.js
中的 restart
方法,構建流程正式開始。
// sidebar.js handleAppRestart: function(l) { clearTimeout(j), j = setTimeout(() => { e.restart(this.props.project); // e為projectActions.js輸出對象 let m = 'edit' === this.props.show ? `project_compile` : `project_restart`; i(m, this.props.project.appid) }, 200) }
在 projectActions.js 中,可以清楚的看到 flux 架構的部分 actions ,這些 actions
都會隨着 dispatch
傳入到 store
當中,進行一個狀態的改變,最后重新渲染到應用中。
// projectActions.js del: function(b) { a.dispatch({ actionType: 'DELETE_PROJECT', projectid: b }) }, add: function(b, c) { a.dispatch({ actionType: 'ADD_PROJECT', project: b, needInitQuickStart: c }) }, close: function(b) { a.dispatch({ actionType: 'CLOSE_PROJECT' }) }, restart: function(b) { a.dispatch({ actionType: 'RESTART_PROJECT', project: b }) }
projectActions.js 的每一個 action 都會通過 projectDispatcher.js 映射到 projectStores.js
中,應用的 restart
,add
方法在 store 中也有具體實現。
add: function(F, G) {
F.hash = a(F.projectid), F.es6 = !0, F.watcher = !0, F.editWebview = !0, F.newFeature = { time: Date.now(), show: !1, check: !1 }, F.initPath = { enable: !1 }, F.uploadPath = { enable: !1 }, w.unshift(F), c(F, G), b(), h.info(`projectStores.js add ${JSON.stringify(F)}`), this.emit('ADD_PROJECT', w) }, close: function() { this.emit('CLOSE_PROJECT') }, restart: function(F) { this.emit('RESTART_PROJECT', F) },
對於 Flux,如果還不清楚,這張圖可以做個簡單的詮釋。nw 中用的是 facebook 官方給出的 Flux 架構,github 上比較活躍的 redux
和 mobx
都是比較好用的狀態管理架構。
2. 三端運行
在未安裝 android/ios sdk 時,我們的 app 依然能夠呈現在 IDE 中,此時是通過雲端的 webpack 將 .wxml,.wxss,.js 轉換成 nw 可解析的 html,css,js 。當然在 android/ios 的微信客戶端上,依然可以訪問,這便是 Write Once, run anywhere
,所以微信小程序和阿里 weex 是異曲同工的,只不過微信小程序的 API 都是基於微信的。
以 IOS 為例
微信小程序與傳統 hybrid 使用 webview
不同,后者是提供了 stringByEvaluatingJavaScriptFromString 方法讓 js 能在當前 context 上執行,實質上還是 web 應用。而前者是通過 JsBrigde 定義模塊的方法映射到 OcBrigde 中,調用 native module
,其中還有很多回調,但其實質上是 native 應用。
native化
在你工作或者實習公司的前端組,可能已經出現了 native 化這些目標,這給公司客戶端人員帶來了不小的危機感,的確,前端能做的事越來越多(依托於強大的開源社區)。
在合適的應用場景下,比如頻率一般流量不大的產品上,native 化的確是很好的選擇,因為對於業務復雜度高以及產品需要頻繁更新迭代的公司來講可以很大的提升開發效率,一個前端工程師能完成曾經一個前端+一個android客戶端+一個IOS客戶端需要完成的任務,同時又能避開多次發版的痛點,所以native 化將是前端未來幾年一個必修的方向。
那它究竟帶來了哪些好處,之於 web app , native app 以及 hybrid app 又有哪些優勢呢?
更低的開發成本
Write Once, Run Anywhere
,只要你會 Web 技術,同樣也能開發出 native 應用。這大大降低了前端開發者進入原生開發領域的門檻。
比 hybrid 更接近原生的用戶體驗
解決了傳統 Webview
帶來的性能瓶頸,因為調用的是原生的模塊,而非直接執行 js 腳本。
解決 native 頻繁發版問題
對於敏捷開發的團隊來說,為了能快速上線產品,一個版本的迭代周期可能幾天就行了,所以發布新版本就成了一個新問題,有時新版本完成開發,而上一個版本還沒有完成審核。而對於用微信小程序/ weex / React Native 開發的 app 只需要加載 jsbundle 即可,這個文件是可以隨時更新,從而 app 就可以避免重新發布。