开发制作微信小程序-小程序架构分析《三》:实

摘要:微信小程序即时运作专用工具 wept 的开发设计早已基本进行了, 你可以以根据我的编码对微信小程序的 web 自然环境完成有更全方位的了解。下边我将详细介绍它的完成全过程及其即时...

--------

开发制作微信小程序

-------

小程序即时运作专用工具 wept 的开发设计早已基本进行了, 你能够根据我的编码对小程序的 web 自然环境完成有更全面的了解。下面我将详细介绍它的完成全过程和即时升级的基本原理。

小程序 web 服实干现

我在 wept 的开发设计中应用 koa 出示 web 服务,和 et-improve 出示模版3D渲染。

第一步: 提前准备网页页面模版 <

<:


 head meta http-equiv= Content-Type content= text/html; charset=utf-8 / link href= mpres/htmledition/images/favicon218877.ico rel= Shortcut Icon script var __wxAppData = {} var __wxRoute var __wxRouteBegin global = {} var __wxConfig = {{= _.config}} /script script src= /script/bridge.js type= text/javascript /script script src= /script/service.js type= text/javascript /script {{each _.utils as util}} script src= /app/{{= util}} type= text/javascript /script {{/}} script src= /app/app.js type= text/javascript /script {{each _.routes as route}} script var __wxRoute = '{{= route | noext}}', __wxRouteBegin = true; /script script src= /app/{{= route}} type= text/javascript /script {{/}} /head body script window._____sendMsgToNW({ sdkName: 'APP_SERVICE_COMPLETE' }) /script /body 
<:


 head link href= mpres/htmledition/images/favicon218877.ico rel= Shortcut Icon meta charset= UTF-8 / meta name= viewport content= width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0 / link rel= stylesheet type= text/css href= /css/default.css link rel= stylesheet type= text/css href= /app/app.wxss link rel= stylesheet type= text/css href= /app/{{= _.path}}.wxss script var __path__ = '{{= _.path}}' /script script src= /script/ViewBridge.js async type= text/javascript /script script src= /script/view.js type= text/javascript /script script {{= _.inject_js}} /script script document.dispatchEvent(new CustomEvent( generateFuncReady , { detail: { generateFunc: $gwx('./{{= _.path}}.wxml') } })) /script /head body div /div /body 
第二步: 完成 http 服务

用 koa 完成的编码逻辑性十分简易:

server.js


// 系统日志正中间件 app.use(logger()) // gzip press({ threshold: 2048, flush: require('zlib').Z_SYNC_FLUSH })) // 不正确提示正中间件 app.use(notifyError) // 应用当今文件目录下文档解决 404 恳求 app.use(staticFallback) // 各种各样 route 完成 app.use(router.routes()) app.use(router.allowedMethods()) // 针对 public 文件目录启用静态数据文档服务 app.use(require('koa-static')(path.resolve(__dirname, '../public'))) // 建立起动服务 let server = http.createServer(app.callback()) server.listen(3000) 

router.js

< 模版和数据信息,輸出 service 网页页面 }) // 让 `/app/**` 载入小程序所属文件目录文档 router.get('/app/(.*)', function* () { if (/\.(wxss|js)$/.test(file)) { // 动态性编译程序为 css 和相应 js } else if (/\.wxml/.test(file)) { // 动态性编译程序为 html } else { // 搜索其它种类文档, 存在则回到 let exists = util.exists(file) if (exists) { yield send(this, file) } else { this.status = 404 throw new Error(`File: ${file} not found`) } } }) 第三步:完成操纵层作用

完成完上面两步,便可以浏览 view 网页页面了,可是你会发现它只能3D渲染,其实不会有任何作用,由于 view 层作用依靠于操纵层开展的通信, 假如操纵层收不到信息,它不会响应任何恶性事件。

操纵层是全部完成全过程中最繁杂的一块,由于官方专用工具的编码与 nwjs 和 react 等第三方组件藕合太高,因此没法拿来立即应用。 你能够在 wept 新项目的 src 文件目录下找到操纵层逻辑性的全部编码,整体上操纵层要负责以下几个作用:

