小程序架构

architecture

微信小程序的框架包含两部分 View 视图层、App Service逻辑层。View 层用来渲染页面结构,AppService 层用来逻辑处理、数据请求、接口调用。

它们在两个线程里运行。

它们在两个线程里运行。

它们在两个线程里运行。

视图层和逻辑层通过系统层的 JSBridage 进行通信,逻辑层把数据变化通知到视图层,触发视图层页面更新,视图层把触发的事件通知到逻辑层进行业务处理。

补充

one-context

视图层使用 WebView 渲染,iOS 中使用自带 WKWebView,在 Android 使用腾讯的 x5 内核(基于 Blink)运行。

逻辑层使用在 iOS 中使用自带的 JSCore 运行,在 Android 中使用腾讯的 x5 内核(基于 Blink)运行。

开发工具使用 nw.js 同时提供了视图层和逻辑层的运行环境。

在 Mac下 使用 js-beautify 对微信开发工具 @v1.02.1808080代码批量格式化:

  1. cd /Applications/wechatwebdevtools.app/Contents/Resources/package.nw
  2. find . -type f -name '*.js' -not -path "./node_modules/*" -not -path -exec js-beautify -r -s 2 -p -f '{}' \;

js/extensions/appservice/index.js 中找到:

  1. 267: function(a, b, c) {
  2. const d = c(8),
  3. e = c(227),
  4. f = c(226),
  5. g = c(228),
  6. h = c(229),
  7. i = c(230);
  8. var j = window.__global.navigator.userAgent,
  9. k = -1 !== j.indexOf('game');
  10. k || i(), window.__global.getNewWeixinJSBridge = (a) => {
  11. const {
  12. invoke: b
  13. } = f(a), {
  14. publish: c
  15. } = g(a), {
  16. subscribe: d,
  17. triggerSubscribeEvent: i
  18. } = h(a), {
  19. on: j,
  20. triggerOnEvent: k
  21. } = e(a);
  22. return {
  23. invoke: b,
  24. publish: c,
  25. subscribe: d,
  26. on: j,
  27. get __triggerOnEvent() {
  28. return k
  29. },
  30. get __triggerSubscribeEvent() {
  31. return i
  32. }
  33. }
  34. }, window.WeixinJSBridge = window.__global.WeixinJSBridge = window.__global.getNewWeixinJSBridge('global'), window.__global.WeixinJSBridgeMap = {
  35. __globalBridge: window.WeixinJSBridge
  36. }, __devtoolsConfig.online && __devtoolsConfig.autoTest && setInterval(() => {
  37. console.clear()
  38. }, 1e4);
  39. try {
  40. var l = new window.__global.XMLHttpRequest;
  41. l.responseType = 'text', l.open('GET', `http://${window.location.host}/calibration/${Date.now()}`, !0), l.send()
  42. } catch (a) {}
  43. }

js/extensions/gamenaitveview/index.js 中找到:

  1. 299: function(a, b, c) {
  2. 'use strict';
  3. Object.defineProperty(b, '__esModule', {
  4. value: !0
  5. });
  6. var d = c(242),
  7. e = c(241),
  8. f = c(243),
  9. g = c(244);
  10. window.WeixinJSBridge = {
  11. on: d.a,
  12. invoke: e.a,
  13. publish: f.a,
  14. subscribe: g.a
  15. }
  16. },

js/extensions/pageframe/index.js中找到:

  1. 317: function(a, b, c) {
  2. 'use strict';
  3. function d() {
  4. window.WeixinJSBridge = {
  5. on: e.a,
  6. invoke: f.a,
  7. publish: g.a,
  8. subscribe: h.a
  9. }, k.a.init();
  10. let a = document.createEvent('UIEvent');
  11. a.initEvent('WeixinJSBridgeReady', !1, !1), document.dispatchEvent(a), i.a.init()
  12. }
  13. Object.defineProperty(b, '__esModule', {
  14. value: !0
  15. });
  16. var e = c(254),
  17. f = c(253),
  18. g = c(255),
  19. h = c(256),
  20. i = c(86),
  21. j = c(257),
  22. k = c.n(j);
  23. 'complete' === document.readyState ? d() : window.addEventListener('load', function() {
  24. d()
  25. })
  26. },

我们都看到了 WeixinJSBridge 的定义。分别都有 oninvokepublishsubscribe 这个几个关键方法。

invoke 举例,在 js/extensions/appservice/index.js中发现这段代码:

  1. f (!r) p[b] = s, f.send({
  2. command: 'APPSERVICE_INVOKE',
  3. data: {
  4. api: c,
  5. args: e,
  6. callbackID: b
  7. }
  8. });