完成 service 层,view 层和操纵层之间的通信逻辑性 根据路由器命令动态性建立 view (wept 应用 iframe 完成) 依据当今网页页面动态性3D渲染 header 和 tabbar 完成原生态 API 启用,回到結果给 service 层 <)编码以下:


window.addEventListener('message', function (e) { let data = e.data let cmd = mand let msg = data.msg // 沒有跟 contentscript 握手环节,不需要解决 if (data.to == 'contentscript') return // 这是个遗留下方式,基本废料掉了 if (mand == 'EXEC_JSSDK') { sdk(data) // 立即转发 view 层信息到 service,关键是各种各样恶性事件通告 } else if (cmd == 'TO_APP_SERVICE') { toAppService(data) // 除 publish 推送信息给 view 层和操纵层能够解决的逻辑性(例如设定题目), // 其它所有转发 service 解决,全部操纵层的解决結果统一先回到 service } else if (cmd == 'COMMAND_FROM_ASJS') { let sdkName = data.sdkName if (command.hasOwnProperty(sdkName)) { command[sdkName](data) } else { console.warn(`Method ${sdkName} not mand!`) } } else { console.warn(`Command ${cmd} not recognized!`) } }) 

mand.js src/service.jssrc/sdk/*.js。针对 view/service 网页页面只需把原先 bridge.js 的window.postMessage 改成 window.top.postMessage 便可。

view 层的操纵逻辑性由 src/view.js 和 src/viewManage.js 完成,viewManage 完成了 navigateTo, redirectTo 和 navigateBack 来响应 service 层根据名为 mand 传来的对应网页页面路由器恶性事件。

header.js 和 tabbar.js 包括了根据 react 完成的 header 和 tabbar 控制模块(原方案是应用 vue,可是没找到与原生态 js 控制模块通信的 API)

sdk 文件目录下包括了 storage,音频,mand.js 里边了。

以上就是完成运作小程序所需 webserver 的所有逻辑性了,实际上现其实不繁杂,关键艰难在与了解手机微信这一整套通信方法。

完成小程序即时升级 第一步: 监控文档转变并通告前端开发

wept 应用了 chokidar 控制模块监控文档转变,转变后应用 WebSocket 告之全部顾客端开展升级实际操作。 实际完成坐落于 lib/watcher.js 和 lib/socket.js, 推送內容是 json 文件格式的标识符串。

前端开发操纵层收到 WebSocket 信息后再根据 postMessage 插口转发信息给 view/service 层:


view.postMessage({ msg: { data: { data: { path } }, eventName: 'reload' }, command: 'CUSTOM' }) 

view/service 层监视 reload 恶性事件:


WeixinJSBridge.subscribe('reload', function(data) { // data 即为上面的 msg.data }) 
第二步: 前端开发响应不一样文档转变

前端开发需要对 4 种(wxml wxss json javascript)不一样种类文档开展 4 种不一样的热升级解决,在其中 wxss 和 json 相对性简易。


wxss 文档转变后前端开发操纵层通告(postMessage 插口)对应网页页面(假如是 app.wxss 则是全部 view 网页页面)开展更新,view 层收到信息后只需要变更对应 css 文档的時间戳便可以了,编码以下:



o.subscribe('reload', function(data) { if (/\.wxss$/.test(data.path)) { var p = '/app/' + data.path var els = document.getElementsByTagName('link') ;[].slice.call(els).forEach(function(el) { var href = el.getAttribute('href').replace(/\?(.*)$/, '') if (p == href) { ('Reload: ' + data.path) el.setAttribute('href', href + '?id=' + Date.now()) } }) } }) 

json 文档转变最先需要分辨,假如是 app.json 大家没法热升级,因此现阶段做法是更新网页页面,针对网页页面的 json, 大家只需要在操纵层上对 header 设定相应情况便可以了 (3D渲染工作中由 react 帮大家解决):



socket.onmessage = function (e) { let data = JSON.parse(e.data) let p = data.path if (data.type == 'reload'){ if (p == 'app.json') { redirectToHome() } else if (/\.json$/.test(p)) { let win = window.__wxConfig__['window'] win.pages[p.replace(/\.json$/, '')] = data.content // header 根据全局性 __wxConfig__ 获得 state 开展3D渲染 header.reset() (`Reset header for ${p.replace(/\.json$/, '')}`) } } } 

wxml 应用 VirtualDom API 出示的 diff apply 开展解决。最先需要一个插口获得新的 generateFunc 涵数(用于转化成 VirtualDom), 加上 koa 的 router:



router.get('/generateFunc', function* () { this.body = yield loadFile(this.query.path + '.wxml') this.type = 'text' }) function loadFile(p, throwErr = true) { return new Promise((resolve, reject) = { fs.stat(`./${p}`, (err, stats) = { if (err) { if (throwErr) return reject(new Error(`file ${p} not found`)) // 文档不存在有将会是文档被删掉,因此不可以应用 reject return resolve('') } if (stats stats.isFile()) { // parer 涵数启用 exec 指令实行 wcsc 文档转化成 wxml 对应的 javascript 编码 return parser(`${p}`).then(resolve, reject) } else { return resolve('') } }) }) } 

有了插口便可以恳求插口,随后实行回到涵数开展 diff apply:



// curr 为当今的 VirtualDom 树 if (!curr) return var xhr = new XMLHttpRequest() xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { var text = xhr.responseText var func = new Function(text + '\n return $gwx( ./' +__path__+ '.wxml )') window.__generateFunc__ = func() var oldTree = curr // 获得当今 data 转化成新的树 var o = m(p.default.getData(), false), // 开展 diff apply a = oldTree.diff(o); a.apply(x); document.dispatchEvent(new CustomEvent( pageReRender , {})); ('Hot apply: ' + __path__ + '.wxml') } } } xhr.open('GET', '/generateFunc?path=' + encodeURIComponent(__path__)) xhr.send() 

javascript 升级逻辑性相对性繁杂一些, 最先仍然是一个插口来获得新的 javascript 编码:



router.get('/generateJavascript', function* () { this.body = yield loadFile(this.query.path) this.type = 'text' }) 

随后大家在 window 目标上添加 Reload 涵数实行实际的拆换逻辑性:



window.Reload = function (e) { var pages = __wxConfig.pages; if (pages.indexOf(window.__wxRoute) == -1) return // 更换原先的结构涵数 f[window.__wxRoute] = e var keys = Object.keys(p) // 判断是不是当今应用中网页页面 var isCurr = s.route == window.__wxRoute keys.forEach(function (key) { var o = p[key]; key = Number(key) var query = o.__query__ var page = o.page var route = o.route // 网页页面早已被建立 if (route == window.__wxRoute) { // 实行封裝后的 onHide 和 onUnload isCurr page.onHide() page.onUnload() // 建立新 page 目标 var newPage = new a.default(e, key, route) newPage.__query__ = query // 再次关联当今网页页面 if (isCurr) s.page = newPage o.page = newPage // 实行 onLoad 和 onShow newPage.onLoad() if (isCurr) newPage.onShow() // 升级 data 数据信息 window.__wxAppData[route] = newPage.data window.__wxAppData[route].__webviewId__ = key // 推送升级恶性事件, 通告 view 层 u.publish(c.UPDATE_APP_DATA) u.info( Update view with init data ) u.info(newPage.data) // 推送 appDataChange 恶性事件 u.publish( appDataChange , { data: { data: newPage.data }, option: { timestamp: Date.now() } }) newPage.__webviewReady__ = true } }) u.info( Reload page: + window.__wxRoute) } 

以上编码需要加上到 t.pageHolder 涵数后才可运作

最终在 view 层原始化后把 Page 涵数切换到 Reload 涵数(自然你还可以在恳求回到 javascript 前把 Page 重取名为 Reload) 。



 body script window._____sendMsgToNW({ sdkName: 'APP_SERVICE_COMPLETE' }) /script /body 
---------

开发制作微信小程序

------------


联系我们

全国服务热线:4000-399-000 公司邮箱:343111187@qq.com

  工作日 9:00-18:00

关注我们

官网公众号

官网公众号

Copyright?2020 广州凡科互联网科技股份有限公司 版权所有 粤ICP备10235580号 客服热线 18720358503