js/extensions/pageframe/index.js 中发现这段代码:

  1. g[d] = c, e.a.send({
  2. command: 'WEBVIEW_INVOKE',
  3. data: {
  4. api: a,
  5. args: b,
  6. callbackID: d
  7. }
  8. })

简单的分析得知:字段 command 用来区分行为,invoke 用来调用 Native 的 Api。在不同的来源要使用不同的前缀。data 里面包含 Api 名,参数。另外 callbackID 指定接受回调的方法句柄。Appservice 和 Webview 使用的通信协议是一致的。

我们不能在代码里使用 BOM 和 DOM 是因为根本没有,另一方面也不希望 JS 代码直接操作视图。

在开发工具中 remote-helper.js 中找到了这样的代码:

  1. const vm = require("vm");
  2. const vmGlobal = {
  3. require: undefined,
  4. eval: undefined,
  5. process: undefined,
  6. setTimeout(...args) {
  7. //...省略代码
  8. return timerCount;
  9. },
  10. clearTimeout(id) {
  11. const timer = timers[id];
  12. if (timer) {
  13. clearTimeout(timer);
  14. delete timers[id];
  15. }
  16. },
  17. setInterval(...args) {
  18. //...省略代码
  19. return timerCount;
  20. },
  21. clearInterval(id) {
  22. const timer = timers[id];
  23. if (timer) {
  24. clearInterval(timer);
  25. delete timers[id];
  26. }
  27. },
  28. console: (() => {
  29. //...省略代码
  30. return consoleClone;
  31. })()
  32. };
  33. const jsVm = vm.createContext(vmGlobal);
  34. // 省略大量代码...
  35. function loadCode(filePath, sourceURL, content) {
  36. let ret;
  37. try {
  38. const script = typeof content === 'string' ? content : fs.readFileSync(filePath, 'utf-8').toString();
  39. ret = vm.runInContext(script, jsVm, {
  40. filename: sourceURL,
  41. });
  42. }
  43. catch (e) {
  44. // something went wrong in user code
  45. console.error(e);
  46. }
  47. return ret;
  48. }

这样的分层设计显然是有意为之的,它的中间层完全控制了程序对于界面进行的操作, 同时对于传递的数据和响应时间也能做到监控。一方面程序的行为受到了极大限制, 另一方面微信可以确保他们对于小程序内容和体验有绝对的控制。

这样的结构也说明了小程序的动画和绘图 API 被设计成生成一个最终对象而不是一步一步执行的样子, 原因就是 Json 格式的数据传递和解析相比与原生 API 都是损耗不菲的,如果频繁调用很可能损耗过多性能,进而影响用户体验。

下载小程序完整包

download

App Service - Life Cylce

lifecycle

面试题

1.动画需要绑定在 data 上,而绘图却不用。你觉得是为什么呢?

  1. var context = wx.createCanvasContext('firstCanvas')
  2. context.setStrokeStyle("#00ff00")
  3. context.setLineWidth(5)
  4. context.rect(0, 0, 200, 200)
  5. context.stroke()
  6. context.setStrokeStyle("#ff0000")
  7. context.setLineWidth(2)
  8. context.moveTo(160, 100)
  9. context.arc(100, 100, 60, 0, 2 * Math.PI, true)
  10. context.moveTo(140, 100)
  11. context.arc(100, 100, 40, 0, Math.PI, false)
  12. context.moveTo(85, 80)
  13. context.arc(80, 80, 5, 0, 2 * Math.PI, true)
  14. context.moveTo(125, 80)
  15. context.arc(120, 80, 5, 0, 2 * Math.PI, true)
  16. context.stroke()
  17. context.draw()
  1. Page({
  2. data: {
  3. animationData: {}
  4. },
  5. onShow: function(){
  6. var animation = wx.createAnimation({
  7. duration: 1000,
  8. timingFunction: 'ease',
  9. })
  10. this.animation = animation
  11. animation.scale(2,2).rotate(45).step()
  12. this.setData({
  13. animationData:animation.export()
  14. })
  15. }
  16. })

2.小程序的 Http Rquest 请求是不是用的浏览器 Fetch API?

知识点考察

  • 知道 Request 是由 Native 实现的
  • JSCore 是不带 Http Request、Websocket、Storage等功能的,那是 Webkit 带的
  • 小程序的 wx.request 是不是遵循 fetch API 规范实现的呢?答案,显然不是。因为没有 Promise